diff --git a/DEPS b/DEPS
index d52c958..f009c671 100644
--- a/DEPS
+++ b/DEPS
@@ -275,7 +275,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': '4ff73144c35b993907a6e3738a7be81c0681e504',
+  'skia_revision': 'd29d446a7df2a4ea2033330778d560b27df91538',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling V8
   # and whatever else without interference from each other.
@@ -283,11 +283,11 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling ANGLE
   # and whatever else without interference from each other.
-  'angle_revision': 'ebdbae9b7a83dbcb7dbc7e4541cbce5fa23cc5ac',
+  'angle_revision': 'bfab7e60a15dc6f72e34406d3f2a3996cd8d0be2',
   # 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': '19e3080dfe86936d88bd68c38281fc4095128758',
+  'swiftshader_revision': '26243894edb812abe0f120ac36c400439848dacb',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling PDFium
   # and whatever else without interference from each other.
@@ -346,7 +346,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling catapult
   # and whatever else without interference from each other.
-  'catapult_revision': '3a2e446a98743856c32b4426cef3237f86ad8787',
+  'catapult_revision': '946bd4b0769ac6e45e60c3fca4ca985c28601fe6',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling libFuzzer
   # and whatever else without interference from each other.
@@ -354,7 +354,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling devtools-frontend
   # and whatever else without interference from each other.
-  'devtools_frontend_revision': '2aeb1ca58a07e752022e6957631d359d4e4d9e7c',
+  'devtools_frontend_revision': '5cac5aa93f35c54d148073ee2131798b3544c0cf',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling libprotobuf-mutator
   # and whatever else without interference from each other.
@@ -390,7 +390,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
-  'dawn_revision': 'c3c3bf538f07c13f1bf6c47974db97e3e97bb385',
+  'dawn_revision': 'f25140fe6ff0babe2fc7712883da6fceb72a3cc7',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
@@ -747,7 +747,7 @@
     Var('chromium_git') + '/external/github.com/toji/webvr.info.git' + '@' + 'c58ae99b9ff9e2aa4c524633519570bf33536248',
 
   'src/docs/website': {
-    'url': Var('chromium_git') + '/website.git' + '@' + '9f911e639c13c9eaf06721a9755bcaf49ce63427',
+    'url': Var('chromium_git') + '/website.git' + '@' + 'ac7da14207c8b336456f0d420809d276cf571c23',
   },
 
   'src/ios/third_party/earl_grey2/src': {
@@ -841,7 +841,7 @@
       'packages': [
         {
           'package': 'chromium/rts/model/linux-amd64',
-          'version': 'mwQUySZTAjUh63HUF4elgaSEXQmxEgTrzt0aEQlqE7oC',
+          'version': 'BFS8Fx6fUVRucxMSpNBFTJP7e70GcBtq2kNc7lD_WOIC',
         },
       ],
       'dep_type': 'cipd',
@@ -852,7 +852,7 @@
       'packages': [
         {
           'package': 'chromium/rts/model/mac-amd64',
-          'version': 'VyKiTzbwRVHNrfBYHduT0eAthyj9joVN-umACpbgqS4C',
+          'version': 'qe-d43t256OjMmrycyV5HFMYVptIyW1OcyqLjhyLkT8C',
         },
       ],
       'dep_type': 'cipd',
@@ -1113,7 +1113,7 @@
   # Tools used when building Chrome for Chrome OS. This affects both the Simple
   # Chrome workflow, as well as the chromeos-chrome ebuild.
   'src/third_party/chromite': {
-      'url': Var('chromium_git') + '/chromiumos/chromite.git' + '@' + 'c0a200192d7742fd7f5cb4ebd9dd158061c57645',
+      'url': Var('chromium_git') + '/chromiumos/chromite.git' + '@' + 'ca90875ae1939f5fca571fe474bcb6c0f4abd656',
       'condition': 'checkout_chromeos',
   },
 
@@ -1136,7 +1136,7 @@
   },
 
   'src/third_party/depot_tools':
-    Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + '04663d61d15e0bcc4eb50b15fb3e9cba3d8857df',
+    Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + '35ef5ada1db47906a6c6974acd82f7bf18438678',
 
   'src/third_party/devtools-frontend/src':
     Var('chromium_git') + '/devtools/devtools-frontend' + '@' + Var('devtools_frontend_revision'),
@@ -1708,7 +1708,7 @@
     Var('chromium_git') + '/external/github.com/gpuweb/cts.git' + '@' + '70df7aba40010cb2a90348f97a8c9b20dbc91ace',
 
   'src/third_party/webrtc':
-    Var('webrtc_git') + '/src.git' + '@' + '099ff62d94ec7640a8009ef07ab562141696e19f',
+    Var('webrtc_git') + '/src.git' + '@' + 'ccb05f17b142c4aff743c91bc589abbb34f83467',
 
   'src/third_party/libgifcodec':
      Var('skia_git') + '/libgifcodec' + '@'+  Var('libgifcodec_revision'),
@@ -1781,7 +1781,7 @@
     Var('chromium_git') + '/v8/v8.git' + '@' +  Var('v8_revision'),
 
   'src-internal': {
-    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@df8fa4f5c0068f51d95e08bcc6175807f95742e8',
+    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@3abc8eec6405361a0c4d92502b3c1d7974a9a4d1',
     'condition': 'checkout_src_internal',
   },
 
@@ -1811,7 +1811,7 @@
     'packages': [
       {
         'package': 'chromeos_internal/apps/help_app/app',
-        'version': 'L3ofmGEfbGxTWEs5VF9UqJT56YKWYcVA_R_rxOzwW80C',
+        'version': 'JXFwptL8JYGWmRiHVmzekUT2Thi_a1pNh5nb0qMMulcC',
       },
     ],
     'condition': 'checkout_chromeos and checkout_src_internal',
@@ -1822,7 +1822,7 @@
     'packages': [
       {
         'package': 'chromeos_internal/apps/media_app/app',
-        'version': '4CUFvhqqtHkEK_wNpa9gIGb5G7CD8FirGDMp0_lJyMQC',
+        'version': '8tpfALg7M-MbUFSmsn1ZqKnZTrabeHFEhpwxqA-fOgMC',
       },
     ],
     'condition': 'checkout_chromeos and checkout_src_internal',
@@ -4222,6 +4222,16 @@
     ],
   },
 
+  # Download telemetry_gpu_integration_test binary dependencies
+  {
+    'name': 'checkout_telemetry_gpu_integration_test_binary_dependencies',
+    'condition': 'host_os == "linux"',
+    'pattern': '.',
+    'action': [ 'python3',
+                'src/content/test/gpu/gpu_tests/fetch_gpu_integration_test_dependencies.py',
+    ],
+  },
+
   # Download test data for Maps telemetry_gpu_integration_test.
   {
     'name': 'maps_perf_test_load_dataset',
diff --git a/PRESUBMIT.py b/PRESUBMIT.py
index b378757..6ced265f 100644
--- a/PRESUBMIT.py
+++ b/PRESUBMIT.py
@@ -70,6 +70,13 @@
 _HEADER_EXTENSIONS = r'\.(h|hpp|hxx)$'
 
 
+# Paths with sources that don't use //base.
+_NON_BASE_DEPENDENT_PATHS = (
+    r"^chrome[\\/]browser[\\/]browser_switcher[\\/]bho[\\/]",
+    r"^tools[\\/]win[\\/]",
+)
+
+
 # Regular expression that matches code only used for test binaries
 # (best effort).
 _TEST_CODE_EXCLUDED_PATHS = (
@@ -1116,7 +1123,6 @@
     'build/android/gyp/java_cpp_features.pydeps',
     'build/android/gyp/java_cpp_strings.pydeps',
     'build/android/gyp/java_google_api_keys.pydeps',
-    'build/android/gyp/jetify_jar.pydeps',
     'build/android/gyp/jinja_template.pydeps',
     'build/android/gyp/lint.pydeps',
     'build/android/gyp/merge_manifest.pydeps',
@@ -1381,15 +1387,22 @@
     return []
 
 
-def _CheckNoStrCatRedefines(input_api, output_api):
+def CheckNoStrCatRedefines(input_api, output_api):
     """Checks no windows headers with StrCat redefined are included directly."""
     files = []
+    files_to_check = (r'.+%s' % _HEADER_EXTENSIONS,
+                      r'.+%s' % _IMPLEMENTATION_EXTENSIONS)
+    files_to_skip = (input_api.DEFAULT_FILES_TO_SKIP +
+                     _NON_BASE_DEPENDENT_PATHS)
+    sources_filter = lambda f: input_api.FilterSourceFile(
+        f, files_to_check=files_to_check, files_to_skip=files_to_skip)
+
     pattern_deny = input_api.re.compile(
         r'^#include\s*[<"](shlwapi|atlbase|propvarutil|sphelper).h[">]',
         input_api.re.MULTILINE)
     pattern_allow = input_api.re.compile(
         r'^#include\s"base/win/windows_defines.inc"', input_api.re.MULTILINE)
-    for f in input_api.AffectedSourceFiles(input_api.FilterSourceFile):
+    for f in input_api.AffectedSourceFiles(sources_filter):
         contents = input_api.ReadFile(f)
         if pattern_deny.search(
                 contents) and not pattern_allow.search(contents):
diff --git a/PRESUBMIT_test.py b/PRESUBMIT_test.py
index 534973eb..9eb7349 100755
--- a/PRESUBMIT_test.py
+++ b/PRESUBMIT_test.py
@@ -3044,7 +3044,7 @@
       MockFile('dir/baz.h', ['#include <atlbase.h>']),
       MockFile('dir/jumbo.h', ['#include "sphelper.h"']),
     ]
-    results = PRESUBMIT._CheckNoStrCatRedefines(mock_input_api, MockOutputApi())
+    results = PRESUBMIT.CheckNoStrCatRedefines(mock_input_api, MockOutputApi())
     self.assertEqual(1, len(results))
     self.assertEqual(4, len(results[0].items))
     self.assertTrue('StrCat' in results[0].message)
@@ -3059,7 +3059,7 @@
       MockFile('dir/baz_win.cc', ['#include "base/win/shlwapi.h"']),
       MockFile('dir/baz-win.h', ['#include "base/win/atl.h"']),
     ]
-    results = PRESUBMIT._CheckNoStrCatRedefines(mock_input_api, MockOutputApi())
+    results = PRESUBMIT.CheckNoStrCatRedefines(mock_input_api, MockOutputApi())
     self.assertEqual(0, len(results))
 
   def testAllowsToCreateWrapper(self):
@@ -3069,7 +3069,16 @@
         '#include <shlwapi.h>',
         '#include "base/win/windows_defines.inc"']),
     ]
-    results = PRESUBMIT._CheckNoStrCatRedefines(mock_input_api, MockOutputApi())
+    results = PRESUBMIT.CheckNoStrCatRedefines(mock_input_api, MockOutputApi())
+    self.assertEqual(0, len(results))
+
+  def testIgnoresNonImplAndHeaders(self):
+    mock_input_api = MockInputApi()
+    mock_input_api.files = [
+      MockFile('dir/foo_win.txt', ['#include "shlwapi.h"']),
+      MockFile('dir/bar.asm', ['#include <propvarutil.h>']),
+    ]
+    results = PRESUBMIT.CheckNoStrCatRedefines(mock_input_api, MockOutputApi())
     self.assertEqual(0, len(results))
 
 
diff --git a/WATCHLISTS b/WATCHLISTS
index d9f9a79..2c124f28 100644
--- a/WATCHLISTS
+++ b/WATCHLISTS
@@ -1696,6 +1696,9 @@
       'filepath': 'ash/webui/scanning/'\
                   '|chrome/browser/ash/scanning/',
     },
+    'screen_ai': {
+      'filepath': 'screen_ai',
+    },
     'security': {
       'filepath': 'base/json/'\
                   '|base/memory/.*shared_memory'\
@@ -2775,6 +2778,7 @@
                           'chenleihu@google.com'],
     'scanning': ['gavinwill+scanning-watch@chromium.org',
                  'zentaro+scanning-watch@chromium.org'],
+    'screen_ai': ['rhalavati+watch@chromium.org'],
     'security': ['security-watchlist@chromium.org'],
     'select_to_speak': ['katie+watch@chromium.org',
                         'anastasi+watch@google.com'],
diff --git a/android_webview/browser/gfx/gpu_service_webview.cc b/android_webview/browser/gfx/gpu_service_webview.cc
index ffe31384..c488be26 100644
--- a/android_webview/browser/gfx/gpu_service_webview.cc
+++ b/android_webview/browser/gfx/gpu_service_webview.cc
@@ -29,9 +29,9 @@
   gpu::GpuPreferences gpu_preferences =
       content::GetGpuPreferencesFromCommandLine();
   auto* command_line = base::CommandLine::ForCurrentProcess();
-  bool success = gpu::InitializeGLThreadSafe(command_line, gpu_preferences,
-                                             &gpu_info, &gpu_feature_info);
-  if (!success) {
+  gl::GLDisplay* display = gpu::InitializeGLThreadSafe(
+      command_line, gpu_preferences, &gpu_info, &gpu_feature_info);
+  if (!display) {
     LOG(FATAL) << "gpu::InitializeGLThreadSafe() failed.";
   }
   auto sync_point_manager = std::make_unique<gpu::SyncPointManager>();
diff --git a/ash/ambient/ui/ambient_animation_view.cc b/ash/ambient/ui/ambient_animation_view.cc
index 8452c3b..21897ef 100644
--- a/ash/ambient/ui/ambient_animation_view.cc
+++ b/ash/ambient/ui/ambient_animation_view.cc
@@ -23,6 +23,8 @@
 #include "ash/ambient/ui/glanceable_info_view.h"
 #include "ash/ambient/ui/media_string_view.h"
 #include "ash/ambient/util/ambient_util.h"
+#include "ash/public/cpp/ambient/ambient_metrics.h"
+#include "ash/public/cpp/metrics_util.h"
 #include "base/bind.h"
 #include "base/check.h"
 #include "base/location.h"
@@ -80,23 +82,13 @@
 constexpr SkColor kDarkModeShieldColor =
     SkColorSetA(gfx::kGoogleGrey900, SK_AlphaOPAQUE / 10);
 
-// TODO(esum): Record throughput metrics to track animation performance in the
-// field. We can use ash::metrics_util::CalculateSmoothness().
-void LogCompositorThroughput(
-    base::TimeTicks logging_start_time,
-    const cc::FrameSequenceMetrics::CustomReportData& data) {
-  base::TimeDelta duration = base::TimeTicks::Now() - logging_start_time;
-  float duration_sec = duration.InSecondsF();
+void LogCompositorThroughput(AmbientAnimationTheme theme, int smoothness) {
   // Use VLOG instead of DVLOG since this log is performance-related and
   // developers will almost certainly only care about this log on non-debug
   // builds. The overhead of "--vmodule" regex matching is very minor so far to
   // performance/CPU.
-  VLOG(1) << "Compositor throughput report: frames_expected="
-          << data.frames_expected << " frames_produced=" << data.frames_produced
-          << " jank_count=" << data.jank_count
-          << " expected_fps=" << data.frames_expected / duration_sec
-          << " actual_fps=" << data.frames_produced / duration_sec
-          << " duration=" << duration;
+  VLOG(1) << "Compositor throughput report: smoothness=" << smoothness;
+  ambient::RecordAmbientModeAnimationSmoothness(smoothness, theme);
 }
 
 // Returns the maximum possible displacement in either dimension from the
@@ -377,8 +369,9 @@
   ui::Compositor* compositor = widget->GetCompositor();
   DCHECK(compositor);
   throughput_tracker_ = compositor->RequestNewThroughputTracker();
-  throughput_tracker_->Start(base::BindOnce(
-      &LogCompositorThroughput, /*logging_start_time=*/base::TimeTicks::Now()));
+  throughput_tracker_->Start(metrics_util::ForSmoothness(
+      base::BindRepeating(&LogCompositorThroughput,
+                          static_resources_->GetAmbientAnimationTheme())));
 }
 
 void AmbientAnimationView::ApplyJitter() {
diff --git a/ash/app_list/app_list_controller_impl.cc b/ash/app_list/app_list_controller_impl.cc
index e6a06c1..43b4eb94 100644
--- a/ash/app_list/app_list_controller_impl.cc
+++ b/ash/app_list/app_list_controller_impl.cc
@@ -1471,6 +1471,10 @@
   if (should_dismiss_immediately_)
     return true;
 
+  if (features::IsProductivityLauncherEnabled())
+    return false;
+
+  // Dismiss immediately if the peeking launcher is below the shelf's top edge.
   DCHECK(Shell::HasInstance());
   const int ideal_shelf_y =
       Shelf::ForWindow(
diff --git a/ash/app_list/app_list_view_delegate.h b/ash/app_list/app_list_view_delegate.h
index 11f038e..68472940 100644
--- a/ash/app_list/app_list_view_delegate.h
+++ b/ash/app_list/app_list_view_delegate.h
@@ -129,7 +129,9 @@
   // its descendants.
   virtual bool CanProcessEventsOnApplistViews() = 0;
 
-  // Returns whether the AppListView should dismiss immediately.
+  // Returns whether the app list should dismiss immediately. For example, when
+  // the assistant takes a screenshot the app list is closed immediately so it
+  // doesn't appear in the screenshot.
   virtual bool ShouldDismissImmediately() = 0;
 
   // Gets the ideal y position for the close animation, which depends on
diff --git a/ash/app_list/views/app_list_bubble_apps_page.cc b/ash/app_list/views/app_list_bubble_apps_page.cc
index cd0c053..57079ff18 100644
--- a/ash/app_list/views/app_list_bubble_apps_page.cc
+++ b/ash/app_list/views/app_list_bubble_apps_page.cc
@@ -320,7 +320,7 @@
                           weak_factory_.GetWeakPtr()));
 }
 
-void AppListBubbleAppsPage::AnimateHideLauncher() {
+void AppListBubbleAppsPage::PrepareForHideLauncher() {
   // Remove the gradient mask from the scroll view to improve performance.
   gradient_helper_.reset();
 }
diff --git a/ash/app_list/views/app_list_bubble_apps_page.h b/ash/app_list/views/app_list_bubble_apps_page.h
index d1f0d4d6..6adf2770 100644
--- a/ash/app_list/views/app_list_bubble_apps_page.h
+++ b/ash/app_list/views/app_list_bubble_apps_page.h
@@ -80,9 +80,9 @@
   // Starts the launcher show animation.
   void AnimateShowLauncher(bool is_side_shelf);
 
-  // Starts the launcher hide animation. None of the child views animate, but
+  // Prepares for launcher hide animation. None of the child views animate, but
   // this disables the scroll view gradient mask to improve performance.
-  void AnimateHideLauncher();
+  void PrepareForHideLauncher();
 
   // Starts the animation for showing the apps page, coming from another page.
   void AnimateShowPage();
diff --git a/ash/app_list/views/app_list_bubble_view.cc b/ash/app_list/views/app_list_bubble_view.cc
index 6d22ada..4ff05be 100644
--- a/ash/app_list/views/app_list_bubble_view.cc
+++ b/ash/app_list/views/app_list_bubble_view.cc
@@ -363,6 +363,17 @@
   // Ensure any in-progress animations have their cleanup callbacks called.
   AbortAllAnimations();
 
+  if (current_page_ == AppListBubblePage::kApps)
+    apps_page_->PrepareForHideLauncher();
+
+  const gfx::Rect target_bounds = layer()->GetTargetBounds();
+
+  if (view_delegate_->ShouldDismissImmediately()) {
+    // Don't animate, just clean up.
+    OnHideAnimationEnded(target_bounds);
+    return;
+  }
+
   ui::AnimationThroughputReporter reporter(
       layer()->GetAnimator(),
       metrics_util::ForSmoothness(base::BindRepeating([](int value) {
@@ -383,13 +394,9 @@
   // Opacity: 100% → 0%
   // Duration: 100ms
   // Ease: Linear
-  const gfx::Rect target_bounds = layer()->GetTargetBounds();
   const gfx::Rect final_bounds =
       GetShowHideAnimationBounds(is_side_shelf, target_bounds);
 
-  if (current_page_ == AppListBubblePage::kApps)
-    apps_page_->AnimateHideLauncher();
-
   views::AnimationBuilder()
       .OnEnded(base::BindOnce(&AppListBubbleView::OnHideAnimationEnded,
                               weak_factory_.GetWeakPtr(), target_bounds))
diff --git a/ash/app_list/views/app_list_bubble_view_unittest.cc b/ash/app_list/views/app_list_bubble_view_unittest.cc
index 8900ef2..04beede 100644
--- a/ash/app_list/views/app_list_bubble_view_unittest.cc
+++ b/ash/app_list/views/app_list_bubble_view_unittest.cc
@@ -43,6 +43,7 @@
 #include "base/strings/string_number_conversions.h"
 #include "base/test/metrics/histogram_tester.h"
 #include "base/test/scoped_feature_list.h"
+#include "chromeos/services/assistant/public/cpp/assistant_enums.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "ui/compositor/layer.h"
 #include "ui/compositor/scoped_animation_duration_scale_mode.h"
@@ -487,6 +488,37 @@
       "Apps.ClamshellLauncher.AnimationSmoothness.Close", 1);
 }
 
+TEST_F(AppListBubbleViewTest, AssistantScreenshotClosesBubbleWithoutAnimation) {
+  SimulateAssistantEnabled();
+  AddAppItems(5);
+
+  // Show the app list without animation.
+  ShowAppList();
+
+  // Switch to the assistant page.
+  LeftClickOn(GetSearchBoxView()->assistant_button());
+
+  // Enable animations.
+  ui::ScopedAnimationDurationScaleMode duration(
+      ui::ScopedAnimationDurationScaleMode::NON_ZERO_DURATION);
+
+  // Simulate the app list being closed by taking a screenshot with assistant.
+  // This makes AppListControllerImpl::ShouldDismissImmediately() return true.
+  AssistantUiController::Get()->ToggleUi(
+      absl::nullopt, chromeos::assistant::AssistantExitPoint::kScreenshot);
+
+  // The bubble dismissed immediately so it is not animating.
+  ui::Layer* bubble_layer = GetAppListTestHelper()->GetBubbleView()->layer();
+  ASSERT_TRUE(bubble_layer);
+  EXPECT_FALSE(IsAnimatingProperty(
+      bubble_layer, ui::LayerAnimationElement::AnimatableProperty::BOUNDS));
+  EXPECT_FALSE(IsAnimatingProperty(
+      bubble_layer, ui::LayerAnimationElement::AnimatableProperty::OPACITY));
+
+  // App list is closed.
+  EXPECT_FALSE(Shell::Get()->app_list_controller()->IsVisible());
+}
+
 TEST_F(AppListBubbleViewTest, ShutdownDuringHideAnimationDoesNotCrash) {
   base::HistogramTester histograms;
 
diff --git a/ash/components/arc/arc_features.cc b/ash/components/arc/arc_features.cc
index 461778d..bfc0b8c 100644
--- a/ash/components/arc/arc_features.cc
+++ b/ash/components/arc/arc_features.cc
@@ -44,10 +44,6 @@
 const base::Feature kEnablePerVmCoreScheduling{
     "ArcEnablePerVmCoreScheduling", base::FEATURE_ENABLED_BY_DEFAULT};
 
-// Controls whether to pass throttling notifications to Android side.
-const base::Feature kEnableThrottlingNotification{
-    "ArcEnableThrottlingNotification", base::FEATURE_ENABLED_BY_DEFAULT};
-
 // Controls whether to use ARC TTS caching to optimize ARC boot.
 const base::Feature kEnableTTSCaching{"ArcEnableTTSCaching",
                                       base::FEATURE_DISABLED_BY_DEFAULT};
diff --git a/ash/components/arc/arc_features.h b/ash/components/arc/arc_features.h
index 07fb4a7..a4d0746 100644
--- a/ash/components/arc/arc_features.h
+++ b/ash/components/arc/arc_features.h
@@ -19,7 +19,6 @@
 extern const base::Feature kDocumentsProviderUnknownSizeFeature;
 extern const base::Feature kEnableArcNearbyShare;
 extern const base::Feature kEnablePerVmCoreScheduling;
-extern const base::Feature kEnableThrottlingNotification;
 extern const base::Feature kEnableTTSCaching;
 extern const base::Feature kEnableUnifiedAudioFocusFeature;
 extern const base::Feature kEnableUnmanagedToManagedTransitionFeature;
diff --git a/ash/components/drivefs/drivefs_host.cc b/ash/components/drivefs/drivefs_host.cc
index b2719f4..aee66b6 100644
--- a/ash/components/drivefs/drivefs_host.cc
+++ b/ash/components/drivefs/drivefs_host.cc
@@ -90,7 +90,10 @@
         auth_delegate->IsMetricsCollectionEnabled(),
         delegate->GetLostAndFoundDirectoryName(),
         base::FeatureList::IsEnabled(chromeos::features::kDriveFsMirroring),
-        delegate->IsVerboseLoggingEnabled()};
+        delegate->IsVerboseLoggingEnabled(),
+        base::FeatureList::IsEnabled(
+            chromeos::features::kDriveFsChromeNetworking),
+    };
     return DriveFsConnection::Create(delegate->CreateMojoListener(),
                                      std::move(config));
   }
diff --git a/ash/components/drivefs/mojom/drivefs.mojom b/ash/components/drivefs/mojom/drivefs.mojom
index 6c961cc..adb6764 100644
--- a/ash/components/drivefs/mojom/drivefs.mojom
+++ b/ash/components/drivefs/mojom/drivefs.mojom
@@ -186,7 +186,7 @@
     HttpRequest request, pending_remote<HttpDelegate> delegate);
 };
 
-// Next MinVersion: 6
+// Next MinVersion: 7
 struct DriveFsConfiguration {
   string user_email;
 
@@ -206,6 +206,11 @@
 
   [MinVersion=5]
   bool enable_verbose_logging = false;
+
+  // Whether the network service bridge is available. This is enabled on CrOS by
+  // the |drive-fs-chrome-networking| flag.
+  [MinVersion=6]
+  bool enable_cros_network = false;
 };
 
 enum AccessTokenStatus {
diff --git a/ash/public/cpp/ambient/ambient_metrics.cc b/ash/public/cpp/ambient/ambient_metrics.cc
index cea8418..b94a530 100644
--- a/ash/public/cpp/ambient/ambient_metrics.cc
+++ b/ash/public/cpp/ambient/ambient_metrics.cc
@@ -11,6 +11,7 @@
 #include "base/metrics/histogram_functions.h"
 #include "base/strings/strcat.h"
 #include "base/time/time.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace ash {
 namespace ambient {
@@ -98,5 +99,13 @@
                               num_albums);
 }
 
+void RecordAmbientModeAnimationSmoothness(int smoothness,
+                                          AmbientAnimationTheme theme) {
+  base::UmaHistogramPercentage(
+      base::StrCat(
+          {"Ash.AmbientMode.LottieAnimationSmoothness.", ToString(theme)}),
+      smoothness);
+}
+
 }  // namespace ambient
 }  // namespace ash
diff --git a/ash/public/cpp/ambient/ambient_metrics.h b/ash/public/cpp/ambient/ambient_metrics.h
index d8000e39..38645b4 100644
--- a/ash/public/cpp/ambient/ambient_metrics.h
+++ b/ash/public/cpp/ambient/ambient_metrics.h
@@ -43,6 +43,10 @@
 
 ASH_PUBLIC_EXPORT void RecordAmbientModeSelectedNumberOfAlbums(int num_albums);
 
+ASH_PUBLIC_EXPORT void RecordAmbientModeAnimationSmoothness(
+    int smoothness,
+    AmbientAnimationTheme theme);
+
 }  // namespace ambient
 }  // namespace ash
 
diff --git a/ash/public/cpp/desk_template.cc b/ash/public/cpp/desk_template.cc
index 780f6668..4e7fe051 100644
--- a/ash/public/cpp/desk_template.cc
+++ b/ash/public/cpp/desk_template.cc
@@ -13,6 +13,27 @@
 
 namespace ash {
 
+namespace {
+
+std::string TabGroupDataToString(const app_restore::RestoreData* restore_data) {
+  std::string result = "tab groups:[";
+
+  for (const auto& app : restore_data->app_id_to_launch_list()) {
+    for (const auto& window : app.second) {
+      if (window.second->tab_group_infos.has_value()) {
+        for (const auto& tab_group : window.second->tab_group_infos.value()) {
+          result += "\n" + tab_group.ToString() + ",";
+        }
+      }
+    }
+  }
+
+  result += "]\n";
+  return result;
+}
+
+}  // namespace
+
 DeskTemplate::DeskTemplate(const std::string& uuid,
                            DeskTemplateSource source,
                            const std::string& name,
@@ -65,6 +86,8 @@
   desk_restore_data_->SetDeskIndex(desk_index);
 }
 
+// TODO(crbug.com/1328850): Factor out common elements between ToString and
+// ToDebugString.
 std::string DeskTemplate::ToString() const {
   std::string result =
       "Template name: " + base::UTF16ToUTF8(template_name_) + "\n";
@@ -127,8 +150,10 @@
   // Converting to value and printing the debug string may be more
   // intensive but gives more complete information which increases
   // the utility of this function.
-  if (desk_restore_data_)
+  if (desk_restore_data_) {
     result += desk_restore_data_->ConvertToValue().DebugString();
+    result += TabGroupDataToString(desk_restore_data_.get());
+  }
   return result;
 }
 
diff --git a/ash/system/time/calendar_model.cc b/ash/system/time/calendar_model.cc
index 6d859b7c..c58df0a7 100644
--- a/ash/system/time/calendar_model.cc
+++ b/ash/system/time/calendar_model.cc
@@ -193,16 +193,7 @@
   mru_months_.clear();
 }
 
-void CalendarModel::ResetLifetimeMetrics(
-    const base::Time& currently_shown_date) {
-  max_distance_browsed_ = 0;
-  first_on_screen_month_ =
-      calendar_utils::GetFirstDayOfMonth(currently_shown_date);
-}
-
 void CalendarModel::UploadLifetimeMetrics() {
-  base::UmaHistogramCounts100000("Ash.Calendar.FetchEvents.MaxDistanceBrowsed",
-                                 max_distance_browsed_);
   base::UmaHistogramCounts100000(
       "Ash.Calendar.FetchEvents.TotalCacheSizeMonths", event_months_.size());
 }
@@ -332,10 +323,6 @@
   // Record the size of the month, and the total number of months.
   base::UmaHistogramCounts1M("Ash.Calendar.FetchEvents.SingleMonthSize",
                              GetEventMapSize(event_months_[start_of_month]));
-
-  // If `start_of_month` is further, in months, from the on-screen month when
-  // the calendar first opened, then update the max distance.
-  UpdateMaxDistanceBrowsed(start_of_month);
 }
 
 void CalendarModel::OnEventFetchFailedInternalError(
@@ -347,13 +334,6 @@
   // specific error code, retry in some cases, etc.
 }
 
-void CalendarModel::UpdateMaxDistanceBrowsed(const base::Time& start_of_month) {
-  max_distance_browsed_ =
-      std::max(max_distance_browsed_,
-               static_cast<size_t>(abs(calendar_utils::GetMonthsBetween(
-                   first_on_screen_month_, start_of_month))));
-}
-
 bool CalendarModel::ShouldInsertEvent(const CalendarEvent* event) const {
   if (!event)
     return false;
diff --git a/ash/system/time/calendar_model.h b/ash/system/time/calendar_model.h
index d7b9dd25f..39b9d79 100644
--- a/ash/system/time/calendar_model.h
+++ b/ash/system/time/calendar_model.h
@@ -71,11 +71,6 @@
   // Clears out all events that start in a non-prunable month.
   void ClearAllPrunableEvents();
 
-  // Resets to defaults the values of all event fetch metrics recorded over the
-  // lifetime of a calendar session, i.e. between a single open and close of the
-  // calendar.
-  void ResetLifetimeMetrics(const base::Time& currently_shown_date);
-
   // Logs to UMA all event fetch metrics recorded over the lifetime of a
   // calendar session.
   void UploadLifetimeMetrics();
@@ -114,6 +109,11 @@
   // time difference. This method is only called when there's a timezone change.
   void RedistributeEvents();
 
+  // Checks whether `start_of_month` is further than we've gone, so far, from
+  // the on-screen month with which the calendar was opened and, if it has, then
+  // update our max distance.
+  void UpdateMaxDistanceBrowsed(const base::Time& start_of_month);
+
  protected:
   // Fetch events for `start_of_month`.
   virtual void MaybeFetchMonth(base::Time start_of_month);
@@ -207,11 +207,6 @@
       base::Time start_of_month,
       CalendarEventFetchInternalErrorCode error);
 
-  // Checks whether `start_of_month` is further than we've gone, so far, from
-  // the on-screen month with which the calendar was opened and, if it has, then
-  // update our max distance.
-  void UpdateMaxDistanceBrowsed(const base::Time& start_of_month);
-
   // Internal storage for fetched events, with each fetched month having a
   // map of days to events.
   MonthToEventsMap event_months_;
@@ -228,15 +223,6 @@
   // All fetch requests that are still in-progress.
   std::map<base::Time, std::unique_ptr<CalendarEventFetch>> pending_fetches_;
 
-  // The first on-screen month to have been displayed when the calendar was
-  // opened.
-  base::Time first_on_screen_month_;
-
-  // Maximum distance, in months, from the on-screen month first displayed in
-  // the calendar when it was opened. This is logged as a metric when the
-  // calendar is closed.
-  size_t max_distance_browsed_;
-
   ScopedSessionObserver session_observer_;
 
   base::ObserverList<Observer> observers_;
diff --git a/ash/system/time/calendar_view_controller.cc b/ash/system/time/calendar_view_controller.cc
index 0fb5c163..c17d5c5 100644
--- a/ash/system/time/calendar_view_controller.cc
+++ b/ash/system/time/calendar_view_controller.cc
@@ -34,15 +34,14 @@
 CalendarViewController::CalendarViewController()
     : currently_shown_date_(base::Time::Now()),
       calendar_open_time_(base::TimeTicks::Now()),
-      month_dwell_time_(base::TimeTicks::Now()) {
+      month_dwell_time_(base::TimeTicks::Now()),
+      first_shown_date_(base::Time::Now()) {
   std::set<base::Time> months = calendar_utils::GetSurroundingMonthsUTC(
       base::Time::Now() +
           calendar_utils::GetTimeDifference(currently_shown_date_),
       calendar_utils::kNumSurroundingMonthsCached);
   Shell::Get()->system_tray_model()->calendar_model()->AddNonPrunableMonths(
       months);
-  Shell::Get()->system_tray_model()->calendar_model()->ResetLifetimeMetrics(
-      currently_shown_date_);
 }
 
 CalendarViewController::~CalendarViewController() {
@@ -58,8 +57,10 @@
   if (user_journey_time_recorded_)
     return;
 
-  UmaHistogramMediumTimes("Ash.Calendar.UserJourneyTime.EventNotLaunched",
-                          base::TimeTicks::Now() - calendar_open_time_);
+  base::UmaHistogramMediumTimes("Ash.Calendar.UserJourneyTime.EventNotLaunched",
+                                base::TimeTicks::Now() - calendar_open_time_);
+  base::UmaHistogramCounts100000("Ash.Calendar.MaxDistanceBrowsed",
+                                 max_distance_browsed_);
 }
 
 void CalendarViewController::AddObserver(Observer* observer) {
@@ -84,9 +85,14 @@
   month_dwell_time_ = base::TimeTicks::Now();
 
   currently_shown_date_ = current_month_first_date;
-  for (auto& observer : observers_) {
+
+  max_distance_browsed_ =
+      std::max(max_distance_browsed_,
+               static_cast<size_t>(abs(calendar_utils::GetMonthsBetween(
+                   first_shown_date_, current_month_first_date))));
+
+  for (auto& observer : observers_)
     observer.OnMonthChanged();
-  }
 }
 
 base::Time CalendarViewController::GetOnScreenMonthFirstDayLocal() {
diff --git a/ash/system/time/calendar_view_controller.h b/ash/system/time/calendar_view_controller.h
index 3db43b84..36382c2 100644
--- a/ash/system/time/calendar_view_controller.h
+++ b/ash/system/time/calendar_view_controller.h
@@ -192,6 +192,14 @@
   // The current row index when the event list view is shown.
   int expanded_row_index_ = 0;
 
+  // Maximum distance, in months, from the on-screen month first displayed in
+  // the calendar when it was opened. This is logged as a metric when the
+  // calendar is closed.
+  size_t max_distance_browsed_ = 0;
+
+  // The first date shown, used to record max distance browsed metrics.
+  const base::Time first_shown_date_;
+
   base::ObserverList<Observer> observers_;
 
   base::WeakPtrFactory<CalendarViewController> weak_factory_{this};
diff --git a/ash/system/time/calendar_view_controller_unittest.cc b/ash/system/time/calendar_view_controller_unittest.cc
index 18a8d67..b350e73a6 100644
--- a/ash/system/time/calendar_view_controller_unittest.cc
+++ b/ash/system/time/calendar_view_controller_unittest.cc
@@ -239,4 +239,19 @@
   EXPECT_EQ(u"December", next_month_name);
 }
 
+// Tests that Ash.Calendar.MaxDistanceBrowsed records once on destruction of
+// CalendarViewController.
+TEST_F(CalendarViewControllerUnittest, MaxDistanceBrowsedRecordedOnClose) {
+  base::HistogramTester histogram_tester;
+  auto controller = std::make_unique<CalendarViewController>();
+
+  histogram_tester.ExpectTotalCount("Ash.Calendar.MaxDistanceBrowsed", 0);
+
+  // Destroy the controller (this happens when the calendar is closed). The
+  // metric should be recorded once.
+  controller.reset();
+
+  histogram_tester.ExpectTotalCount("Ash.Calendar.MaxDistanceBrowsed", 1);
+}
+
 }  // namespace ash
diff --git a/ash/system/time/calendar_view_unittest.cc b/ash/system/time/calendar_view_unittest.cc
index 14bfc431..f081227d 100644
--- a/ash/system/time/calendar_view_unittest.cc
+++ b/ash/system/time/calendar_view_unittest.cc
@@ -99,6 +99,10 @@
   }
 
   void CreateCalendarView() {
+    if (!widget_) {
+      widget_ = CreateFramelessTestWidget();
+      widget_->SetFullscreen(true);
+    }
     AccountId user_account = AccountId::FromUserEmail(kTestUser);
     GetSessionControllerClient()->SwitchActiveUser(user_account);
 
@@ -551,6 +555,86 @@
                 ->GetText());
 }
 
+// Tests the Ash.Calendar.MaxDistanceBrowsed metric only records once in
+// CalendarViews lifetime.
+TEST_F(CalendarViewTest, MaxDistanceBrowsedRecordsOncePerLifetime) {
+  base::Time date;
+  ASSERT_TRUE(base::Time::FromString("24 Oct 2021 10:00 GMT", &date));
+
+  // Set time override.
+  SetFakeNow(date);
+  base::subtle::ScopedTimeClockOverrides time_override(
+      &CalendarViewTest::FakeTimeNow, /*time_ticks_override=*/nullptr,
+      /*thread_ticks_override=*/nullptr);
+
+  auto histogram_tester = std::make_unique<base::HistogramTester>();
+
+  CreateCalendarView();
+  DestroyCalendarViewWidget();
+
+  // The metric should log once.
+  histogram_tester->ExpectTotalCount("Ash.Calendar.MaxDistanceBrowsed", 1);
+
+  // Create the CalendarView again, and scroll once. The metric should record
+  // once, but only once the widget has been destroyed.
+  histogram_tester = std::make_unique<base::HistogramTester>();
+  CreateCalendarView();
+
+  ScrollDownOneMonth();
+  histogram_tester->ExpectTotalCount("Ash.Calendar.MaxDistanceBrowsed", 0);
+  DestroyCalendarViewWidget();
+
+  histogram_tester->ExpectTotalCount("Ash.Calendar.MaxDistanceBrowsed", 1);
+
+  // Create the CalendarView again, scroll a few more times. Still the metric
+  // should only record once.
+  histogram_tester = std::make_unique<base::HistogramTester>();
+  CreateCalendarView();
+
+  ScrollDownOneMonth();
+  ScrollDownOneMonth();
+  ScrollDownOneMonth();
+  ScrollUpOneMonth();
+
+  DestroyCalendarViewWidget();
+  histogram_tester->ExpectTotalCount("Ash.Calendar.MaxDistanceBrowsed", 1);
+}
+
+// Tests the Ash.Calendar.MaxDistanceBrowsed metric records max distance
+// traveled from today.
+TEST_F(CalendarViewTest,
+       MaxDistanceBrowsedRecordsAbsoluteValueOfDistanceTraveled) {
+  base::Time date;
+  ASSERT_TRUE(base::Time::FromString("24 Oct 2021 10:00 GMT", &date));
+
+  // Set time override.
+  SetFakeNow(date);
+  base::subtle::ScopedTimeClockOverrides time_override(
+      &CalendarViewTest::FakeTimeNow, /*time_ticks_override=*/nullptr,
+      /*thread_ticks_override=*/nullptr);
+
+  auto histogram_tester = base::HistogramTester();
+
+  CreateCalendarView();
+  const int scroll_up_count = 10;
+  const int scroll_down_count = scroll_up_count - 1;
+  // Scroll up.
+  for (int i = 0; i < scroll_up_count; ++i)
+    ScrollUpOneMonth();
+  // Return to today.
+  for (int i = 0; i < scroll_up_count; ++i)
+    ScrollDownOneMonth();
+  // Scroll down from today.
+  for (int i = 0; i < scroll_down_count; ++i)
+    ScrollDownOneMonth();
+
+  DestroyCalendarViewWidget();
+
+  // `scroll_up_count` is the furthest traveled.
+  histogram_tester.ExpectBucketCount("Ash.Calendar.MaxDistanceBrowsed",
+                                     scroll_up_count, 1);
+}
+
 // Used to determine whether focus goes directly to the proper CalendarDateCell
 // prior to moving on to the EventListView.
 class DateCellFocusChangeListener : public views::FocusChangeListener {
diff --git a/ash/webui/personalization_app/resources/trusted/cros_button_style.css b/ash/webui/personalization_app/resources/trusted/cros_button_style.css
index dd1bb48..d5d133ef0 100644
--- a/ash/webui/personalization_app/resources/trusted/cros_button_style.css
+++ b/ash/webui/personalization_app/resources/trusted/cros_button_style.css
@@ -36,7 +36,7 @@
 cr-icon-button:focus-visible,
 cr-button:focus-visible {
   box-shadow: none;
-  outline: 2px solid var(--cros-focus-ring-color);
+  outline: 2px solid rgba(var(--cros-focus-ring-color), 0.8);
 }
 
 cr-icon-button:hover,
@@ -60,10 +60,6 @@
   --iron-icon-fill-color: var(--cros-button-label-color-primary) !important;
 }
 
-cr-button:focus-visible {
-  outline: 2px solid var(--cros-focus-ring-color);
-}
-
 iron-icon {
   --iron-icon-height: 20px;
   --iron-icon-width: 20px;
diff --git a/ash/webui/personalization_app/resources/trusted/user/avatar_camera_element.html b/ash/webui/personalization_app/resources/trusted/user/avatar_camera_element.html
index bd13cdd2..9d0609e6 100644
--- a/ash/webui/personalization_app/resources/trusted/user/avatar_camera_element.html
+++ b/ash/webui/personalization_app/resources/trusted/user/avatar_camera_element.html
@@ -2,6 +2,7 @@
   cr-dialog {
     --cr-dialog-width: 300px;
     --cr-dialog-body-padding-horizontal: 24px;
+    --cr-focus-outline-color: var(--cros-focus-ring-color);
   }
 
   div[slot='body'] {
diff --git a/ash/webui/personalization_app/resources/trusted/user/user_preview_element.html b/ash/webui/personalization_app/resources/trusted/user/user_preview_element.html
index 7af9911..6bdfab2 100644
--- a/ash/webui/personalization_app/resources/trusted/user/user_preview_element.html
+++ b/ash/webui/personalization_app/resources/trusted/user/user_preview_element.html
@@ -171,7 +171,7 @@
       <p id="name">[[info_.name]]</p>
       <div id="emailContainer" tabindex="0" on-click="onClickUserEmail_"
           on-keypress="onClickUserEmail_" role="link"
-          aria-label="$i18n{ariaLabelGoToAccountSettings}">
+          aria-description="$i18n{ariaLabelGoToAccountSettings}">
         <p id="email">[[info_.email]]</p>
         <iron-icon icon="cr:open-in-new"></iron-icon>
       </div>
diff --git a/base/task/sequence_manager/task_queue_impl.cc b/base/task/sequence_manager/task_queue_impl.cc
index 25c9633..1bcefa5 100644
--- a/base/task/sequence_manager/task_queue_impl.cc
+++ b/base/task/sequence_manager/task_queue_impl.cc
@@ -9,14 +9,17 @@
 #include <memory>
 #include <utility>
 
+#include "base/atomic_sequence_num.h"
 #include "base/check.h"
 #include "base/compiler_specific.h"
 #include "base/containers/stack_container.h"
 #include "base/feature_list.h"
 #include "base/logging.h"
 #include "base/memory/scoped_refptr.h"
+#include "base/metrics/histogram_macros.h"
 #include "base/notreached.h"
 #include "base/observer_list.h"
+#include "base/rand_util.h"
 #include "base/ranges/algorithm.h"
 #include "base/strings/stringprintf.h"
 #include "base/task/common/scoped_defer_task_posting.h"
@@ -522,6 +525,8 @@
     sequence_manager_->WillQueueTask(&pending_task, name_);
     MaybeReportIpcTaskQueuedFromMainThread(pending_task, name_);
   }
+  RecordQueuingDelayedTaskMetrics(pending_task.delayed_run_time -
+                                  lazy_now->Now());
   main_thread_only().delayed_incoming_queue.push(std::move(pending_task));
   UpdateWakeUp(lazy_now);
 
@@ -570,6 +575,43 @@
   TraceQueueSize();
 }
 
+void TaskQueueImpl::RecordQueuingDelayedTaskMetrics(TimeDelta delay) {
+  // This logic minimizes the performance overhead of emitting a histogram.
+  static constexpr int kBatchSize = 10000;
+  static const int kOffset = RandInt(0, kBatchSize - 1);
+  static std::atomic<size_t> max_delayed_incoming_queue_size = 0;
+  static AtomicSequenceNumber sample_counter;
+  // Sample all TaskQueue's towards a single histogram for simplicity (still
+  // gives us an idea of the overall data structure needs).
+  size_t local_max =
+      max_delayed_incoming_queue_size.load(std::memory_order_relaxed);
+
+  // Use random sampling to reduce the cost of recording these histograms.
+  if ((sample_counter.GetNext() - kOffset) % kBatchSize == 0) {
+    while (!max_delayed_incoming_queue_size.compare_exchange_weak(
+        local_max, 0, std::memory_order_relaxed)) {
+      // Retry
+    }
+
+    UMA_HISTOGRAM_LONG_TIMES("Scheduler.TaskQueueImpl.PostDelayedTaskDelay",
+                             delay);
+    UMA_HISTOGRAM_COUNTS_1000(
+        "Scheduler.TaskQueueImpl.DelayedIncomingQueueSize",
+        main_thread_only().delayed_incoming_queue.size());
+    UMA_HISTOGRAM_COUNTS_1000(
+        "Scheduler.TaskQueueImpl.MaxDelayedIncomingQueueSize", local_max);
+  } else {
+    // |max_delayed_incoming_queue_size| is atomically assigned the size of
+    // |delayed_incoming_queue| if it's bigger.
+    while (main_thread_only().delayed_incoming_queue.size() > local_max &&
+           !max_delayed_incoming_queue_size.compare_exchange_weak(
+               local_max, main_thread_only().delayed_incoming_queue.size(),
+               std::memory_order_relaxed)) {
+      // Retry
+    }
+  }
+}
+
 void TaskQueueImpl::ReloadEmptyImmediateWorkQueue() {
   DCHECK(main_thread_only().immediate_work_queue->Empty());
   main_thread_only().immediate_work_queue->TakeImmediateIncomingQueueTasks();
diff --git a/base/task/sequence_manager/task_queue_impl.h b/base/task/sequence_manager/task_queue_impl.h
index ad03f59..382a2bb 100644
--- a/base/task/sequence_manager/task_queue_impl.h
+++ b/base/task/sequence_manager/task_queue_impl.h
@@ -501,6 +501,12 @@
   void MoveReadyImmediateTasksToImmediateWorkQueueLocked()
       EXCLUSIVE_LOCKS_REQUIRED(any_thread_lock_);
 
+  // Records the delay for some tasks in the main thread and the size of the
+  // |delayed_incoming_queue| pseudorandomly in a histogram. The |delay| will be
+  // different than the delay passed to PostDelayedTask for cross-thread delayed
+  // tasks.
+  void RecordQueuingDelayedTaskMetrics(TimeDelta delay);
+
   // LazilyDeallocatedDeque use TimeTicks to figure out when to resize.  We
   // should use real time here always.
   using TaskDeque =
diff --git a/base/task/thread_pool/worker_thread.cc b/base/task/thread_pool/worker_thread.cc
index c206142..25b64de 100644
--- a/base/task/thread_pool/worker_thread.cc
+++ b/base/task/thread_pool/worker_thread.cc
@@ -7,6 +7,7 @@
 #include <stddef.h>
 
 #include <algorithm>
+#include <atomic>
 #include <utility>
 
 #include "base/allocator/buildflags.h"
@@ -15,11 +16,13 @@
 #include "base/check_op.h"
 #include "base/compiler_specific.h"
 #include "base/debug/alias.h"
+#include "base/feature_list.h"
 #include "base/synchronization/waitable_event.h"
 #include "base/task/thread_pool/environment_config.h"
 #include "base/task/thread_pool/task_tracker.h"
 #include "base/task/thread_pool/worker_thread_observer.h"
 #include "base/threading/hang_watcher.h"
+#include "base/time/time.h"
 #include "base/time/time_override.h"
 #include "base/trace_event/base_tracing.h"
 #include "build/build_config.h"
@@ -38,8 +41,42 @@
 #include "base/allocator/partition_allocator/thread_cache.h"
 #endif
 
-namespace base {
-namespace internal {
+namespace {
+#if BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC) && \
+    defined(PA_THREAD_CACHE_SUPPORTED)
+// Returns the desired sleep time before the worker has to wake up to purge
+// the cache thread or reclaim itself. |min_sleep_time| contains the minimal
+// acceptable amount of time to sleep.
+base::TimeDelta GetSleepTimeBeforePurge(base::TimeDelta min_sleep_time) {
+  const base::TimeTicks now = base::TimeTicks::Now();
+
+  // Do not wake up to purge within the first minute of process lifetime. In
+  // short lived processes this will avoid waking up to try and save memory
+  // for a heap that will be going away soon. For longer lived processes this
+  // should allow for better performance at process startup since even if a
+  // worker goes to sleep for kPurgeThreadCacheIdleDelay it's very likely it
+  // will be needed soon after because of heavy startup workloads.
+  constexpr base::TimeDelta kFirstSleepLength = base::Minutes(1);
+
+  // Use the first time a worker goes to sleep in this process as an
+  // approximation of the process creation time.
+  static const base::TimeTicks first_scheduled_wake = now + kFirstSleepLength;
+
+  // Align wakeups for purges to reduce the chances of taking the CPU out of
+  // sleep multiple times for these operations.
+  constexpr base::TimeDelta kPurgeThreadCacheIdleDelay = base::Seconds(1);
+  const base::TimeTicks snapped_wake =
+      (now + min_sleep_time)
+          .SnappedToNextTick(base::TimeTicks(), kPurgeThreadCacheIdleDelay);
+
+  // Avoid scheduling at |first_scheduled_wake| if it would result in a sleep
+  // that's too short.
+  return std::max(snapped_wake - now, first_scheduled_wake - now);
+}
+#endif
+}  // namespace
+
+namespace base::internal {
 
 constexpr TimeDelta WorkerThread::Delegate::kPurgeThreadCacheIdleDelay;
 
@@ -63,17 +100,32 @@
   // many times.
 #if BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC) && \
     defined(PA_THREAD_CACHE_SUPPORTED)
-  bool was_signaled = wake_up_event->TimedWait(
-      std::min(sleep_time, kPurgeThreadCacheIdleDelay));
+  TimeDelta min_sleep_time = std::min(sleep_time, kPurgeThreadCacheIdleDelay);
+
+  static const base::Feature kDelayFirstWorkerWake{
+      "DelayFirstWorkerWake", base::FEATURE_DISABLED_BY_DEFAULT};
+  // ThreadPoolInstance::Start() must always be after FeatureList
+  // initialization. This means this function has access to the feature state on
+  // first call. Cache the feature check to avoid the overhead of calling
+  // IsEnabled() every time.
+  static const bool is_delay_first_worker_sleep_enabled =
+      FeatureList::IsEnabled(kDelayFirstWorkerWake);
+
+  if (is_delay_first_worker_sleep_enabled)
+    min_sleep_time = GetSleepTimeBeforePurge(min_sleep_time);
+
+  const bool was_signaled = wake_up_event->TimedWait(min_sleep_time);
 
   // Timed out.
   if (!was_signaled) {
     ThreadCache::PurgeCurrentThread();
 
-    if (sleep_time > kPurgeThreadCacheIdleDelay) {
+    // The thread woke up to purge before its standard reclaim time. Sleep for
+    // what's remaining until then.
+    if (sleep_time > min_sleep_time) {
       wake_up_event->TimedWait(sleep_time.is_max()
                                    ? base::TimeDelta::Max()
-                                   : sleep_time - kPurgeThreadCacheIdleDelay);
+                                   : sleep_time - min_sleep_time);
     }
   }
 #else
@@ -433,5 +485,4 @@
   PERFETTO_INTERNAL_ADD_EMPTY_EVENT();
 }
 
-}  // namespace internal
-}  // namespace base
+}  // namespace base::internal
diff --git a/build/android/gyp/jetify_jar.py b/build/android/gyp/jetify_jar.py
deleted file mode 100755
index e97ad97..0000000
--- a/build/android/gyp/jetify_jar.py
+++ /dev/null
@@ -1,68 +0,0 @@
-#!/usr/bin/env python3
-#
-# Copyright 2019 The Chromium Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-from __future__ import print_function
-
-import argparse
-import os
-import subprocess
-import sys
-
-from util import build_utils
-
-
-def _AddArguments(parser):
-  """Adds arguments related to jetifying to parser.
-
-  Args:
-    parser: ArgumentParser object.
-  """
-  parser.add_argument(
-      '--input-path',
-      required=True,
-      help='Path to input file(s). Either the classes '
-      'directory, or the path to a jar.')
-  parser.add_argument(
-      '--output-path',
-      required=True,
-      help='Path to output final file(s) to. Either the '
-      'final classes directory, or the directory in '
-      'which to place the instrumented/copied jar.')
-  parser.add_argument(
-      '--jetify-path', required=True, help='Path to jetify bin.')
-  parser.add_argument(
-      '--jetify-config-path', required=True, help='Path to jetify config file.')
-
-
-def _RunJetifyCommand(parser):
-  args = parser.parse_args()
-  cmd = [
-      args.jetify_path,
-      '-i',
-      args.input_path,
-      '-o',
-      args.output_path,
-      # Need to suppress a lot of warning output when jar doesn't have
-      # any references rewritten.
-      '-l',
-      'error'
-  ]
-  if args.jetify_config_path:
-    cmd.extend(['-c', args.jetify_config_path])
-  # Must wait for jetify command to complete to prevent race condition.
-  env = os.environ.copy()
-  env['JAVA_HOME'] = build_utils.JAVA_HOME
-  subprocess.check_call(cmd, env=env)
-
-
-def main():
-  parser = argparse.ArgumentParser()
-  _AddArguments(parser)
-  _RunJetifyCommand(parser)
-
-
-if __name__ == '__main__':
-  sys.exit(main())
diff --git a/build/android/gyp/jetify_jar.pydeps b/build/android/gyp/jetify_jar.pydeps
deleted file mode 100644
index 6a1a589..0000000
--- a/build/android/gyp/jetify_jar.pydeps
+++ /dev/null
@@ -1,6 +0,0 @@
-# Generated by running:
-#   build/print_python_deps.py --root build/android/gyp --output build/android/gyp/jetify_jar.pydeps build/android/gyp/jetify_jar.py
-../../gn_helpers.py
-jetify_jar.py
-util/__init__.py
-util/build_utils.py
diff --git a/build/config/android/internal_rules.gni b/build/config/android/internal_rules.gni
index a430e73..c1e532e1 100644
--- a/build/config/android/internal_rules.gni
+++ b/build/config/android/internal_rules.gni
@@ -876,6 +876,9 @@
         invoker.test_suite,
       ]
 
+      # Test runner uses this generated wrapper script.
+      data += [ "$root_build_dir/bin/helper/${invoker.test_suite}" ]
+
       deps += [ ":${invoker.test_suite}$build_config_target_suffix" ]
       _junit_binary_build_config =
           "${target_gen_dir}/${invoker.test_suite}.build_config.json"
@@ -1779,7 +1782,11 @@
   template("filter_jar") {
     action_with_pydeps(target_name) {
       script = "//build/android/gyp/filter_zip.py"
-      forward_variables_from(invoker, TESTONLY_AND_VISIBILITY + [ "deps" ])
+      forward_variables_from(invoker,
+                             TESTONLY_AND_VISIBILITY + [
+                                   "deps",
+                                   "data",
+                                 ])
       inputs = [ invoker.input_jar ]
       if (defined(invoker.inputs)) {
         inputs += invoker.inputs
@@ -1822,6 +1829,7 @@
     filter_jar(_filter_jar_target_name) {
       forward_variables_from(invoker,
                              [
+                               "data",
                                "jar_excluded_patterns",
                                "jar_included_patterns",
                              ])
@@ -3862,6 +3870,9 @@
                                    "jar_excluded_patterns",
                                    "jar_included_patterns",
                                  ])
+
+          # Robolectric tests require these to be on swarming.
+          data = [ _host_processed_jar_path ]
           input_jar_path = _unprocessed_jar_path
           jar_deps = _unprocessed_jar_deps + _full_classpath_deps
           output_jar_path = _host_processed_jar_path
diff --git a/build/toolchain/linux/BUILD.gn b/build/toolchain/linux/BUILD.gn
index 64face8..e67f09f 100644
--- a/build/toolchain/linux/BUILD.gn
+++ b/build/toolchain/linux/BUILD.gn
@@ -207,6 +207,8 @@
     also_build_lacros_chrome = false
     chromeos_is_browser_only = true
     use_clang_coverage = false
+    dcheck_always_on = false
+    symbol_level=1
   }
 }
 
diff --git a/build/toolchain/win/ml.py b/build/toolchain/win/ml.py
deleted file mode 100755
index 6a1b6e57..0000000
--- a/build/toolchain/win/ml.py
+++ /dev/null
@@ -1,290 +0,0 @@
-#!/usr/bin/env python
-# Copyright 2018 The Chromium Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-"""Wraps ml.exe or ml64.exe and postprocesses the output to be deterministic.
-Sets timestamp in .obj file to 0, hence incompatible with link.exe /incremental.
-
-Use by prefixing the ml(64).exe invocation with this script:
-    python ml.py ml.exe [args...]"""
-
-import array
-import collections
-import struct
-import subprocess
-import sys
-
-
-class Struct(object):
-  """A thin wrapper around the struct module that returns a namedtuple"""
-  def __init__(self, name, *args):
-    """Pass the name of the return type, and then an interleaved list of
-    format strings as used by the struct module and of field names."""
-    self.fmt = '<' + ''.join(args[0::2])
-    self.type = collections.namedtuple(name, args[1::2])
-
-  def pack_into(self, buffer, offset, data):
-    return struct.pack_into(self.fmt, buffer, offset, *data)
-
-  def unpack_from(self, buffer, offset=0):
-    return self.type(*struct.unpack_from(self.fmt, buffer, offset))
-
-  def size(self):
-    return struct.calcsize(self.fmt)
-
-
-def Subtract(nt, **kwargs):
-  """Subtract(nt, f=2) returns a new namedtuple with 2 subtracted from nt.f"""
-  return nt._replace(**{k: getattr(nt, k) - v for k, v in kwargs.items()})
-
-
-def MakeDeterministic(objdata):
-  # Takes data produced by ml(64).exe (without any special flags) and
-  # 1. Sets the timestamp to 0
-  # 2. Strips the .debug$S section (which contains an unwanted absolute path)
-
-  # This makes several assumptions about ml's output:
-  # - Section data is in the same order as the corresponding section headers:
-  #   section headers preceding the .debug$S section header have their data
-  #   preceding the .debug$S section data; likewise for section headers
-  #   following the .debug$S section.
-  # - The .debug$S section contains only the absolute path to the obj file and
-  #   nothing else, in particular there's only a single entry in the symbol
-  #   table referring to the .debug$S section.
-  # - There are no COFF line number entries.
-  # - There's no IMAGE_SYM_CLASS_CLR_TOKEN symbol.
-  # These seem to hold in practice; if they stop holding this script needs to
-  # become smarter.
-
-  objdata = array.array('b', objdata)  # Writable, e.g. via struct.pack_into.
-
-  # Read coff header.
-  COFFHEADER = Struct('COFFHEADER',
-                      'H', 'Machine',
-                      'H', 'NumberOfSections',
-                      'I', 'TimeDateStamp',
-                      'I', 'PointerToSymbolTable',
-                      'I', 'NumberOfSymbols',
-
-                      'H', 'SizeOfOptionalHeader',
-                      'H', 'Characteristics')
-  coff_header = COFFHEADER.unpack_from(objdata)
-  assert coff_header.SizeOfOptionalHeader == 0  # Only set for binaries.
-
-  # Read section headers following coff header.
-  SECTIONHEADER = Struct('SECTIONHEADER',
-                         '8s', 'Name',
-                         'I', 'VirtualSize',
-                         'I', 'VirtualAddress',
-
-                         'I', 'SizeOfRawData',
-                         'I', 'PointerToRawData',
-                         'I', 'PointerToRelocations',
-                         'I', 'PointerToLineNumbers',
-
-                         'H', 'NumberOfRelocations',
-                         'H', 'NumberOfLineNumbers',
-                         'I', 'Characteristics')
-  section_headers = []
-  debug_section_index = -1
-  for i in range(0, coff_header.NumberOfSections):
-    section_header = SECTIONHEADER.unpack_from(
-        objdata, offset=COFFHEADER.size() + i * SECTIONHEADER.size())
-    assert not section_header[0].startswith(b'/')  # Support short names only.
-    section_headers.append(section_header)
-
-    if section_header.Name == b'.debug$S':
-      assert debug_section_index == -1
-      debug_section_index = i
-  assert debug_section_index != -1
-
-  data_start = COFFHEADER.size() + len(section_headers) * SECTIONHEADER.size()
-
-  # Verify the .debug$S section looks like we expect.
-  assert section_headers[debug_section_index].Name == b'.debug$S'
-  assert section_headers[debug_section_index].VirtualSize == 0
-  assert section_headers[debug_section_index].VirtualAddress == 0
-  debug_size = section_headers[debug_section_index].SizeOfRawData
-  debug_offset = section_headers[debug_section_index].PointerToRawData
-  assert section_headers[debug_section_index].PointerToRelocations == 0
-  assert section_headers[debug_section_index].PointerToLineNumbers == 0
-  assert section_headers[debug_section_index].NumberOfRelocations == 0
-  assert section_headers[debug_section_index].NumberOfLineNumbers == 0
-
-  # Make sure sections in front of .debug$S have their data preceding it.
-  for header in section_headers[:debug_section_index]:
-    assert header.PointerToRawData < debug_offset
-    assert header.PointerToRelocations < debug_offset
-    assert header.PointerToLineNumbers < debug_offset
-
-  # Make sure sections after of .debug$S have their data following it.
-  for header in section_headers[debug_section_index + 1:]:
-    # Make sure the .debug$S data is at the very end of section data:
-    assert header.PointerToRawData > debug_offset
-    assert header.PointerToRelocations == 0
-    assert header.PointerToLineNumbers == 0
-
-  # Make sure the first non-empty section's data starts right after the section
-  # headers.
-  for section_header in section_headers:
-    if section_header.PointerToRawData == 0:
-      assert section_header.PointerToRelocations == 0
-      assert section_header.PointerToLineNumbers == 0
-      continue
-    assert section_header.PointerToRawData == data_start
-    break
-
-  # Make sure the symbol table (and hence, string table) appear after the last
-  # section:
-  assert (coff_header.PointerToSymbolTable >=
-      section_headers[-1].PointerToRawData + section_headers[-1].SizeOfRawData)
-
-  # The symbol table contains a symbol for the no-longer-present .debug$S
-  # section. If we leave it there, lld-link will complain:
-  #
-  #    lld-link: error: .debug$S should not refer to non-existent section 5
-  #
-  # so we need to remove that symbol table entry as well. This shifts symbol
-  # entries around and we need to update symbol table indices in:
-  # - relocations
-  # - line number records (never present)
-  # - one aux symbol entry (IMAGE_SYM_CLASS_CLR_TOKEN; not present in ml output)
-  SYM = Struct('SYM',
-               '8s', 'Name',
-               'I', 'Value',
-               'h', 'SectionNumber',  # Note: Signed!
-               'H', 'Type',
-
-               'B', 'StorageClass',
-               'B', 'NumberOfAuxSymbols')
-  i = 0
-  debug_sym = -1
-  while i < coff_header.NumberOfSymbols:
-    sym_offset = coff_header.PointerToSymbolTable + i * SYM.size()
-    sym = SYM.unpack_from(objdata, sym_offset)
-
-    # 107 is IMAGE_SYM_CLASS_CLR_TOKEN, which has aux entry "CLR Token
-    # Definition", which contains a symbol index. Check it's never present.
-    assert sym.StorageClass != 107
-
-    # Note: sym.SectionNumber is 1-based, debug_section_index is 0-based.
-    if sym.SectionNumber - 1 == debug_section_index:
-      assert debug_sym == -1, 'more than one .debug$S symbol found'
-      debug_sym = i
-      # Make sure the .debug$S symbol looks like we expect.
-      # In particular, it should have exactly one aux symbol.
-      assert sym.Name == b'.debug$S'
-      assert sym.Value == 0
-      assert sym.Type == 0
-      assert sym.StorageClass == 3
-      assert sym.NumberOfAuxSymbols == 1
-    elif sym.SectionNumber > debug_section_index:
-      sym = Subtract(sym, SectionNumber=1)
-      SYM.pack_into(objdata, sym_offset, sym)
-    i += 1 + sym.NumberOfAuxSymbols
-  assert debug_sym != -1, '.debug$S symbol not found'
-
-  # Note: Usually the .debug$S section is the last, but for files saying
-  # `includelib foo.lib`, like safe_terminate_process.asm in 32-bit builds,
-  # this isn't true: .drectve is after .debug$S.
-
-  # Update symbol table indices in relocations.
-  # There are a few processor types that have one or two relocation types
-  # where SymbolTableIndex has a different meaning, but not for x86.
-  REL = Struct('REL',
-               'I', 'VirtualAddress',
-               'I', 'SymbolTableIndex',
-               'H', 'Type')
-  for header in section_headers[0:debug_section_index]:
-    for j in range(0, header.NumberOfRelocations):
-      rel_offset = header.PointerToRelocations + j * REL.size()
-      rel = REL.unpack_from(objdata, rel_offset)
-      assert rel.SymbolTableIndex != debug_sym
-      if rel.SymbolTableIndex > debug_sym:
-        rel = Subtract(rel, SymbolTableIndex=2)
-        REL.pack_into(objdata, rel_offset, rel)
-
-  # Update symbol table indices in line numbers -- just check they don't exist.
-  for header in section_headers:
-    assert header.NumberOfLineNumbers == 0
-
-  # Now that all indices are updated, remove the symbol table entry referring to
-  # .debug$S and its aux entry.
-  del objdata[coff_header.PointerToSymbolTable + debug_sym * SYM.size():
-              coff_header.PointerToSymbolTable + (debug_sym + 2) * SYM.size()]
-
-  # Now we know that it's safe to write out the input data, with just the
-  # timestamp overwritten to 0, the last section header cut out (and the
-  # offsets of all other section headers decremented by the size of that
-  # one section header), and the last section's data cut out. The symbol
-  # table offset needs to be reduced by one section header and the size of
-  # the missing section.
-  # (The COFF spec only requires on-disk sections to be aligned in image files,
-  # for obj files it's not required. If that wasn't the case, deleting slices
-  # if data would not generally be safe.)
-
-  # Update section offsets and remove .debug$S section data.
-  for i in range(0, debug_section_index):
-    header = section_headers[i]
-    if header.SizeOfRawData:
-      header = Subtract(header, PointerToRawData=SECTIONHEADER.size())
-    if header.NumberOfRelocations:
-      header = Subtract(header, PointerToRelocations=SECTIONHEADER.size())
-    if header.NumberOfLineNumbers:
-      header = Subtract(header, PointerToLineNumbers=SECTIONHEADER.size())
-    SECTIONHEADER.pack_into(
-        objdata, COFFHEADER.size() + i * SECTIONHEADER.size(), header)
-  for i in range(debug_section_index + 1, len(section_headers)):
-    header = section_headers[i]
-    shift = SECTIONHEADER.size() + debug_size
-    if header.SizeOfRawData:
-      header = Subtract(header, PointerToRawData=shift)
-    if header.NumberOfRelocations:
-      header = Subtract(header, PointerToRelocations=shift)
-    if header.NumberOfLineNumbers:
-      header = Subtract(header, PointerToLineNumbers=shift)
-    SECTIONHEADER.pack_into(
-        objdata, COFFHEADER.size() + i * SECTIONHEADER.size(), header)
-
-  del objdata[debug_offset:debug_offset + debug_size]
-
-  # Finally, remove .debug$S section header and update coff header.
-  coff_header = coff_header._replace(TimeDateStamp=0)
-  coff_header = Subtract(coff_header,
-                         NumberOfSections=1,
-                         PointerToSymbolTable=SECTIONHEADER.size() + debug_size,
-                         NumberOfSymbols=2)
-  COFFHEADER.pack_into(objdata, 0, coff_header)
-
-  del objdata[
-      COFFHEADER.size() + debug_section_index * SECTIONHEADER.size():
-      COFFHEADER.size() + (debug_section_index + 1) * SECTIONHEADER.size()]
-
-  # All done!
-  if sys.version_info.major == 2:
-    return objdata.tostring()
-  else:
-    return objdata.tobytes()
-
-
-def main():
-  ml_result = subprocess.call(sys.argv[1:])
-  if ml_result != 0:
-    return ml_result
-
-  objfile = None
-  for i in range(1, len(sys.argv)):
-    if sys.argv[i].startswith('/Fo'):
-      objfile = sys.argv[i][len('/Fo'):]
-  assert objfile, 'failed to find ml output'
-
-  with open(objfile, 'rb') as f:
-    objdata = f.read()
-  objdata = MakeDeterministic(objdata)
-  with open(objfile, 'wb') as f:
-    f.write(objdata)
-
-
-if __name__ == '__main__':
-  sys.exit(main())
diff --git a/build/toolchain/win/toolchain.gni b/build/toolchain/win/toolchain.gni
index e7fd6209..9cdcc6bf 100644
--- a/build/toolchain/win/toolchain.gni
+++ b/build/toolchain/win/toolchain.gni
@@ -254,11 +254,12 @@
           ml = "armasm64.exe"
         }
       } else {
-        # x86/x64 builds always use the MSVC assembler.
+        prefix = rebase_path("$clang_base_path/bin", root_build_dir)
+        ml = "$prefix/llvm-ml.exe"
         if (toolchain_args.current_cpu == "x64") {
-          ml = "ml64.exe"
+          ml += " --m64"
         } else {
-          ml = "ml.exe"
+          ml += " --m32"
         }
       }
 
@@ -270,16 +271,6 @@
         if (toolchain_args.current_cpu != "arm64") {
           ml += " /c"
         }
-        if (use_lld) {
-          # Wrap ml(64).exe with a script that makes its output deterministic.
-          # It's lld only because the script zaps obj Timestamp which
-          # link.exe /incremental looks at.
-          # TODO(https://crbug.com/762167): If we end up writing an llvm-ml64,
-          # make sure it has deterministic output (maybe with /Brepro or
-          # something) and remove this wrapper.
-          ml_py = rebase_path("//build/toolchain/win/ml.py", root_build_dir)
-          ml = "$python_path $ml_py $ml"
-        }
       }
       if (toolchain_args.current_cpu != "arm64" || toolchain_is_clang) {
         command = "$python_path $_tool_wrapper_path asm-wrapper $env $ml {{defines}} {{include_dirs}} {{asmflags}} {{source}}"
diff --git a/cc/metrics/event_latency_tracing_recorder.cc b/cc/metrics/event_latency_tracing_recorder.cc
index acf108a..67ea64b 100644
--- a/cc/metrics/event_latency_tracing_recorder.cc
+++ b/cc/metrics/event_latency_tracing_recorder.cc
@@ -227,64 +227,67 @@
   if (stage_history) {
     DCHECK(viz_breakdown);
     // Find the first compositor stage that starts at the same time or after the
-    // end of the final event dispatch stage. At least,
-    // SubmitCompositorFrameToPresentationCompositorFrame stage should match
-    // this criteria.
+    // end of the final event dispatch stage.
     auto stage_it = std::find_if(
         stage_history->begin(), stage_history->end(),
         [dispatch_timestamp](const CompositorFrameReporter::StageData& stage) {
           return stage.start_time >= dispatch_timestamp;
         });
-    DCHECK(stage_it != stage_history->end());
+    // TODO(crbug.com/1330903): Ideally, at least the start time of
+    // SubmitCompositorFrameToPresentationCompositorFrame stage should be
+    // greater than or equal to the final event dispatch timestamp, but
+    // apparently, this is not always the case (see crbug.com/1330903). Skip
+    // recording compositor stages for now until we investigate the issue.
+    if (stage_it != stage_history->end()) {
+      DCHECK(dispatch_stage ==
+                 EventMetrics::DispatchStage::kRendererCompositorFinished ||
+             dispatch_stage ==
+                 EventMetrics::DispatchStage::kRendererMainFinished);
 
-    DCHECK(dispatch_stage ==
-               EventMetrics::DispatchStage::kRendererCompositorFinished ||
-           dispatch_stage ==
-               EventMetrics::DispatchStage::kRendererMainFinished);
-
-    // Record dispatch-to-compositor stage only if it has non-zero duration.
-    if (dispatch_timestamp < stage_it->start_time) {
-      const char* d2c_breakdown_name = GetDispatchToCompositorBreakdownName(
-          dispatch_stage, stage_it->stage_type);
-      TRACE_EVENT_BEGIN(kTracingCategory,
-                        perfetto::StaticString{d2c_breakdown_name}, trace_track,
-                        dispatch_timestamp);
-      TRACE_EVENT_END(kTracingCategory, trace_track, stage_it->start_time);
-    }
-
-    // Compositor stages.
-    for (; stage_it != stage_history->end(); ++stage_it) {
-      if (stage_it->start_time >= termination_time)
-        break;
-      DCHECK_GE(stage_it->end_time, stage_it->start_time);
-      if (stage_it->start_time == stage_it->end_time)
-        continue;
-      const char* stage_name =
-          CompositorFrameReporter::GetStageName(stage_it->stage_type);
-
-      TRACE_EVENT_BEGIN(kTracingCategory, perfetto::StaticString{stage_name},
-                        trace_track, stage_it->start_time);
-
-      if (stage_it->stage_type ==
-          CompositorFrameReporter::StageType::
-              kSubmitCompositorFrameToPresentationCompositorFrame) {
-        DCHECK(viz_breakdown);
-        for (auto it = viz_breakdown->CreateIterator(true); it.IsValid();
-             it.Advance()) {
-          base::TimeTicks start_time = it.GetStartTime();
-          base::TimeTicks end_time = it.GetEndTime();
-          if (start_time >= end_time)
-            continue;
-          const char* breakdown_name =
-              CompositorFrameReporter::GetVizBreakdownName(it.GetBreakdown());
-          TRACE_EVENT_BEGIN(kTracingCategory,
-                            perfetto::StaticString{breakdown_name}, trace_track,
-                            start_time);
-          TRACE_EVENT_END(kTracingCategory, trace_track, end_time);
-        }
+      // Record dispatch-to-compositor stage only if it has non-zero duration.
+      if (dispatch_timestamp < stage_it->start_time) {
+        const char* d2c_breakdown_name = GetDispatchToCompositorBreakdownName(
+            dispatch_stage, stage_it->stage_type);
+        TRACE_EVENT_BEGIN(kTracingCategory,
+                          perfetto::StaticString{d2c_breakdown_name},
+                          trace_track, dispatch_timestamp);
+        TRACE_EVENT_END(kTracingCategory, trace_track, stage_it->start_time);
       }
 
-      TRACE_EVENT_END(kTracingCategory, trace_track, stage_it->end_time);
+      // Compositor stages.
+      for (; stage_it != stage_history->end(); ++stage_it) {
+        if (stage_it->start_time >= termination_time)
+          break;
+        DCHECK_GE(stage_it->end_time, stage_it->start_time);
+        if (stage_it->start_time == stage_it->end_time)
+          continue;
+        const char* stage_name =
+            CompositorFrameReporter::GetStageName(stage_it->stage_type);
+
+        TRACE_EVENT_BEGIN(kTracingCategory, perfetto::StaticString{stage_name},
+                          trace_track, stage_it->start_time);
+
+        if (stage_it->stage_type ==
+            CompositorFrameReporter::StageType::
+                kSubmitCompositorFrameToPresentationCompositorFrame) {
+          DCHECK(viz_breakdown);
+          for (auto it = viz_breakdown->CreateIterator(true); it.IsValid();
+               it.Advance()) {
+            base::TimeTicks start_time = it.GetStartTime();
+            base::TimeTicks end_time = it.GetEndTime();
+            if (start_time >= end_time)
+              continue;
+            const char* breakdown_name =
+                CompositorFrameReporter::GetVizBreakdownName(it.GetBreakdown());
+            TRACE_EVENT_BEGIN(kTracingCategory,
+                              perfetto::StaticString{breakdown_name},
+                              trace_track, start_time);
+            TRACE_EVENT_END(kTracingCategory, trace_track, end_time);
+          }
+        }
+
+        TRACE_EVENT_END(kTracingCategory, trace_track, stage_it->end_time);
+      }
     }
   } else {
     DCHECK(!viz_breakdown);
diff --git a/cc/trees/ukm_manager.cc b/cc/trees/ukm_manager.cc
index 4bb9182a..e0b88d5 100644
--- a/cc/trees/ukm_manager.cc
+++ b/cc/trees/ukm_manager.cc
@@ -330,7 +330,13 @@
         [dispatch_timestamp](const CompositorFrameReporter::StageData& stage) {
           return stage.start_time >= dispatch_timestamp;
         });
-    DCHECK(stage_it != stage_history.end());
+    // TODO(crbug.com/1330903): Ideally, at least the start time of
+    // SubmitCompositorFrameToPresentationCompositorFrame stage should be
+    // greater than or equal to the final event dispatch timestamp, but
+    // apparently, this is not always the case (see crbug.com/1330903). Skip
+    // recording compositor stages for now until we investigate the issue.
+    if (stage_it == stage_history.end())
+      continue;
 
     switch (dispatch_stage) {
       case EventMetrics::DispatchStage::kRendererCompositorFinished:
diff --git a/chrome/android/chrome_test_java_sources.gni b/chrome/android/chrome_test_java_sources.gni
index 747c7d2..116f0c5 100644
--- a/chrome/android/chrome_test_java_sources.gni
+++ b/chrome/android/chrome_test_java_sources.gni
@@ -20,6 +20,7 @@
   "javatests/src/org/chromium/chrome/browser/FeaturesAnnotationsTest.java",
   "javatests/src/org/chromium/chrome/browser/FocusedEditableTextFieldZoomTest.java",
   "javatests/src/org/chromium/chrome/browser/HTTPSTabsOpenedFromExternalAppTest.java",
+  "javatests/src/org/chromium/chrome/browser/ImageFetcherIntegrationTest.java",
   "javatests/src/org/chromium/chrome/browser/InstalledAppTest.java",
   "javatests/src/org/chromium/chrome/browser/IntentFilterUnitTest.java",
   "javatests/src/org/chromium/chrome/browser/IntentHandlerBrowserTest.java",
diff --git a/chrome/android/java/res/values-night/values.xml b/chrome/android/java/res/values-night/values.xml
index 1805181..d5e8c0b 100644
--- a/chrome/android/java/res/values-night/values.xml
+++ b/chrome/android/java/res/values-night/values.xml
@@ -5,6 +5,5 @@
 
 <resources>
     <!-- Tablet tab strip -->
-    <item name="compositor_background_tab_outline_alpha" format="float" type="dimen">0.15</item>
     <item name="compositor_background_tab_overlay_alpha" format="float" type="dimen">0.3</item>
 </resources>
diff --git a/chrome/android/java/res/values/values.xml b/chrome/android/java/res/values/values.xml
index bf615b74b..f28204fa 100644
--- a/chrome/android/java/res/values/values.xml
+++ b/chrome/android/java/res/values/values.xml
@@ -36,7 +36,7 @@
     <item name="contextual_search_sheet_full_height_fraction" format="float" type="dimen">0.95</item>
 
     <!-- Tablet tab strip -->
-    <item name="compositor_background_tab_outline_alpha" format="float" type="dimen">0.2</item>
+    <item name="compositor_background_tab_outline_alpha" format="float" type="dimen">0.5</item>
     <item name="compositor_background_tab_overlay_alpha" format="float" type="dimen">0</item>
 
     <!-- Revamped Incognito NTP -->
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 c27d9300..ee1e395 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
@@ -81,6 +81,7 @@
                 add(ChromeFeatureList.CCT_RESIZABLE_FOR_THIRD_PARTIES);
                 add(ChromeFeatureList.CCT_TOOLBAR_CUSTOMIZATIONS);
                 add(ChromeFeatureList.CLOSE_TAB_SUGGESTIONS);
+                add(ChromeFeatureList.CREATE_SAFEBROWSING_ON_STARTUP);
                 add(ChromeFeatureList.CRITICAL_PERSISTED_TAB_DATA);
                 add(ChromeFeatureList.COMMAND_LINE_ON_NON_ROOTED);
                 add(ChromeFeatureList.CONDITIONAL_TAB_STRIP_ANDROID);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/autofill/AutofillExpirationDateFixFlowPrompt.java b/chrome/android/java/src/org/chromium/chrome/browser/autofill/AutofillExpirationDateFixFlowPrompt.java
index 4cf87f3..704558c 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/autofill/AutofillExpirationDateFixFlowPrompt.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/autofill/AutofillExpirationDateFixFlowPrompt.java
@@ -94,6 +94,7 @@
                 cardholderAccount, confirmButtonLabel, filledConfirmButton);
         mDelegate = delegate;
         mErrorMessage = (TextView) mDialogView.findViewById(R.id.error_message);
+        // Infobar: show masked card number only.
         TextView cardDetailsMasked = (TextView) mDialogView.findViewById(R.id.cc_details_masked);
         cardDetailsMasked.setText(cardLabel);
         mDialogView.findViewById(R.id.message_divider).setVisibility(View.GONE);
@@ -118,6 +119,7 @@
         // Set drawable id as 0 to remove the icon on the title.
         this(context, delegate, title, /*drawableId=*/0, cardLabel, cardholderAccount,
                 confirmButtonLabel, true);
+        // Message: show masked card number, divider and gpay logo.
         mDialogView.findViewById(R.id.message_divider).setVisibility(View.VISIBLE);
         mDialogView.findViewById(R.id.google_pay_logo).setVisibility(View.VISIBLE);
     }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/autofill/AutofillNameFixFlowPrompt.java b/chrome/android/java/src/org/chromium/chrome/browser/autofill/AutofillNameFixFlowPrompt.java
index 04ccb4c..a134dba 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/autofill/AutofillNameFixFlowPrompt.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/autofill/AutofillNameFixFlowPrompt.java
@@ -95,6 +95,8 @@
         super(context, delegate, R.layout.autofill_name_fixflow, title, drawableId,
                 cardholderAccount, confirmButtonLabel, filledConfirmButton);
         mDelegate = delegate;
+        // Dialog of infobar doesn't show any details of the cc.
+        mDialogView.findViewById(R.id.cc_details).setVisibility(View.GONE);
         mUserNameInput = (EditText) mDialogView.findViewById(R.id.cc_name_edit);
         mUserNameInput.setText(inferredName, BufferType.EDITABLE);
         mNameFixFlowTooltipIcon = (ImageView) mDialogView.findViewById(R.id.cc_name_tooltip_icon);
@@ -126,6 +128,7 @@
             String confirmButtonLabel) {
         this(context, delegate, inferredName, title, /*drawableId=*/0, cardholderAccount,
                 confirmButtonLabel, true);
+        // Unlike infobar, dialog of Message UI should show all cc details.
         mDialogView.findViewById(R.id.cc_details).setVisibility(View.VISIBLE);
         TextView detailsMasked = mDialogView.findViewById(R.id.cc_details_masked);
         detailsMasked.setText(cardLabel);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchFieldTrial.java b/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchFieldTrial.java
index 1fefc6f..0bc1836 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchFieldTrial.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchFieldTrial.java
@@ -47,7 +47,6 @@
     // Cached values to avoid repeated and redundant JNI operations.
     private static Boolean sEnabled;
     private static Boolean[] sSwitches = new Boolean[ContextualSearchSwitch.NUM_ENTRIES];
-    private static Integer[] sSettings = new Integer[ContextualSearchSetting.NUM_ENTRIES];
 
     // SWITCHES
     // TODO(donnd): remove all supporting code once short-lived data collection is done.
@@ -169,7 +168,10 @@
         int IS_PAGE_CONTENT_NOTIFICATION_DISABLED = 16;
         /** Whether logging for Machine Learning is disabled. */
         int IS_UKM_RANKER_LOGGING_DISABLED = 17;
-        /** Whether or not ML-based Tap suppression is enabled. */
+        /**
+         * @deprecated
+         * Whether or not ML-based Tap suppression is enabled.
+         */
         int IS_CONTEXTUAL_SEARCH_ML_TAP_SUPPRESSION_ENABLED = 18;
         /**
          * @deprecated
@@ -216,69 +218,8 @@
             "disable_send_url" // IS_SEND_BASE_PAGE_URL_DISABLED
     };
 
-    @IntDef({ContextualSearchSetting.MANDATORY_PROMO_LIMIT,
-            ContextualSearchSetting.SCREEN_TOP_SUPPRESSION_DPS,
-            ContextualSearchSetting.MINIMUM_SELECTION_LENGTH,
-            ContextualSearchSetting.WAIT_AFTER_TAP_DELAY_MS,
-            ContextualSearchSetting.TAP_DURATION_THRESHOLD_MS,
-            ContextualSearchSetting.RECENT_SCROLL_DURATION_MS})
-    @Retention(RetentionPolicy.SOURCE)
-    /**
-     * These are integer Setting values that are backed by a Variation Param.
-     * Values are used for indexing ContextualSearchSwitchStrings - should start from 0 and can't
-     * have gaps.
-     */
-    @interface ContextualSearchSetting {
-        /**
-         * @deprecated
-         * The number of times the Promo should be seen before it becomes mandatory.
-         */
-        int MANDATORY_PROMO_LIMIT = 0;
-        /**
-         * @deprecated
-         * A Y value limit that will suppress a Tap near the top of the screen.
-         * (any Y value less than the limit will suppress the Tap trigger).
-         */
-        int SCREEN_TOP_SUPPRESSION_DPS = 1;
-        /** The minimum valid selection length. */
-        int MINIMUM_SELECTION_LENGTH = 2;
-        /**
-         * An amount to delay after a Tap gesture is recognized, in case some user gesture
-         * immediately follows that would prevent the UI from showing.
-         * The classic example is a scroll, which might be a signal that the previous tap was
-         * accidental.
-         */
-        int WAIT_AFTER_TAP_DELAY_MS = 3;
-        /**
-         * @deprecated
-         * A threshold for the duration of a tap gesture for categorization as brief or
-         * lengthy (the maximum amount of time in milliseconds for a tap gesture that's still
-         * considered a very brief duration tap).
-         */
-        int TAP_DURATION_THRESHOLD_MS = 4;
-        /**
-         * The duration to use for suppressing Taps after a recent scroll, or {@code 0} if no
-         * suppression is configured (the period of time after a scroll when tap triggering is
-         * suppressed).
-         */
-        int RECENT_SCROLL_DURATION_MS = 5;
-
-        int NUM_ENTRIES = 6;
-    }
-
-    // Indexed by ContextualSearchSetting
-    private static final String[] ContextualSearchSettingNames = {
-            "mandatory_promo_limit", // MANDATORY_PROMO_LIMIT
-            "screen_top_suppression_dps", // SCREEN_TOP_SUPPRESSION_DPS
-            "minimum_selection_length", // MINIMUM_SELECTION_LENGTH
-            "wait_after_tap_delay_ms", // WAIT_AFTER_TAP_DELAY_MS
-            "tap_duration_threshold_ms", // TAP_DURATION_THRESHOLD_MS
-            "recent_scroll_duration_ms" // RECENT_SCROLL_DURATION_MS
-    };
-
     private ContextualSearchFieldTrial() {
         assert ContextualSearchSwitchNames.length == ContextualSearchSwitch.NUM_ENTRIES;
-        assert ContextualSearchSettingNames.length == ContextualSearchSetting.NUM_ENTRIES;
     }
 
     /**
@@ -307,13 +248,6 @@
         return sSwitches[value].booleanValue();
     }
 
-    static int getValue(@ContextualSearchSetting int value) {
-        if (sSettings[value] == null) {
-            sSettings[value] = getIntParamValueOrDefault(ContextualSearchSettingNames[value], 0);
-        }
-        return sSettings[value].intValue();
-    }
-
     /**
      * Gets the "stamp" parameter from the RelatedSearches FieldTrial feature.
      * @return The stamp parameter from the feature. If no stamp param is present then an empty
@@ -416,29 +350,4 @@
         return TextUtils.equals(ENABLED_VALUE,
                 VariationsAssociatedData.getVariationParamValue(FIELD_TRIAL_NAME, paramName));
     }
-
-    /**
-     * Returns an integer value for a Finch parameter, or the default value if no parameter
-     * exists in the current configuration.  Also checks for a command-line switch with the same
-     * name.
-     * @param paramName The name of the Finch parameter (or command-line switch) to get a value
-     *                  for.
-     * @param defaultValue The default value to return when there's no param or switch.
-     * @return An integer value -- either the param or the default.
-     */
-    private static int getIntParamValueOrDefault(String paramName, int defaultValue) {
-        String value = CommandLine.getInstance().getSwitchValue(paramName);
-        if (TextUtils.isEmpty(value)) {
-            value = VariationsAssociatedData.getVariationParamValue(FIELD_TRIAL_NAME, paramName);
-        }
-        if (!TextUtils.isEmpty(value)) {
-            try {
-                return Integer.parseInt(value);
-            } catch (NumberFormatException e) {
-                return defaultValue;
-            }
-        }
-
-        return defaultValue;
-    }
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchManager.java b/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchManager.java
index caec0d8..161c8fd 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchManager.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchManager.java
@@ -22,7 +22,6 @@
 import org.chromium.base.Callback;
 import org.chromium.base.ObserverList;
 import org.chromium.base.SysUtils;
-import org.chromium.base.TimeUtils;
 import org.chromium.base.annotations.CalledByNative;
 import org.chromium.base.annotations.NativeMethods;
 import org.chromium.base.supplier.Supplier;
@@ -37,7 +36,6 @@
 import org.chromium.chrome.browser.compositor.bottombar.contextualsearch.ContextualSearchPanelCoordinator;
 import org.chromium.chrome.browser.compositor.bottombar.contextualsearch.ContextualSearchPanelInterface;
 import org.chromium.chrome.browser.compositor.layouts.LayoutManagerImpl;
-import org.chromium.chrome.browser.contextualsearch.ContextualSearchFieldTrial.ContextualSearchSetting;
 import org.chromium.chrome.browser.contextualsearch.ContextualSearchInternalStateController.InternalState;
 import org.chromium.chrome.browser.contextualsearch.ContextualSearchSelectionController.SelectionType;
 import org.chromium.chrome.browser.contextualsearch.ResolvedSearchTerm.CardTag;
@@ -1517,28 +1515,7 @@
     public void handleNonSuppressedTap(long tapTimeNanoseconds) {
         if (isSuppressed()) return;
 
-        // If there's a wait-after-tap experiment then we may want to delay a bit longer for
-        // the user to take an action like scrolling that will reset our internal state.
-        long delayBeforeFinishingWorkMs = 0;
-        if (ContextualSearchFieldTrial.getValue(ContextualSearchSetting.WAIT_AFTER_TAP_DELAY_MS) > 0
-                && tapTimeNanoseconds > 0) {
-            delayBeforeFinishingWorkMs = ContextualSearchFieldTrial.getValue(
-                                                 ContextualSearchSetting.WAIT_AFTER_TAP_DELAY_MS)
-                    - (System.nanoTime() - tapTimeNanoseconds)
-                            / TimeUtils.NANOSECONDS_PER_MILLISECOND;
-        }
-
-        // Finish work on the current state, either immediately or with a delay.
-        if (delayBeforeFinishingWorkMs <= 0) {
-            finishSuppressionDecision();
-        } else {
-            new Handler().postDelayed(new Runnable() {
-                @Override
-                public void run() {
-                    finishSuppressionDecision();
-                }
-            }, delayBeforeFinishingWorkMs);
-        }
+        finishSuppressionDecision();
     }
 
     /**
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchSelectionController.java b/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchSelectionController.java
index 4dd881b4..3a68a3bc 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchSelectionController.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchSelectionController.java
@@ -14,8 +14,6 @@
 import org.chromium.base.Log;
 import org.chromium.base.supplier.Supplier;
 import org.chromium.chrome.browser.compositor.bottombar.OverlayPanel;
-import org.chromium.chrome.browser.contextualsearch.ContextualSearchFieldTrial.ContextualSearchSetting;
-import org.chromium.chrome.browser.contextualsearch.ContextualSearchFieldTrial.ContextualSearchSwitch;
 import org.chromium.chrome.browser.tab.Tab;
 import org.chromium.content_public.browser.GestureStateListener;
 import org.chromium.content_public.browser.SelectionPopupController;
@@ -161,10 +159,6 @@
         mTabSupplier = tabSupplier;
         mPxToDp = 1.f / mActivity.getResources().getDisplayMetrics().density;
         mContainsWordPattern = Pattern.compile(CONTAINS_WORD_PATTERN);
-        // TODO(donnd): remove when behind-the-flag bug fixed (crbug.com/786589).
-        Log.i(TAG, "Tap suppression enabled: %s",
-                ContextualSearchFieldTrial.getSwitch(
-                        ContextualSearchSwitch.IS_CONTEXTUAL_SEARCH_ML_TAP_SUPPRESSION_ENABLED));
     }
 
     /**
@@ -533,27 +527,6 @@
      * @return Whether the selection is valid.
      */
     private boolean validateSelectionSuppression(String selection) {
-        boolean isValid = isValidSelection(selection);
-
-        if (mSelectionType == SelectionType.TAP) {
-            int minSelectionLength = ContextualSearchFieldTrial.getValue(
-                    ContextualSearchSetting.MINIMUM_SELECTION_LENGTH);
-            if (selection.length() < minSelectionLength) {
-                isValid = false;
-                ContextualSearchUma.logSelectionLengthSuppression(true);
-            } else if (minSelectionLength > 0) {
-                ContextualSearchUma.logSelectionLengthSuppression(false);
-            }
-        }
-
-        return isValid;
-    }
-
-    /** Determines if the given selection is valid or not.
-     * @param selection The selection portion of the context.
-     * @return whether the given selection is considered a valid target for a search.
-     */
-    private boolean isValidSelection(String selection) {
         return isValidSelection(selection, getSelectionPopupController());
     }
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchUma.java b/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchUma.java
index edfccf3..73fc0c3 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchUma.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchUma.java
@@ -461,15 +461,6 @@
     }
 
     /**
-     * Log whether the UX was suppressed due to the selection length.
-     * @param wasSuppressed Whether showing the UX was suppressed due to selection length.
-     */
-    public static void logSelectionLengthSuppression(boolean wasSuppressed) {
-        RecordHistogram.recordBooleanHistogram(
-                "Search.ContextualSearchSelectionLengthSuppression", wasSuppressed);
-    }
-
-    /**
      * Logs whether results were seen and whether any tap suppression heuristics were satisfied.
      * @param wasSearchContentViewSeen If the panel was opened.
      * @param wasAnySuppressionHeuristicSatisfied Whether any of the implemented suppression
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/init/ChromeBrowserInitializer.java b/chrome/android/java/src/org/chromium/chrome/browser/init/ChromeBrowserInitializer.java
index 2ca0e6b..5190161 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/init/ChromeBrowserInitializer.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/init/ChromeBrowserInitializer.java
@@ -34,6 +34,8 @@
 import org.chromium.chrome.browser.app.flags.ChromeCachedFlags;
 import org.chromium.chrome.browser.crash.LogcatExtractionRunnable;
 import org.chromium.chrome.browser.download.DownloadManagerService;
+import org.chromium.chrome.browser.flags.CachedFeatureFlags;
+import org.chromium.chrome.browser.flags.ChromeFeatureList;
 import org.chromium.chrome.browser.flags.ChromeSwitches;
 import org.chromium.chrome.browser.language.GlobalAppLocaleController;
 import org.chromium.chrome.browser.metrics.UmaUtils;
@@ -44,6 +46,7 @@
 import org.chromium.components.minidump_uploader.CrashFileManager;
 import org.chromium.components.module_installer.util.ModuleUtil;
 import org.chromium.components.policy.CombinedPolicyProvider;
+import org.chromium.components.safe_browsing.SafeBrowsingApiBridge;
 import org.chromium.content_public.browser.BrowserStartupController;
 import org.chromium.content_public.browser.DeviceUtils;
 import org.chromium.content_public.browser.SpeechRecognition;
@@ -213,6 +216,10 @@
         ThreadUtils.assertOnUiThread();
         if (mPreInflationStartupComplete) return;
 
+        if (CachedFeatureFlags.isEnabled(ChromeFeatureList.CREATE_SAFEBROWSING_ON_STARTUP)) {
+            new Thread(SafeBrowsingApiBridge::ensureCreated).start();
+        }
+
         // Ensure critical files are available, so they aren't blocked on the file-system
         // behind long-running accesses in next phase.
         // Don't do any large file access here!
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/sharing/shared_clipboard/SharedClipboardShareActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/sharing/shared_clipboard/SharedClipboardShareActivity.java
index 545f55d..dc272b6 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/sharing/shared_clipboard/SharedClipboardShareActivity.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/sharing/shared_clipboard/SharedClipboardShareActivity.java
@@ -20,7 +20,6 @@
 import org.chromium.base.task.PostTask;
 import org.chromium.base.task.TaskTraits;
 import org.chromium.chrome.R;
-import org.chromium.chrome.browser.flags.ChromeFeatureList;
 import org.chromium.chrome.browser.init.AsyncInitializationActivity;
 import org.chromium.chrome.browser.settings.SettingsLauncherImpl;
 import org.chromium.chrome.browser.sharing.SharingAdapter;
@@ -43,7 +42,8 @@
      * the SharedClipboardShareActivity appropriately. This call requires native to be loaded.
      */
     public static void updateComponentEnabledState() {
-        boolean enabled = ChromeFeatureList.isEnabled(ChromeFeatureList.SHARED_CLIPBOARD_UI);
+        // TODO(https://crbug.com/1311675): Remove this.
+        boolean enabled = false;
         PostTask.postTask(TaskTraits.USER_VISIBLE, () -> setComponentEnabled(enabled));
     }
 
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/ImageFetcherIntegrationTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/ImageFetcherIntegrationTest.java
new file mode 100644
index 0000000..de54a14c
--- /dev/null
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/ImageFetcherIntegrationTest.java
@@ -0,0 +1,102 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.chrome.browser;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+
+import android.graphics.Bitmap;
+import android.graphics.Color;
+
+import androidx.test.filters.MediumTest;
+
+import org.junit.ClassRule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import org.chromium.base.Callback;
+import org.chromium.base.test.util.Batch;
+import org.chromium.base.test.util.CallbackHelper;
+import org.chromium.base.test.util.CommandLineFlags;
+import org.chromium.chrome.browser.flags.ChromeSwitches;
+import org.chromium.chrome.browser.profiles.Profile;
+import org.chromium.chrome.test.ChromeBrowserTestRule;
+import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
+import org.chromium.components.image_fetcher.ImageFetcher;
+import org.chromium.components.image_fetcher.ImageFetcherConfig;
+import org.chromium.components.image_fetcher.ImageFetcherFactory;
+import org.chromium.content_public.browser.test.util.TestThreadUtils;
+import org.chromium.net.test.EmbeddedTestServerRule;
+
+import java.util.concurrent.Callable;
+import java.util.concurrent.TimeoutException;
+
+/**
+ * Tests for {@link ImageFetcher}.
+ */
+@RunWith(ChromeJUnit4ClassRunner.class)
+@CommandLineFlags.Add({ChromeSwitches.DISABLE_FIRST_RUN_EXPERIENCE})
+@Batch(Batch.UNIT_TESTS)
+public class ImageFetcherIntegrationTest {
+    @ClassRule
+    public static final ChromeBrowserTestRule sRule = new ChromeBrowserTestRule();
+
+    @ClassRule
+    public static final EmbeddedTestServerRule sTestServerRule = new EmbeddedTestServerRule();
+
+    private class TestImageFetcherCallback extends CallbackHelper implements Callback<Bitmap> {
+        public Bitmap mBitmap;
+
+        @Override
+        public void onResult(Bitmap bitmap) {
+            mBitmap = bitmap;
+            notifyCalled();
+        }
+    }
+
+    /**
+     * Fetches image from ImageFetcher and waits for callback.
+     */
+    private static void fetchImageAndWait(
+            String imageUrl, int size, TestImageFetcherCallback callbackWaiter) throws Exception {
+        TestThreadUtils.runOnUiThreadBlocking(new Callable<Void>() {
+            @Override
+            public Void call() throws TimeoutException {
+                ImageFetcher imageFetcher =
+                        ImageFetcherFactory.createImageFetcher(ImageFetcherConfig.NETWORK_ONLY,
+                                Profile.getLastUsedRegularProfile().getProfileKey());
+                ImageFetcher.Params params =
+                        ImageFetcher.Params.create(imageUrl, "random", size, size);
+                imageFetcher.fetchImage(params, callbackWaiter);
+                return null;
+            }
+        });
+        callbackWaiter.waitForFirst();
+    }
+
+    /**
+     * Test that ImageFetcher#fetchImage() selects the .ico frame based on the passed-in desired
+     * downloads size.
+     */
+    @Test
+    @MediumTest
+    public void testDesiredFrameSizeFavicon() throws Exception {
+        String icoUrl = sTestServerRule.getServer().getURL(
+                "/chrome/test/data/android/image_fetcher/icon.ico");
+        {
+            TestImageFetcherCallback imageFetcherCallback = new TestImageFetcherCallback();
+            fetchImageAndWait(icoUrl, 60, imageFetcherCallback);
+            assertNotNull(imageFetcherCallback.mBitmap);
+            assertEquals(Color.RED, imageFetcherCallback.mBitmap.getPixel(0, 0));
+        }
+
+        {
+            TestImageFetcherCallback imageFetcherCallback = new TestImageFetcherCallback();
+            fetchImageAndWait(icoUrl, 120, imageFetcherCallback);
+            assertNotNull(imageFetcherCallback.mBitmap);
+            assertEquals(Color.GREEN, imageFetcherCallback.mBitmap.getPixel(0, 0));
+        }
+    }
+}
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/SafeBrowsingTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/SafeBrowsingTest.java
index 3cc7d8b..1ad07b2 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/SafeBrowsingTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/SafeBrowsingTest.java
@@ -80,10 +80,7 @@
 
     @Before
     public void setUp() {
-        // Create a new temporary instance to ensure the Class is loaded. Otherwise we will get a
-        // ClassNotFoundException when trying to instantiate during startup.
-        SafeBrowsingApiBridge.setSafeBrowsingHandlerType(
-                new MockSafeBrowsingApiHandler().getClass());
+        SafeBrowsingApiBridge.setSafeBrowsingHandlerType(MockSafeBrowsingApiHandler.class);
     }
 
     @After
@@ -116,4 +113,17 @@
         loadUrlNonBlocking(url);
         waitForInterstitial(true);
     }
+
+    @Test
+    @MediumTest
+    @Features.EnableFeatures(ChromeFeatureList.CREATE_SAFEBROWSING_ON_STARTUP)
+    public void interstitialPageWithEarlyInit() throws Exception {
+        mTestServer = EmbeddedTestServer.createAndStartServer(InstrumentationRegistry.getContext());
+        String url = mTestServer.getURL("/chrome/test/data/android/about.html");
+        MockSafeBrowsingApiHandler.addMockResponse(url, "{\"matches\":[{\"threat_type\":\"5\"}]}");
+        mActivityTestRule.startMainActivityOnBlankPage();
+
+        loadUrlNonBlocking(url);
+        waitForInterstitial(true);
+    }
 }
diff --git a/chrome/app/generated_resources.grd b/chrome/app/generated_resources.grd
index 9040867..2a45eed 100644
--- a/chrome/app/generated_resources.grd
+++ b/chrome/app/generated_resources.grd
@@ -5302,54 +5302,55 @@
       <!-- End Extension Settings Overridden Dialog strings. -->
       <!-- Force Installed Deprecated Apps Deletion Dialog strings. -->
       <message name="IDS_FORCE_INSTALLED_DEPRECATED_APPS_CONTENT" desc="Content of the force installed deprecated app dialog">
-        Old versions of Chrome Apps won't open after December 2022. Contact your administrator to update to a new version or remove this app.
+        Your administrator installed "<ph name="EXTENSION_NAME">$1<ex>Google Docs</ex></ph>" but this Chrome App is no longer supported. Contact your administrator to remove it.
+      </message>
+      <message name="IDS_FORCE_INSTALLED_PREINSTALLED_DEPRECATED_APPS_TITLE" desc="Title of the force installed and pre-installed deprecated app dialog">
+        <ph name="EXTENSION_NAME">$1<ex>Google Docs</ex></ph> needs to be updated
+      </message>
+      <message name="IDS_FORCE_INSTALLED_PREINSTALLED_DEPRECATED_APPS_CONTENT" desc="Content of the force installed and pre-installed deprecated app dialog">
+        Contact your administrator for the newest version.
+      </message>
+      <message name="IDS_FORCE_INSTALLED_DEPRECATED_APPS_LEARN_MORE_AX_LABEL" desc="Accessibility label text for IDS_DEPRECATED_APPS_LEARN_MORE link">
+        Learn more about unsupported Chrome Apps
       </message>
       <!-- End Force Installed Deprecated Apps Deletion Dialog strings. -->
       <!-- Deprecated Apps Deletion Dialog strings. -->
-      <message name="IDS_DEPRECATED_APPS_RENDERER_TITLE_PLURAL" desc="The title of the deprecated app dialog">
-        <ph name="APPS">$1<ex>2</ex></ph> apps are no longer supported
+      <message name="IDS_DEPRECATED_APPS_RENDERER_TITLE" desc="The title of the deprecated app dialog">
+        {NUM_APPS, plural,
+        =1 {Unsupported App}
+        other {Unsupported Apps}}
       </message>
-      <message name="IDS_DEPRECATED_APPS_RENDERER_TITLE_WITH_APP_NAME" desc="The title of the deprecated app dialog with the app name">
-        "<ph name="EXTENSION_NAME">$1<ex>Google Docs</ex></ph>" is no longer supported
-      </message>
-      <message name="IDS_DEPRECATED_APPS_MONITOR_RENDERER" desc="Dialog content that educates users that Chrome Apps will soon no longer launch.">
-        Old versions of Chrome apps won't open after December 2022. You can check if there's a new version available.
+      <message name="IDS_DEPRECATED_APPS_MONITOR_RENDERER" desc="States how many deprecated apps are present, with a link to a help article">
+        {NUM_APPS, plural,
+        =1 {1 of your apps is no longer supported.}
+        other {# of your apps are no longer supported.}}
       </message>
       <message name="IDS_DEPRECATED_APPS_LEARN_MORE" desc="Redirects to a link with more information on chrome apps deprecation">
         Learn more
       </message>
-      <message name="IDS_DEPRECATED_APPS_LEARN_MORE_AX_LABEL" desc="Accessibility label text for IDS_DEPRECATED_APPS_LEARN_MORE link">
-        Learn more about unsupported Chrome apps
-      </message>
       <message name="IDS_DEPRECATED_APPS_DELETION_LINK" desc="Contains link to trigger the deprecated apps deletion dialog from chrome://apps">
         {NUM_APPS, plural,
-        =1 {Remove 1 unsupported app}
-        other {Remove # unsupported apps}}
+        =1 {Delete 1 unsupported app}
+        other {Delete # unsupported apps}}
       </message>
       <if expr="use_titlecase">
-        <message name="IDS_DEPRECATED_APPS_OK_LABEL" desc="Label for OK button to delete deprecated apps on deprecated apps dialog">
+        <message name="IDS_DEPRECATED_APPS_OK_LABEL" desc="Label for OK button on deprecated apps dialog">
           {NUM_APPS, plural,
-          =1 {Remove App}
-          other {Remove Apps}}
+          =1 {Delete App Now}
+          other {Delete Apps Now}}
         </message>
         <message name="IDS_DEPRECATED_APPS_CANCEL_LABEL" desc="Label for Cancel button on deprecated apps dialog">
-          Cancel
-        </message>
-        <message name="IDS_DEPRECATED_APPS_LAUNCH_ANYWAY_LABEL" desc="Label for button cancel button to launch the app anyways in the deprecated apps dialog">
-          Open Anyway
+          Ask Next Time
         </message>
       </if>
         <if expr="not use_titlecase">
-        <message name="IDS_DEPRECATED_APPS_OK_LABEL" desc="Label for OK button to delete deprecated apps on deprecated apps dialog">
+        <message name="IDS_DEPRECATED_APPS_OK_LABEL" desc="Label for OK button on deprecated apps dialog">
           {NUM_APPS, plural,
-          =1 {Remove app}
-          other {Remove apps}}
+          =1 {Delete app now}
+          other {Delete apps now}}
         </message>
         <message name="IDS_DEPRECATED_APPS_CANCEL_LABEL" desc="Label for Cancel button on deprecated apps dialog">
-          Cancel
-        </message>
-        <message name="IDS_DEPRECATED_APPS_LAUNCH_ANYWAY_LABEL" desc="Label for button cancel button to launch the app anyways in the deprecated apps dialog">
-          Open anyway
+          Ask next time
         </message>
       </if>
       <!-- End Deprecated Apps Deletion Dialog strings. -->
diff --git a/chrome/app/generated_resources_grd/IDS_DEPRECATED_APPS_CANCEL_LABEL.png.sha1 b/chrome/app/generated_resources_grd/IDS_DEPRECATED_APPS_CANCEL_LABEL.png.sha1
index 26ea844..e20a484 100644
--- a/chrome/app/generated_resources_grd/IDS_DEPRECATED_APPS_CANCEL_LABEL.png.sha1
+++ b/chrome/app/generated_resources_grd/IDS_DEPRECATED_APPS_CANCEL_LABEL.png.sha1
@@ -1 +1 @@
-1e123c34e823e2462febf9190afc628a2ce8e57c
\ No newline at end of file
+d9b944ef5ef27e0f1295241c9f406b9c8cb72aee
\ No newline at end of file
diff --git a/chrome/app/generated_resources_grd/IDS_DEPRECATED_APPS_DELETION_LINK.png.sha1 b/chrome/app/generated_resources_grd/IDS_DEPRECATED_APPS_DELETION_LINK.png.sha1
index af82f71..b9455dd3 100644
--- a/chrome/app/generated_resources_grd/IDS_DEPRECATED_APPS_DELETION_LINK.png.sha1
+++ b/chrome/app/generated_resources_grd/IDS_DEPRECATED_APPS_DELETION_LINK.png.sha1
@@ -1 +1 @@
-7a07c53c45948b89a41926c6db34d2789d145eee
\ No newline at end of file
+6ff2631811c45b9f3b84abc7c67b5c5afcd547e5
\ No newline at end of file
diff --git a/chrome/app/generated_resources_grd/IDS_DEPRECATED_APPS_LAUNCH_ANYWAY_LABEL.png.sha1 b/chrome/app/generated_resources_grd/IDS_DEPRECATED_APPS_LAUNCH_ANYWAY_LABEL.png.sha1
deleted file mode 100644
index ff28de09..0000000
--- a/chrome/app/generated_resources_grd/IDS_DEPRECATED_APPS_LAUNCH_ANYWAY_LABEL.png.sha1
+++ /dev/null
@@ -1 +0,0 @@
-cba71d69dd28b563b84d8f711f75b512dd087540
\ No newline at end of file
diff --git a/chrome/app/generated_resources_grd/IDS_DEPRECATED_APPS_LEARN_MORE_AX_LABEL.png.sha1 b/chrome/app/generated_resources_grd/IDS_DEPRECATED_APPS_LEARN_MORE_AX_LABEL.png.sha1
deleted file mode 100644
index 18ae197..0000000
--- a/chrome/app/generated_resources_grd/IDS_DEPRECATED_APPS_LEARN_MORE_AX_LABEL.png.sha1
+++ /dev/null
@@ -1 +0,0 @@
-b76eea7f0c5ea4eb7e989849d710a4ebd29730f3
\ No newline at end of file
diff --git a/chrome/app/generated_resources_grd/IDS_DEPRECATED_APPS_MONITOR_RENDERER.png.sha1 b/chrome/app/generated_resources_grd/IDS_DEPRECATED_APPS_MONITOR_RENDERER.png.sha1
index ae4936a95..1b2ad4ec 100644
--- a/chrome/app/generated_resources_grd/IDS_DEPRECATED_APPS_MONITOR_RENDERER.png.sha1
+++ b/chrome/app/generated_resources_grd/IDS_DEPRECATED_APPS_MONITOR_RENDERER.png.sha1
@@ -1 +1 @@
-d5ff0b22ce14fcea1fc49a2c7de9b15f730c131a
\ No newline at end of file
+9e28ad20100395c69d134fa5af69a1b20a557d8d
\ No newline at end of file
diff --git a/chrome/app/generated_resources_grd/IDS_DEPRECATED_APPS_OK_LABEL.png.sha1 b/chrome/app/generated_resources_grd/IDS_DEPRECATED_APPS_OK_LABEL.png.sha1
index bd48885..e20a484 100644
--- a/chrome/app/generated_resources_grd/IDS_DEPRECATED_APPS_OK_LABEL.png.sha1
+++ b/chrome/app/generated_resources_grd/IDS_DEPRECATED_APPS_OK_LABEL.png.sha1
@@ -1 +1 @@
-5237e4a51724524f17f62063829e5f73555821f3
\ No newline at end of file
+d9b944ef5ef27e0f1295241c9f406b9c8cb72aee
\ No newline at end of file
diff --git a/chrome/app/generated_resources_grd/IDS_DEPRECATED_APPS_RENDERER_TITLE.png.sha1 b/chrome/app/generated_resources_grd/IDS_DEPRECATED_APPS_RENDERER_TITLE.png.sha1
new file mode 100644
index 0000000..e20a484
--- /dev/null
+++ b/chrome/app/generated_resources_grd/IDS_DEPRECATED_APPS_RENDERER_TITLE.png.sha1
@@ -0,0 +1 @@
+d9b944ef5ef27e0f1295241c9f406b9c8cb72aee
\ No newline at end of file
diff --git a/chrome/app/generated_resources_grd/IDS_DEPRECATED_APPS_RENDERER_TITLE_PLURAL.png.sha1 b/chrome/app/generated_resources_grd/IDS_DEPRECATED_APPS_RENDERER_TITLE_PLURAL.png.sha1
deleted file mode 100644
index c48a3de..0000000
--- a/chrome/app/generated_resources_grd/IDS_DEPRECATED_APPS_RENDERER_TITLE_PLURAL.png.sha1
+++ /dev/null
@@ -1 +0,0 @@
-0489408824bfd6e642d234e9d96445ad9d4a2299
\ No newline at end of file
diff --git a/chrome/app/generated_resources_grd/IDS_DEPRECATED_APPS_RENDERER_TITLE_WITH_APP_NAME.png.sha1 b/chrome/app/generated_resources_grd/IDS_DEPRECATED_APPS_RENDERER_TITLE_WITH_APP_NAME.png.sha1
deleted file mode 100644
index 227f05d5..0000000
--- a/chrome/app/generated_resources_grd/IDS_DEPRECATED_APPS_RENDERER_TITLE_WITH_APP_NAME.png.sha1
+++ /dev/null
@@ -1 +0,0 @@
-8a964ff06f8f3ce67a0e5e13029a4316e17731a8
\ No newline at end of file
diff --git a/chrome/app/generated_resources_grd/IDS_FORCE_INSTALLED_DEPRECATED_APPS_CONTENT.png.sha1 b/chrome/app/generated_resources_grd/IDS_FORCE_INSTALLED_DEPRECATED_APPS_CONTENT.png.sha1
index 10790c7b..204e0f04 100644
--- a/chrome/app/generated_resources_grd/IDS_FORCE_INSTALLED_DEPRECATED_APPS_CONTENT.png.sha1
+++ b/chrome/app/generated_resources_grd/IDS_FORCE_INSTALLED_DEPRECATED_APPS_CONTENT.png.sha1
@@ -1 +1 @@
-0beafb213eafd8a58fd41a4bbd830a216cc85cbe
\ No newline at end of file
+69621246a4875aff9ab7a1d0d9528316415fe560
\ No newline at end of file
diff --git a/chrome/app/generated_resources_grd/IDS_FORCE_INSTALLED_DEPRECATED_APPS_LEARN_MORE_AX_LABEL.png.sha1 b/chrome/app/generated_resources_grd/IDS_FORCE_INSTALLED_DEPRECATED_APPS_LEARN_MORE_AX_LABEL.png.sha1
new file mode 100644
index 0000000..8ec600b
--- /dev/null
+++ b/chrome/app/generated_resources_grd/IDS_FORCE_INSTALLED_DEPRECATED_APPS_LEARN_MORE_AX_LABEL.png.sha1
@@ -0,0 +1 @@
+767a804ffadd4122174e17e900980fc7eb77f42b
\ No newline at end of file
diff --git a/chrome/app/generated_resources_grd/IDS_FORCE_INSTALLED_PREINSTALLED_DEPRECATED_APPS_CONTENT.png.sha1 b/chrome/app/generated_resources_grd/IDS_FORCE_INSTALLED_PREINSTALLED_DEPRECATED_APPS_CONTENT.png.sha1
new file mode 100644
index 0000000..34a2322
--- /dev/null
+++ b/chrome/app/generated_resources_grd/IDS_FORCE_INSTALLED_PREINSTALLED_DEPRECATED_APPS_CONTENT.png.sha1
@@ -0,0 +1 @@
+cc0483851ef3f43bde9de8ffaae445e13f21402d
\ No newline at end of file
diff --git a/chrome/app/generated_resources_grd/IDS_FORCE_INSTALLED_PREINSTALLED_DEPRECATED_APPS_TITLE.png.sha1 b/chrome/app/generated_resources_grd/IDS_FORCE_INSTALLED_PREINSTALLED_DEPRECATED_APPS_TITLE.png.sha1
new file mode 100644
index 0000000..34a2322
--- /dev/null
+++ b/chrome/app/generated_resources_grd/IDS_FORCE_INSTALLED_PREINSTALLED_DEPRECATED_APPS_TITLE.png.sha1
@@ -0,0 +1 @@
+cc0483851ef3f43bde9de8ffaae445e13f21402d
\ No newline at end of file
diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn
index 9cc4fee..d023ce9c 100644
--- a/chrome/browser/BUILD.gn
+++ b/chrome/browser/BUILD.gn
@@ -1568,8 +1568,6 @@
     "sharing/features.h",
     "sharing/ping_message_handler.cc",
     "sharing/ping_message_handler.h",
-    "sharing/shared_clipboard/feature_flags.cc",
-    "sharing/shared_clipboard/feature_flags.h",
     "sharing/shared_clipboard/remote_copy_handle_message_result.h",
     "sharing/shared_clipboard/shared_clipboard_message_handler.cc",
     "sharing/shared_clipboard/shared_clipboard_message_handler.h",
@@ -3176,8 +3174,8 @@
       "reputation/safety_tip_infobar.h",
       "reputation/safety_tip_infobar_delegate.cc",
       "reputation/safety_tip_infobar_delegate.h",
-      "reputation/safety_tip_message_delegate.cc",
-      "reputation/safety_tip_message_delegate.h",
+      "reputation/safety_tip_message_delegate_android.cc",
+      "reputation/safety_tip_message_delegate_android.h",
       "safe_browsing/android/password_reuse_controller_android.cc",
       "safe_browsing/android/password_reuse_controller_android.h",
       "safe_browsing/android/safe_browsing_referring_app_bridge_android.cc",
diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc
index b907bd0..9da30b6e 100644
--- a/chrome/browser/about_flags.cc
+++ b/chrome/browser/about_flags.cc
@@ -52,7 +52,6 @@
 #include "chrome/browser/resource_coordinator/tab_manager_features.h"
 #include "chrome/browser/share/share_features.h"
 #include "chrome/browser/sharing/features.h"
-#include "chrome/browser/sharing/shared_clipboard/feature_flags.h"
 #include "chrome/browser/sharing_hub/sharing_hub_features.h"
 #include "chrome/browser/site_isolation/about_flags.h"
 #include "chrome/browser/ui/app_list/search/search_features.h"
@@ -6195,10 +6194,6 @@
      flag_descriptions::kRestrictGamepadAccessDescription, kOsAll,
      FEATURE_VALUE_TYPE(features::kRestrictGamepadAccess)},
 
-    {"shared-clipboard-ui", flag_descriptions::kSharedClipboardUIName,
-     flag_descriptions::kSharedClipboardUIDescription, kOsAll,
-     FEATURE_VALUE_TYPE(kSharedClipboardUI)},
-
 #if !BUILDFLAG(IS_ANDROID)
     {"sharing-desktop-screenshots",
      flag_descriptions::kSharingDesktopScreenshotsName,
@@ -7118,6 +7113,10 @@
     {"shared-highlighting-amp", flag_descriptions::kSharedHighlightingAmpName,
      flag_descriptions::kSharedHighlightingAmpDescription, kOsAll,
      FEATURE_VALUE_TYPE(shared_highlighting::kSharedHighlightingAmp)},
+    {"shared-highlighting-manager",
+     flag_descriptions::kSharedHighlightingManagerName,
+     flag_descriptions::kSharedHighlightingManagerDescription, kOsAll,
+     FEATURE_VALUE_TYPE(shared_highlighting::kSharedHighlightingManager)},
     {"shared-highlighting-refined-blocklist",
      flag_descriptions::kSharedHighlightingRefinedBlocklistName,
      flag_descriptions::kSharedHighlightingRefinedBlocklistDescription, kOsAll,
@@ -9089,8 +9088,10 @@
                          const std::string& histogram_name) {
   std::set<std::string> switches;
   std::set<std::string> features;
+  std::set<std::string> variation_ids;
   FlagsStateSingleton::GetFlagsState()->GetSwitchesAndFeaturesFromFlags(
-      flags_storage, &switches, &features);
+      flags_storage, &switches, &features, &variation_ids);
+  // Don't report variation IDs since we don't have an UMA histogram for them.
   flags_ui::ReportAboutFlagsHistogram(histogram_name, switches, features);
 }
 
diff --git a/chrome/browser/apps/platform_apps/platform_app_launch.cc b/chrome/browser/apps/platform_apps/platform_app_launch.cc
index 10f4cd2..c132936 100644
--- a/chrome/browser/apps/platform_apps/platform_app_launch.cc
+++ b/chrome/browser/apps/platform_apps/platform_app_launch.cc
@@ -138,7 +138,7 @@
     url = GURL(chrome::kChromeUIAppsWithForceInstalledDeprecationDialogURL +
                app_id);
   } else {
-    url = GURL(chrome::kChromeUIAppsWithDeprecationDialogURL + app_id);
+    url = GURL(chrome::kChromeUIAppsWithDeprecationDialogURL);
   }
 
   NavigateParams params(browser, url, ui::PAGE_TRANSITION_AUTO_TOPLEVEL);
diff --git a/chrome/browser/ash/arc/instance_throttle/arc_instance_throttle.cc b/chrome/browser/ash/arc/instance_throttle/arc_instance_throttle.cc
index 71eda08..2041e24 100644
--- a/chrome/browser/ash/arc/instance_throttle/arc_instance_throttle.cc
+++ b/chrome/browser/ash/arc/instance_throttle/arc_instance_throttle.cc
@@ -383,9 +383,6 @@
 
 void ArcInstanceThrottle::NotifyCpuRestriction(
     CpuRestrictionState cpu_restriction_state) {
-  if (!base::FeatureList::IsEnabled(kEnableThrottlingNotification))
-    return;
-
   auto* power =
       ARC_GET_INSTANCE_FOR_METHOD(bridge_->power(), OnCpuRestrictionChanged);
   if (!power)
diff --git a/chrome/browser/ash/arc/instance_throttle/arc_instance_throttle_unittest.cc b/chrome/browser/ash/arc/instance_throttle/arc_instance_throttle_unittest.cc
index 212ba58..b8d331f 100644
--- a/chrome/browser/ash/arc/instance_throttle/arc_instance_throttle_unittest.cc
+++ b/chrome/browser/ash/arc/instance_throttle/arc_instance_throttle_unittest.cc
@@ -358,16 +358,15 @@
 }
 
 // Tests that power instance notification is off by default.
-TEST_F(ArcInstanceThrottleTest, TestPowerNotificationDisabled) {
-  base::test::ScopedFeatureList scoped_feature_list;
-  scoped_feature_list.InitWithFeatures({},
-                                       {arc::kEnableThrottlingNotification});
+TEST_F(ArcInstanceThrottleTest, TestPowerNotification) {
   // Set power instance and it should be automatically notified once connection
   // is made.
   CreatePowerInstance();
+  EXPECT_EQ(1, power_instance()->cpu_restriction_state_count());
   GetThrottleObserver()->SetActive(true);
+  EXPECT_EQ(2, power_instance()->cpu_restriction_state_count());
   GetThrottleObserver()->SetActive(false);
-  EXPECT_EQ(0, power_instance()->cpu_restriction_state_count());
+  EXPECT_EQ(3, power_instance()->cpu_restriction_state_count());
 }
 
 class ArcInstanceThrottleVMTest : public testing::Test {
diff --git a/chrome/browser/ash/login/screens/welcome_screen_browsertest.cc b/chrome/browser/ash/login/screens/welcome_screen_browsertest.cc
index 2efd9f05..98a5555 100644
--- a/chrome/browser/ash/login/screens/welcome_screen_browsertest.cc
+++ b/chrome/browser/ash/login/screens/welcome_screen_browsertest.cc
@@ -885,7 +885,7 @@
 // skipToLoginForTesting is called.
 IN_PROC_BROWSER_TEST_F(WelcomeScreenChromeVoxHintTest, SkipToLoginForTesting) {
   OobeScreenWaiter(WelcomeView::kScreenId).Wait();
-  test::ExecuteOobeJS("Oobe.skipToLoginForTesting()");
+  ash::WizardController::default_controller()->SkipToLoginForTesting();
   OobeScreenWaiter(GaiaView::kScreenId).Wait();
 
   EXPECT_TRUE(IdleDetectionCancelledForTesting());
diff --git a/chrome/browser/ash/remote_apps/remote_apps_impl_browsertest.cc b/chrome/browser/ash/remote_apps/remote_apps_impl_browsertest.cc
index 49333be9..60640ca4 100644
--- a/chrome/browser/ash/remote_apps/remote_apps_impl_browsertest.cc
+++ b/chrome/browser/ash/remote_apps/remote_apps_impl_browsertest.cc
@@ -135,17 +135,32 @@
     return AppListModelProvider::Get()->model()->FindItem(id);
   }
 
-  bool IsAppListItemInFront(const std::string& id) {
+  int GetAppListItemIndex(const std::string& id) {
     AppListModel* const model = AppListModelProvider::Get()->model();
     AppListItemList* const item_list = model->top_level_item_list();
 
     size_t index;
     if (!item_list->FindItemIndex(id, &index))
-      return false;
+      return -1;
+    return index;
+  }
 
+  bool IsAppListItemInFront(const std::string& id) {
+    const int index = GetAppListItemIndex(id);
+    DCHECK_GE(index, 0);
     return index == 0;
   }
 
+  bool IsAppListItemLast(const std::string& id) {
+    const int index = GetAppListItemIndex(id);
+    DCHECK_GE(index, 0);
+    const int model_size = AppListModelProvider::Get()
+                               ->model()
+                               ->top_level_item_list()
+                               ->item_count();
+    return index == model_size - 1;
+  }
+
  private:
   base::DictionaryValue config_;
   EmbeddedPolicyTestServerMixin policy_test_server_mixin_{&mixin_host_};
@@ -158,7 +173,11 @@
 
   ash::AppListItem* app = GetAppListItem(kId1);
   EXPECT_FALSE(app->is_folder());
+  EXPECT_FALSE(app->is_new_install());
   EXPECT_EQ("App 1", app->name());
+  // If `add_to_front` is not set, the item should be added to the back of the
+  // app list.
+  EXPECT_TRUE(IsAppListItemLast(kId1));
 }
 
 IN_PROC_BROWSER_TEST_F(RemoteAppsImplBrowsertest, AddAppToFront) {
@@ -181,12 +200,17 @@
   EXPECT_EQ(2u, folder->ChildItemCount());
   EXPECT_TRUE(folder->FindChildItem(kId2));
   EXPECT_TRUE(folder->FindChildItem(kId3));
+  EXPECT_FALSE(folder->is_new_install());
 
   ash::AppListItem* app1 = GetAppListItem(kId2);
   EXPECT_EQ(kId1, app1->folder_id());
+  EXPECT_FALSE(app1->is_new_install());
 
   ash::AppListItem* app2 = GetAppListItem(kId3);
   EXPECT_EQ(kId1, app2->folder_id());
+  EXPECT_FALSE(app2->is_new_install());
+
+  EXPECT_TRUE(IsAppListItemLast(kId1));
 }
 
 IN_PROC_BROWSER_TEST_F(RemoteAppsImplBrowsertest, AddFolderToFront) {
diff --git a/chrome/browser/ash/wallpaper_handlers/wallpaper_handlers.cc b/chrome/browser/ash/wallpaper_handlers/wallpaper_handlers.cc
index ea20413..86ebecb 100644
--- a/chrome/browser/ash/wallpaper_handlers/wallpaper_handlers.cc
+++ b/chrome/browser/ash/wallpaper_handlers/wallpaper_handlers.cc
@@ -602,7 +602,7 @@
 
   auto fetcher = std::make_unique<signin::PrimaryAccountAccessTokenFetcher>(
       "wallpaper_google_photos_fetcher", identity_manager_, scopes,
-      signin::PrimaryAccountAccessTokenFetcher::Mode::kImmediate,
+      signin::PrimaryAccountAccessTokenFetcher::Mode::kWaitUntilAvailable,
       signin::ConsentLevel::kSignin);
   auto* fetcher_ptr = fetcher.get();
   fetcher_ptr->Start(base::BindOnce(
diff --git a/chrome/browser/extensions/api/bookmark_manager_private/bookmark_manager_private_api.cc b/chrome/browser/extensions/api/bookmark_manager_private/bookmark_manager_private_api.cc
index 05ef87ca..d4c0c54 100644
--- a/chrome/browser/extensions/api/bookmark_manager_private/bookmark_manager_private_api.cc
+++ b/chrome/browser/extensions/api/bookmark_manager_private/bookmark_manager_private_api.cc
@@ -19,6 +19,7 @@
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/extensions/api/bookmarks/bookmark_api_constants.h"
 #include "chrome/browser/extensions/api/bookmarks/bookmark_api_helpers.h"
+#include "chrome/browser/extensions/api/tabs/windows_util.h"
 #include "chrome/browser/extensions/chrome_extension_function_details.h"
 #include "chrome/browser/extensions/extension_tab_util.h"
 #include "chrome/browser/platform_util.h"
@@ -629,9 +630,16 @@
     urls.push_back(node->url());
   }
 
+  std::string error;
+  windows_util::IncognitoResult incognito_result =
+      windows_util::ShouldOpenIncognitoWindow(calling_profile,
+                                              params->incognito, &urls, &error);
+  if (incognito_result == windows_util::IncognitoResult::kError)
+    return Error(std::move(error));
+
   DCHECK(!calling_profile->IsOffTheRecord());
   Profile* window_profile =
-      params->incognito
+      incognito_result == windows_util::IncognitoResult::kIncognito
           ? calling_profile->GetPrimaryOTRProfile(/*create_if_needed=*/true)
           : calling_profile;
 
diff --git a/chrome/browser/extensions/api/bookmark_manager_private/bookmark_manager_private_api_unittest.cc b/chrome/browser/extensions/api/bookmark_manager_private/bookmark_manager_private_api_unittest.cc
index 6741dca65..7b2616b35 100644
--- a/chrome/browser/extensions/api/bookmark_manager_private/bookmark_manager_private_api_unittest.cc
+++ b/chrome/browser/extensions/api/bookmark_manager_private/bookmark_manager_private_api_unittest.cc
@@ -58,7 +58,7 @@
   }
 
   Browser* browser() { return browser_.get(); }
-  const bookmarks::BookmarkModel* model() const { return model_; }
+  bookmarks::BookmarkModel* model() { return model_; }
   std::string node_id() const { return node_id_; }
   GURL& url() { return url_; }
 
@@ -137,4 +137,47 @@
                                                       args, profile()));
 }
 
+TEST_F(BookmarkManagerPrivateApiUnitTest,
+       RunOpenInNewWindowFunctionIncognitoDisabled) {
+  // Incognito disabled.
+  IncognitoModePrefs::SetAvailability(
+      profile()->GetPrefs(), IncognitoModePrefs::Availability::kDisabled);
+
+  auto new_tab_function =
+      base::MakeRefCounted<BookmarkManagerPrivateOpenInNewWindowFunction>();
+  std::string args = base::StringPrintf(R"([["%s"], true])", node_id().c_str());
+  EXPECT_EQ("Incognito mode is disabled.",
+            api_test_utils::RunFunctionAndReturnError(new_tab_function.get(),
+                                                      args, profile()));
+}
+
+TEST_F(BookmarkManagerPrivateApiUnitTest,
+       RunOpenInNewWindowFunctionIncognitoForced) {
+  // Incognito forced.
+  IncognitoModePrefs::SetAvailability(
+      profile()->GetPrefs(), IncognitoModePrefs::Availability::kForced);
+
+  auto new_tab_function =
+      base::MakeRefCounted<BookmarkManagerPrivateOpenInNewWindowFunction>();
+  std::string args =
+      base::StringPrintf(R"([["%s"], false])", node_id().c_str());
+  EXPECT_EQ("Incognito mode is forced. Cannot open normal windows.",
+            api_test_utils::RunFunctionAndReturnError(new_tab_function.get(),
+                                                      args, profile()));
+}
+
+TEST_F(BookmarkManagerPrivateApiUnitTest,
+       RunOpenInNewWindowFunctionIncognitoIncompatibleNode) {
+  const bookmarks::BookmarkNode* node = model()->AddURL(
+      model()->other_node(), 0, u"history", GURL("chrome://history"));
+  std::string node_id = base::NumberToString(node->id());
+
+  auto new_tab_function =
+      base::MakeRefCounted<BookmarkManagerPrivateOpenInNewWindowFunction>();
+  std::string args = base::StringPrintf(R"([["%s"], true])", node_id.c_str());
+  EXPECT_EQ("Cannot open URL \"chrome://history/\" in an incognito window.",
+            api_test_utils::RunFunctionAndReturnError(new_tab_function.get(),
+                                                      args, profile()));
+}
+
 }  // namespace extensions
diff --git a/chrome/browser/extensions/api/enterprise_reporting_private/enterprise_reporting_private_api.cc b/chrome/browser/extensions/api/enterprise_reporting_private/enterprise_reporting_private_api.cc
index aefbcf4..7caa584b 100644
--- a/chrome/browser/extensions/api/enterprise_reporting_private/enterprise_reporting_private_api.cc
+++ b/chrome/browser/extensions/api/enterprise_reporting_private/enterprise_reporting_private_api.cc
@@ -547,6 +547,11 @@
     return false;
   }
 
+  if (!record.has_timestamp_us()) {
+    // Missing record timestamp
+    return false;
+  }
+
   if (!::reporting::Priority_IsValid(params->request.priority) ||
       !::reporting::Priority_Parse(
           ::reporting::Priority_Name(params->request.priority), &priority)) {
diff --git a/chrome/browser/extensions/api/enterprise_reporting_private/enterprise_reporting_private_unittest.cc b/chrome/browser/extensions/api/enterprise_reporting_private/enterprise_reporting_private_unittest.cc
index 6e8acc6f5..5a07332 100644
--- a/chrome/browser/extensions/api/enterprise_reporting_private/enterprise_reporting_private_unittest.cc
+++ b/chrome/browser/extensions/api/enterprise_reporting_private/enterprise_reporting_private_unittest.cc
@@ -33,10 +33,15 @@
 #include "components/reporting/proto/synced/record.pb.h"
 #include "components/safe_browsing/core/common/safe_browsing_prefs.h"
 #include "components/version_info/version_info.h"
+#include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 #if BUILDFLAG(IS_CHROMEOS)
+#include "chromeos/dbus/missive/fake_missive_client.h"
 #include "chromeos/dbus/missive/missive_client.h"
+#include "components/reporting/proto/synced/record.pb.h"
+#include "components/reporting/proto/synced/record_constants.pb.h"
+#include "components/reporting/util/status.h"
 #endif
 
 #if BUILDFLAG(IS_WIN)
@@ -55,6 +60,11 @@
     ::extensions::api::enterprise_reporting_private;
 
 using SettingValue = enterprise_signals::SettingValue;
+using ::testing::_;
+using ::testing::Eq;
+using ::testing::Invoke;
+using ::testing::StrEq;
+using ::testing::WithArgs;
 
 namespace extensions {
 
@@ -1025,34 +1035,42 @@
 }
 
 #if BUILDFLAG(IS_CHROMEOS)
+class MockMissiveClient : public ::chromeos::FakeMissiveClient {
+ public:
+  MockMissiveClient() = default;
+  ~MockMissiveClient() override = default;
+
+  MockMissiveClient(const MockMissiveClient& other) = delete;
+  MockMissiveClient& operator=(const MockMissiveClient& other) = delete;
+
+  void Init() override {}
+
+  MissiveClient::TestInterface* GetTestInterface() override { return this; }
+
+  MOCK_METHOD(void,
+              EnqueueRecord,
+              (const ::reporting::Priority,
+               ::reporting::Record,
+               base::OnceCallback<void(::reporting::Status)>),
+              (override));
+};
+
 // Test for API enterprise.reportingPrivate.enqueueRecord
 class EnterpriseReportingPrivateEnqueueRecordFunctionTest
     : public ExtensionApiUnittest {
- public:
-  EnterpriseReportingPrivateEnqueueRecordFunctionTest() = default;
-
-  EnterpriseReportingPrivateEnqueueRecordFunctionTest(
-      const EnterpriseReportingPrivateEnqueueRecordFunctionTest&) = delete;
-  EnterpriseReportingPrivateEnqueueRecordFunctionTest& operator=(
-      const EnterpriseReportingPrivateEnqueueRecordFunctionTest&) = delete;
-
+ protected:
   static constexpr char kNoError[] = "";
   static constexpr char kTestDMTokenValue[] = "test_dm_token_value";
 
+  EnterpriseReportingPrivateEnqueueRecordFunctionTest() = default;
+
   void SetUp() override {
     ExtensionApiUnittest::SetUp();
-    ::chromeos::MissiveClient::InitializeFake();
+    ::chromeos::MissiveClient::InitializeFake<MockMissiveClient>();
     function_ =
         base::MakeRefCounted<EnterpriseReportingPrivateEnqueueRecordFunction>();
-    // Set up a Record to attach to an EnqueueRecordRequest
-    base::Value data{base::Value::Type::DICTIONARY};
-    data.SetKey("TEST_KEY", base::Value("TEST_VALUE"));
-    std::string serialized_data;
-    ASSERT_TRUE(base::JSONWriter::Write(data, &serialized_data));
-    reporting::Record record;
-    record.set_data(serialized_data);
-    record.set_destination(reporting::Destination::TELEMETRY_METRIC);
-    serialized_record_data_.resize(record.SerializeAsString().size());
+    const auto record = GetTestRecord();
+    serialized_record_data_.resize(record.ByteSizeLong());
     ASSERT_TRUE(record.SerializeToArray(serialized_record_data_.data(),
                                         serialized_record_data_.size()));
   }
@@ -1063,7 +1081,21 @@
     ExtensionApiUnittest::TearDown();
   }
 
- protected:
+  ::reporting::Record GetTestRecord() const {
+    base::Value data{base::Value::Type::DICTIONARY};
+    data.SetKey("TEST_KEY", base::Value("TEST_VALUE"));
+    std::string serialized_data;
+    DCHECK(base::JSONWriter::Write(data, &serialized_data));
+
+    ::reporting::Record record;
+    record.set_data(serialized_data);
+    record.set_destination(::reporting::Destination::TELEMETRY_METRIC);
+    record.set_timestamp_us(base::Time::Now().ToJavaTime() *
+                            base::Time::kMicrosecondsPerMillisecond);
+
+    return record;
+  }
+
   std::vector<uint8_t> serialized_record_data_;
   scoped_refptr<extensions::EnterpriseReportingPrivateEnqueueRecordFunction>
       function_;
@@ -1076,7 +1108,7 @@
   api::enterprise_reporting_private::EnqueueRecordRequest
       enqueue_record_request;
   enqueue_record_request.record_data = serialized_record_data_;
-  enqueue_record_request.priority = reporting::Priority::BACKGROUND_BATCH;
+  enqueue_record_request.priority = ::reporting::Priority::BACKGROUND_BATCH;
   enqueue_record_request.event_type =
       api::enterprise_reporting_private::EventType::EVENT_TYPE_USER;
 
@@ -1085,8 +1117,24 @@
       base::Value::FromUniquePtrValue(enqueue_record_request.ToValue()));
 
   // Set up DM token
-  policy::SetDMTokenForTesting(
-      policy::DMToken::CreateValidTokenForTesting(kTestDMTokenValue));
+  const auto dm_token =
+      policy::DMToken::CreateValidTokenForTesting(kTestDMTokenValue);
+  policy::SetDMTokenForTesting(dm_token);
+
+  auto* const reporting_client =
+      static_cast<MockMissiveClient*>(::chromeos::MissiveClient::Get());
+  EXPECT_CALL(*reporting_client, EnqueueRecord(_, _, _))
+      .WillOnce(WithArgs<1, 2>(
+          Invoke([&](::reporting::Record record,
+                     base::OnceCallback<void(::reporting::Status)>
+                         completion_callback) {
+            EXPECT_THAT(record.destination(),
+                        Eq(::reporting::Destination::TELEMETRY_METRIC));
+            EXPECT_THAT(record.dm_token(), StrEq(dm_token.value()));
+            EXPECT_THAT(record.data(), StrEq(GetTestRecord().data()));
+
+            std::move(completion_callback).Run(::reporting::Status::StatusOK());
+          })));
 
   extension_function_test_utils::RunFunction(function_.get(), std::move(params),
                                              browser(),
@@ -1115,6 +1163,10 @@
   policy::SetDMTokenForTesting(
       policy::DMToken::CreateValidTokenForTesting(kTestDMTokenValue));
 
+  auto* const reporting_client =
+      static_cast<MockMissiveClient*>(::chromeos::MissiveClient::Get());
+  EXPECT_CALL(*reporting_client, EnqueueRecord(_, _, _)).Times(0);
+
   extension_function_test_utils::RunFunction(function_.get(), std::move(params),
                                              browser(),
                                              extensions::api_test_utils::NONE);
@@ -1132,7 +1184,7 @@
       enqueue_record_request;
   enqueue_record_request.record_data = serialized_record_data_;
 
-  enqueue_record_request.priority = reporting::Priority::BACKGROUND_BATCH;
+  enqueue_record_request.priority = ::reporting::Priority::BACKGROUND_BATCH;
 
   enqueue_record_request.event_type =
       api::enterprise_reporting_private::EventType::EVENT_TYPE_USER;
@@ -1144,6 +1196,10 @@
   policy::SetDMTokenForTesting(
       policy::DMToken::CreateValidTokenForTesting(kTestDMTokenValue));
 
+  auto* const reporting_client =
+      static_cast<MockMissiveClient*>(::chromeos::MissiveClient::Get());
+  EXPECT_CALL(*reporting_client, EnqueueRecord(_, _, _)).Times(0);
+
   extension_function_test_utils::RunFunction(function_.get(), std::move(params),
                                              browser(),
                                              extensions::api_test_utils::NONE);
@@ -1159,7 +1215,7 @@
   api::enterprise_reporting_private::EnqueueRecordRequest
       enqueue_record_request;
   enqueue_record_request.record_data = serialized_record_data_;
-  enqueue_record_request.priority = reporting::Priority::BACKGROUND_BATCH;
+  enqueue_record_request.priority = ::reporting::Priority::BACKGROUND_BATCH;
   enqueue_record_request.event_type =
       api::enterprise_reporting_private::EventType::EVENT_TYPE_USER;
 
@@ -1170,6 +1226,10 @@
   // Set up invalid DM token
   policy::SetDMTokenForTesting(policy::DMToken::CreateInvalidTokenForTesting());
 
+  auto* const reporting_client =
+      static_cast<MockMissiveClient*>(::chromeos::MissiveClient::Get());
+  EXPECT_CALL(*reporting_client, EnqueueRecord(_, _, _)).Times(0);
+
   extension_function_test_utils::RunFunction(function_.get(), std::move(params),
                                              browser(),
                                              extensions::api_test_utils::NONE);
@@ -1178,6 +1238,45 @@
             EnterpriseReportingPrivateEnqueueRecordFunction::
                 kErrorCannotAssociateRecordWithUser);
 }
+
+TEST_F(EnterpriseReportingPrivateEnqueueRecordFunctionTest,
+       InvalidRecordWithMissingTimestampReturnsError) {
+  function_->SetProfileIsAffiliatedForTesting(true);
+  api::enterprise_reporting_private::EnqueueRecordRequest
+      enqueue_record_request;
+  // Clear timestamp from test record and set up serialized record
+  auto record = GetTestRecord();
+  record.clear_timestamp_us();
+  serialized_record_data_.clear();
+  serialized_record_data_.resize(record.ByteSizeLong());
+  ASSERT_TRUE(record.SerializeToArray(serialized_record_data_.data(),
+                                      serialized_record_data_.size()));
+  enqueue_record_request.record_data = serialized_record_data_;
+  enqueue_record_request.priority = ::reporting::Priority::BACKGROUND_BATCH;
+  enqueue_record_request.event_type =
+      api::enterprise_reporting_private::EventType::EVENT_TYPE_USER;
+
+  // TODO (b/234559917): Use base::Value::List instead
+  std::unique_ptr<base::ListValue> params = std::make_unique<base::ListValue>();
+  params->Append(
+      base::Value::FromUniquePtrValue(enqueue_record_request.ToValue()));
+
+  // Set up invalid DM token
+  policy::SetDMTokenForTesting(
+      policy::DMToken::CreateValidTokenForTesting(kTestDMTokenValue));
+
+  auto* const reporting_client =
+      static_cast<MockMissiveClient*>(::chromeos::MissiveClient::Get());
+  EXPECT_CALL(*reporting_client, EnqueueRecord(_, _, _)).Times(0);
+
+  extension_function_test_utils::RunFunction(function_.get(), std::move(params),
+                                             browser(),
+                                             extensions::api_test_utils::NONE);
+
+  EXPECT_EQ(function_->GetError(),
+            EnterpriseReportingPrivateEnqueueRecordFunction::
+                kErrorInvalidEnqueueRecordRequest);
+}
 #endif  // BUILDFLAG(IS_CHROMEOS)
 
 }  // namespace extensions
diff --git a/chrome/browser/extensions/api/favicon/favicon_util.cc b/chrome/browser/extensions/api/favicon/favicon_util.cc
index 7b10672..a55ca6b 100644
--- a/chrome/browser/extensions/api/favicon/favicon_util.cc
+++ b/chrome/browser/extensions/api/favicon/favicon_util.cc
@@ -74,8 +74,9 @@
   }
 
   // Parse url. Restrict which parameters are exposed to the Extension API.
+  // pageUrl must be present.
   chrome::ParsedFaviconPath parsed;
-  if (!ParseFaviconPath(url, &parsed)) {
+  if (!ParseFaviconPath(url, &parsed) || parsed.page_url.empty()) {
     std::move(callback).Run(nullptr);
     return;
   }
diff --git a/chrome/browser/extensions/api/tabs/tabs_api.cc b/chrome/browser/extensions/api/tabs/tabs_api.cc
index dcc75d0..24b84b76 100644
--- a/chrome/browser/extensions/api/tabs/tabs_api.cc
+++ b/chrome/browser/extensions/api/tabs/tabs_api.cc
@@ -575,55 +575,6 @@
   return RespondNow(OneArgument(base::Value(std::move(window_list))));
 }
 
-bool WindowsCreateFunction::ShouldOpenIncognitoWindow(
-    const windows::Create::Params::CreateData* create_data,
-    std::vector<GURL>* urls,
-    std::string* error) {
-  Profile* profile = Profile::FromBrowserContext(browser_context());
-  const IncognitoModePrefs::Availability incognito_availability =
-      IncognitoModePrefs::GetAvailability(profile->GetPrefs());
-  bool incognito = false;
-  if (create_data && create_data->incognito) {
-    incognito = *create_data->incognito;
-    if (incognito &&
-        incognito_availability == IncognitoModePrefs::Availability::kDisabled) {
-      *error = tabs_constants::kIncognitoModeIsDisabled;
-      return false;
-    }
-    if (!incognito &&
-        incognito_availability == IncognitoModePrefs::Availability::kForced) {
-      *error = tabs_constants::kIncognitoModeIsForced;
-      return false;
-    }
-  } else if (incognito_availability ==
-             IncognitoModePrefs::Availability::kForced) {
-    // If incognito argument is not specified explicitly, we default to
-    // incognito when forced so by policy.
-    incognito = true;
-  }
-
-  // Remove all URLs that are not allowed in an incognito session. Note that a
-  // ChromeOS guest session is not considered incognito in this case.
-  if (incognito && !profile->IsGuestSession()) {
-    std::string first_url_erased;
-    for (size_t i = 0; i < urls->size();) {
-      if (IsURLAllowedInIncognito((*urls)[i], profile)) {
-        i++;
-      } else {
-        if (first_url_erased.empty())
-          first_url_erased = (*urls)[i].spec();
-        urls->erase(urls->begin() + i);
-      }
-    }
-    if (urls->empty() && !first_url_erased.empty()) {
-      *error = ErrorUtils::FormatErrorMessage(
-          tabs_constants::kURLsNotAllowedInIncognitoError, first_url_erased);
-      return false;
-    }
-  }
-  return incognito;
-}
-
 ExtensionFunction::ResponseAction WindowsCreateFunction::Run() {
   std::unique_ptr<windows::Create::Params> params(
       windows::Create::Params::Create(args()));
@@ -659,14 +610,19 @@
 
   // Decide whether we are opening a normal window or an incognito window.
   std::string error;
-  bool open_incognito_window =
-      ShouldOpenIncognitoWindow(create_data, &urls, &error);
-  if (!error.empty())
+  Profile* calling_profile = Profile::FromBrowserContext(browser_context());
+  windows_util::IncognitoResult incognito_result =
+      windows_util::ShouldOpenIncognitoWindow(
+          calling_profile,
+          create_data && create_data->incognito
+              ? absl::optional<bool>(*create_data->incognito)
+              : absl::nullopt,
+          &urls, &error);
+  if (incognito_result == windows_util::IncognitoResult::kError)
     return RespondNow(Error(std::move(error)));
 
-  Profile* calling_profile = Profile::FromBrowserContext(browser_context());
   Profile* window_profile =
-      open_incognito_window
+      incognito_result == windows_util::IncognitoResult::kIncognito
           ? calling_profile->GetPrimaryOTRProfile(/*create_if_needed=*/true)
           : calling_profile;
 
diff --git a/chrome/browser/extensions/api/tabs/tabs_api.h b/chrome/browser/extensions/api/tabs/tabs_api.h
index 19e9404..03d1938f 100644
--- a/chrome/browser/extensions/api/tabs/tabs_api.h
+++ b/chrome/browser/extensions/api/tabs/tabs_api.h
@@ -72,17 +72,6 @@
 class WindowsCreateFunction : public ExtensionFunction {
   ~WindowsCreateFunction() override {}
   ResponseAction Run() override;
-  // Returns whether the window should be created in incognito mode.
-  // |create_data| are the options passed by the extension. It may be NULL.
-  // |urls| is the list of urls to open. If we are creating an incognito window,
-  // the function will remove these urls which may not be opened in incognito
-  // mode.  If window creation leads the browser into an erroneous state,
-  // |is_error| is set to true (also, error_ member variable is assigned
-  // the proper error message).
-  bool ShouldOpenIncognitoWindow(
-      const api::windows::Create::Params::CreateData* create_data,
-      std::vector<GURL>* urls,
-      std::string* error);
   DECLARE_EXTENSION_FUNCTION("windows.create", WINDOWS_CREATE)
 };
 class WindowsUpdateFunction : public ExtensionFunction {
diff --git a/chrome/browser/extensions/api/tabs/windows_util.cc b/chrome/browser/extensions/api/tabs/windows_util.cc
index a6cd32d..c31f5381 100644
--- a/chrome/browser/extensions/api/tabs/windows_util.cc
+++ b/chrome/browser/extensions/api/tabs/windows_util.cc
@@ -13,12 +13,15 @@
 #include "chrome/browser/extensions/extension_util.h"
 #include "chrome/browser/extensions/window_controller.h"
 #include "chrome/browser/extensions/window_controller_list.h"
+#include "chrome/browser/prefs/incognito_mode_prefs.h"
 #include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/ui/browser_navigator.h"
 #include "extensions/browser/extension_function.h"
 #include "extensions/browser/extension_function_dispatcher.h"
 #include "extensions/common/constants.h"
 #include "extensions/common/error_utils.h"
 #include "extensions/common/extension.h"
+#include "url/gurl.h"
 
 namespace windows_util {
 
@@ -93,4 +96,54 @@
              controller->profile();
 }
 
+IncognitoResult ShouldOpenIncognitoWindow(Profile* profile,
+                                          absl::optional<bool> incognito,
+                                          std::vector<GURL>* urls,
+                                          std::string* error) {
+  const IncognitoModePrefs::Availability incognito_availability =
+      IncognitoModePrefs::GetAvailability(profile->GetPrefs());
+  bool incognito_result = false;
+  if (incognito.has_value()) {
+    incognito_result = incognito.value();
+    if (incognito_result &&
+        incognito_availability == IncognitoModePrefs::Availability::kDisabled) {
+      *error = extensions::tabs_constants::kIncognitoModeIsDisabled;
+      return IncognitoResult::kError;
+    }
+    if (!incognito_result &&
+        incognito_availability == IncognitoModePrefs::Availability::kForced) {
+      *error = extensions::tabs_constants::kIncognitoModeIsForced;
+      return IncognitoResult::kError;
+    }
+  } else if (incognito_availability ==
+             IncognitoModePrefs::Availability::kForced) {
+    // If incognito argument is not specified explicitly, we default to
+    // incognito when forced so by policy.
+    incognito_result = true;
+  }
+
+  // Remove all URLs that are not allowed in an incognito session. Note that a
+  // ChromeOS guest session is not considered incognito in this case.
+  if (incognito_result && !profile->IsGuestSession()) {
+    std::string first_url_erased;
+    for (size_t i = 0; i < urls->size();) {
+      if (IsURLAllowedInIncognito((*urls)[i], profile)) {
+        i++;
+      } else {
+        if (first_url_erased.empty())
+          first_url_erased = (*urls)[i].spec();
+        urls->erase(urls->begin() + i);
+      }
+    }
+    if (urls->empty() && !first_url_erased.empty()) {
+      *error = extensions::ErrorUtils::FormatErrorMessage(
+          extensions::tabs_constants::kURLsNotAllowedInIncognitoError,
+          first_url_erased);
+      return IncognitoResult::kError;
+    }
+  }
+  return incognito_result ? IncognitoResult::kIncognito
+                          : IncognitoResult::kRegular;
+}
+
 }  // namespace windows_util
diff --git a/chrome/browser/extensions/api/tabs/windows_util.h b/chrome/browser/extensions/api/tabs/windows_util.h
index 551e1bbbb..5c10a3e 100644
--- a/chrome/browser/extensions/api/tabs/windows_util.h
+++ b/chrome/browser/extensions/api/tabs/windows_util.h
@@ -6,10 +6,13 @@
 #define CHROME_BROWSER_EXTENSIONS_API_TABS_WINDOWS_UTIL_H__
 
 #include <string>
+#include <vector>
 
 #include "chrome/browser/extensions/window_controller_list.h"
 
 class ExtensionFunction;
+class Profile;
+class GURL;
 
 namespace extensions {
 class WindowController;
@@ -35,6 +38,20 @@
                         const extensions::WindowController* controller,
                         extensions::WindowController::TypeFilter filter);
 
+// Enum return value for `ShouldOpenIncognitoWindow`, indicating whether to use
+// incognito or the presence of an error.
+enum IncognitoResult { kRegular, kIncognito, kError };
+
+// Returns whether the window should be created in incognito mode. `incognito`
+// is the optional caller preference. `urls` is the list of urls to open. If
+// we are creating an incognito window, the function will remove these urls
+// which may not be opened in incognito mode.  If window creation leads the
+// browser into an erroneous state, `error` is populated.
+IncognitoResult ShouldOpenIncognitoWindow(Profile* profile,
+                                          absl::optional<bool> incognito,
+                                          std::vector<GURL>* urls,
+                                          std::string* error);
+
 }  // namespace windows_util
 
 #endif  // CHROME_BROWSER_EXTENSIONS_API_TABS_WINDOWS_UTIL_H__
diff --git a/chrome/browser/extensions/api/tabs/windows_util_unittest.cc b/chrome/browser/extensions/api/tabs/windows_util_unittest.cc
new file mode 100644
index 0000000..20d9231
--- /dev/null
+++ b/chrome/browser/extensions/api/tabs/windows_util_unittest.cc
@@ -0,0 +1,84 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/extensions/api/tabs/windows_util.h"
+
+#include "base/memory/raw_ptr.h"
+#include "base/memory/scoped_refptr.h"
+#include "base/strings/stringprintf.h"
+#include "base/strings/utf_string_conversions.h"
+#include "chrome/browser/bookmarks/bookmark_model_factory.h"
+#include "chrome/browser/extensions/extension_service_test_base.h"
+#include "chrome/browser/ui/browser.h"
+#include "chrome/test/base/test_browser_window.h"
+#include "chrome/test/base/testing_profile.h"
+#include "components/bookmarks/browser/bookmark_model.h"
+#include "components/bookmarks/test/bookmark_test_helpers.h"
+#include "extensions/browser/api_test_utils.h"
+
+namespace windows_util {
+
+class WindowsUtilUnitTest : public extensions::ExtensionServiceTestBase {
+ public:
+  void SetUp() override {
+    ExtensionServiceTestBase::SetUp();
+    InitializeExtensionService(CreateDefaultInitParams());
+  }
+};
+
+TEST_F(WindowsUtilUnitTest, ShouldOpenIncognitoWindowIncognitoDisabled) {
+  // Incognito disabled.
+  IncognitoModePrefs::SetAvailability(
+      profile()->GetPrefs(), IncognitoModePrefs::Availability::kDisabled);
+
+  std::string error;
+  std::vector<GURL> urls;
+  urls.emplace_back(GURL("https://google.com"));
+  EXPECT_EQ(
+      IncognitoResult::kError,
+      ShouldOpenIncognitoWindow(profile(), /*incognito=*/true, &urls, &error));
+  EXPECT_EQ("Incognito mode is disabled.", error);
+}
+
+TEST_F(WindowsUtilUnitTest, ShouldOpenIncognitoWindowIncognitoForced) {
+  // Incognito forced.
+  IncognitoModePrefs::SetAvailability(
+      profile()->GetPrefs(), IncognitoModePrefs::Availability::kForced);
+
+  std::string error;
+  std::vector<GURL> urls;
+  urls.emplace_back(GURL("https://google.com"));
+  EXPECT_EQ(
+      IncognitoResult::kError,
+      ShouldOpenIncognitoWindow(profile(), /*incognito=*/false, &urls, &error));
+  EXPECT_EQ("Incognito mode is forced. Cannot open normal windows.", error);
+}
+
+TEST_F(WindowsUtilUnitTest, ShouldOpenIncognitoWindowIncompatibleURL) {
+  std::string error;
+  std::vector<GURL> urls;
+  urls.emplace_back(GURL("chrome://history"));
+  EXPECT_EQ(
+      IncognitoResult::kError,
+      ShouldOpenIncognitoWindow(profile(), /*incognito=*/true, &urls, &error));
+  EXPECT_EQ("Cannot open URL \"chrome://history/\" in an incognito window.",
+            error);
+  EXPECT_TRUE(urls.empty());
+}
+
+TEST_F(WindowsUtilUnitTest,
+       ShouldOpenIncognitoWindowIncompatibleURLWithSomeLeft) {
+  std::string error;
+  std::vector<GURL> urls;
+  urls.emplace_back(GURL("chrome://history"));
+  urls.emplace_back(GURL("https://google.com"));
+  EXPECT_EQ(
+      IncognitoResult::kIncognito,
+      ShouldOpenIncognitoWindow(profile(), /*incognito=*/true, &urls, &error));
+
+  size_t expected_size = 1;
+  EXPECT_EQ(expected_size, urls.size());
+}
+
+}  // namespace windows_util
diff --git a/chrome/browser/flag-metadata.json b/chrome/browser/flag-metadata.json
index 71f83fd..2853f62d 100644
--- a/chrome/browser/flag-metadata.json
+++ b/chrome/browser/flag-metadata.json
@@ -5417,11 +5417,6 @@
     "expiry_milestone": 92
   },
   {
-    "name": "shared-clipboard-ui",
-    "owners": [ "//chrome/browser/sharing/OWNERS" ],
-    "expiry_milestone": 90
-  },
-  {
     "name": "shared-highlighting-amp",
     "owners": ["cheickcisse@google.com"],
     "expiry_milestone": 97
@@ -5432,6 +5427,11 @@
     "expiry_milestone": 101
   },
   {
+    "name": "shared-highlighting-manager",
+    "owners": ["jeffreycohen", "kristipark", "cheickcisse@google.com"],
+    "expiry_milestone": 108
+  },
+  {
     "name": "shared-highlighting-refined-blocklist",
     "owners": ["jeffreycohen", "chrome-with-friends-robots@google.com" ],
     "expiry_milestone": 108
diff --git a/chrome/browser/flag_descriptions.cc b/chrome/browser/flag_descriptions.cc
index 2e0773f..0794684c 100644
--- a/chrome/browser/flag_descriptions.cc
+++ b/chrome/browser/flag_descriptions.cc
@@ -2339,12 +2339,6 @@
 const char kSidePanelJourneysDescription[] =
     "Enables Journeys within the side panel.";
 
-const char kSharedClipboardUIName[] =
-    "Enable shared clipboard feature signals to be handled";
-const char kSharedClipboardUIDescription[] =
-    "Enables shared clipboard feature signals to be handled by showing "
-    "a list of user's available devices to share the clipboard.";
-
 const char kSharingDesktopScreenshotsName[] = "Desktop Screenshots";
 const char kSharingDesktopScreenshotsDescription[] =
     "Enables taking"
@@ -2942,6 +2936,10 @@
 const char kSharedHighlightingAmpDescription[] =
     "Enables Shared Highlighting for AMP Viwers.";
 
+const char kSharedHighlightingManagerName[] = "Refactoring Shared Highlighting";
+const char kSharedHighlightingManagerDescription[] =
+    "Refactors Shared Highlighting by centralizing the IPC calls in a Manager.";
+
 const char kSharedHighlightingRefinedBlocklistName[] =
     "Shared Highlighting Blocklist Refinement";
 const char kSharedHighlightingRefinedBlocklistDescription[] =
diff --git a/chrome/browser/flag_descriptions.h b/chrome/browser/flag_descriptions.h
index 8003e91a..909c422 100644
--- a/chrome/browser/flag_descriptions.h
+++ b/chrome/browser/flag_descriptions.h
@@ -1316,9 +1316,6 @@
 extern const char kSidePanelJourneysName[];
 extern const char kSidePanelJourneysDescription[];
 
-extern const char kSharedClipboardUIName[];
-extern const char kSharedClipboardUIDescription[];
-
 extern const char kSharingDesktopScreenshotsName[];
 extern const char kSharingDesktopScreenshotsDescription[];
 
@@ -1659,6 +1656,9 @@
 extern const char kSharedHighlightingAmpName[];
 extern const char kSharedHighlightingAmpDescription[];
 
+extern const char kSharedHighlightingManagerName[];
+extern const char kSharedHighlightingManagerDescription[];
+
 extern const char kSharedHighlightingRefinedBlocklistName[];
 extern const char kSharedHighlightingRefinedBlocklistDescription[];
 
diff --git a/chrome/browser/flags/android/chrome_feature_list.cc b/chrome/browser/flags/android/chrome_feature_list.cc
index 7836d1d4..f658255 100644
--- a/chrome/browser/flags/android/chrome_feature_list.cc
+++ b/chrome/browser/flags/android/chrome_feature_list.cc
@@ -23,7 +23,6 @@
 #include "chrome/browser/performance_hints/performance_hints_features.h"
 #include "chrome/browser/push_messaging/push_messaging_features.h"
 #include "chrome/browser/share/share_features.h"
-#include "chrome/browser/sharing/shared_clipboard/feature_flags.h"
 #include "chrome/browser/ui/ui_features.h"
 #include "chrome/browser/video_tutorials/switches.h"
 #include "chrome/common/chrome_features.h"
@@ -266,7 +265,6 @@
     &kSearchEnginePromoNewDevice,
     &kSearchEnginePromoNewDeviceV2,
     &kShareButtonInTopToolbar,
-    &kSharedClipboardUI,
     &kShowScrollableMVTOnNTPAndroid,
     &kFeedPositionAndroid,
     &kSpannableInlineAutocomplete,
@@ -364,6 +362,7 @@
     &query_tiles::features::kQueryTilesInNTP,
     &query_tiles::features::kQueryTilesSegmentation,
     &reading_list::switches::kReadLater,
+    &safe_browsing::kCreateSafebrowsingOnStartup,
     &segmentation_platform::features::kContextualPageActionsWithPriceTracking,
     &send_tab_to_self::kSendTabToSelfSigninPromo,
     &send_tab_to_self::kSendTabToSelfV2,
@@ -513,7 +512,7 @@
     "CCTResizableForFirstParties", base::FEATURE_ENABLED_BY_DEFAULT};
 
 const base::Feature kCCTResizableForThirdParties{
-    "CCTResizableForThirdParties", base::FEATURE_ENABLED_BY_DEFAULT};
+    "CCTResizableForThirdParties", base::FEATURE_DISABLED_BY_DEFAULT};
 
 const base::Feature kCCTResourcePrefetch{"CCTResourcePrefetch",
                                          base::FEATURE_ENABLED_BY_DEFAULT};
diff --git a/chrome/browser/flags/android/java/src/org/chromium/chrome/browser/flags/CachedFeatureFlags.java b/chrome/browser/flags/android/java/src/org/chromium/chrome/browser/flags/CachedFeatureFlags.java
index cc78c84784..9dbecb0 100644
--- a/chrome/browser/flags/android/java/src/org/chromium/chrome/browser/flags/CachedFeatureFlags.java
+++ b/chrome/browser/flags/android/java/src/org/chromium/chrome/browser/flags/CachedFeatureFlags.java
@@ -100,6 +100,7 @@
                     .put(ChromeFeatureList.BACK_GESTURE_REFACTOR, false)
                     .put(ChromeFeatureList.TRUSTED_WEB_ACTIVITY_NOTIFICATION_PERMISSION_DELEGATION,
                             false)
+                    .put(ChromeFeatureList.CREATE_SAFEBROWSING_ON_STARTUP, false)
                     .build();
 
     /**
diff --git a/chrome/browser/flags/android/java/src/org/chromium/chrome/browser/flags/ChromeFeatureList.java b/chrome/browser/flags/android/java/src/org/chromium/chrome/browser/flags/ChromeFeatureList.java
index edd78ed..997308e6 100644
--- a/chrome/browser/flags/android/java/src/org/chromium/chrome/browser/flags/ChromeFeatureList.java
+++ b/chrome/browser/flags/android/java/src/org/chromium/chrome/browser/flags/ChromeFeatureList.java
@@ -314,6 +314,7 @@
             "SyncAndroidPromosWithSingleButton";
     public static final String SYNC_ANDROID_PROMOS_WITH_TITLE = "SyncAndroidPromosWithTitle";
     public static final String CONTINUOUS_SEARCH = "ContinuousSearch";
+    public static final String CREATE_SAFEBROWSING_ON_STARTUP = "CreateSafebrowsingOnStartup";
     public static final String CRITICAL_PERSISTED_TAB_DATA = "CriticalPersistedTabData";
     public static final String DARKEN_WEBSITES_CHECKBOX_IN_THEMES_SETTING =
             "DarkenWebsitesCheckboxInThemesSetting";
@@ -489,7 +490,6 @@
     public static final String SEND_TAB_TO_SELF_V2 = "SendTabToSelfV2";
     public static final String SHARE_BUTTON_IN_TOP_TOOLBAR = "ShareButtonInTopToolbar";
     public static final String SHARE_CROW_BUTTON = "ShareCrowButton";
-    public static final String SHARED_CLIPBOARD_UI = "SharedClipboardUI";
     public static final String SHARED_HIGHLIGHTING_AMP = "SharedHighlightingAmp";
     public static final String SHOPPING_LIST = "ShoppingList";
     public static final String SHOW_EXTENDED_PRELOADING_SETTING = "ShowExtendedPreloadingSetting";
diff --git a/chrome/browser/lacros/for_which_extension_type.h b/chrome/browser/lacros/for_which_extension_type.h
index 0845126f..184fdcd 100644
--- a/chrome/browser/lacros/for_which_extension_type.h
+++ b/chrome/browser/lacros/for_which_extension_type.h
@@ -37,6 +37,16 @@
     return for_chrome_apps_ ? val_for_chrome_apps : val_for_extensions;
   }
 
+  template <class T>
+  const T& ChooseIntentFilter(bool is_quick_office,
+                              const T& val_for_chrome_apps,
+                              const T& val_for_extensions) const {
+    if (is_quick_office || for_chrome_apps_)
+      return val_for_chrome_apps;
+    else
+      return val_for_extensions;
+  }
+
  private:
   friend ForWhichExtensionType InitForChromeApps();
   friend ForWhichExtensionType InitForExtensions();
diff --git a/chrome/browser/lacros/lacros_extension_apps_controller.cc b/chrome/browser/lacros/lacros_extension_apps_controller.cc
index 0bc42055..e215b8d 100644
--- a/chrome/browser/lacros/lacros_extension_apps_controller.cc
+++ b/chrome/browser/lacros/lacros_extension_apps_controller.cc
@@ -37,6 +37,7 @@
 #include "extensions/browser/extension_prefs.h"
 #include "extensions/browser/extension_system.h"
 #include "extensions/browser/uninstall_reason.h"
+#include "extensions/common/constants.h"
 #include "extensions/common/extension.h"
 #include "extensions/common/extension_urls.h"
 #include "ui/base/window_open_disposition.h"
@@ -299,7 +300,8 @@
   auto params = apps::ConvertCrosapiToLaunchParams(launch_params, profile);
   params.app_id = extension->id();
 
-  if (which_type_.IsChromeApps()) {
+  if (which_type_.IsChromeApps() ||
+      extension_misc::IsQuickOfficeExtension(extension->id())) {
     OpenApplication(profile, std::move(params));
 
     // TODO(https://crbug.com/1225848): Store the resulting instance token,
@@ -341,6 +343,7 @@
 
   } else {
     NOTREACHED();
+    std::move(callback).Run(std::move(result));
   }
 }
 
diff --git a/chrome/browser/lacros/lacros_extension_apps_publisher.cc b/chrome/browser/lacros/lacros_extension_apps_publisher.cc
index 0594d3b..a3845513 100644
--- a/chrome/browser/lacros/lacros_extension_apps_publisher.cc
+++ b/chrome/browser/lacros/lacros_extension_apps_publisher.cc
@@ -321,10 +321,11 @@
     app->allow_uninstall = (policy->UserMayModifySettings(extension, nullptr) &&
                             !policy->MustRemainInstalled(extension, nullptr));
 
-    // Add file_handlers for Chrome Apps, or file_browser_handler for
-    // Extensions.
+    // Add file_handlers for Chrome Apps and quickoffice, or
+    // file_browser_handler for Extensions.
     base::Extend(app->intent_filters,
-                 which_type_.ChooseForChromeAppOrExtension(
+                 which_type_.ChooseIntentFilter(
+                     extension_misc::IsQuickOfficeExtension(extension->id()),
                      apps_util::CreateIntentFiltersForChromeApp,
                      apps_util::CreateIntentFiltersForExtension)(extension));
     return app;
diff --git a/chrome/browser/media/cdm_document_service_impl.cc b/chrome/browser/media/cdm_document_service_impl.cc
index fdda065..7985b99d 100644
--- a/chrome/browser/media/cdm_document_service_impl.cc
+++ b/chrome/browser/media/cdm_document_service_impl.cc
@@ -94,16 +94,15 @@
                    "Windows 10.";
     return false;
   }
+  // If the path exist, we can assume the right permission are already
+  // set on it.
+  if (base::PathExists(cdm_store_path_root))
+    return true;
 
-  // TODO(crbug.com/1323885): After things stabilizes, assume that if the path
-  // exists, the right permission should have been set on it, so only grant
-  // permission on newly created folders.
-  if (!base::PathExists(cdm_store_path_root)) {
-    base::File::Error file_error;
-    if (!base::CreateDirectoryAndGetError(cdm_store_path_root, &file_error)) {
-      DLOG(ERROR) << "Create CDM store path failed with " << file_error;
-      return false;
-    }
+  base::File::Error file_error;
+  if (!base::CreateDirectoryAndGetError(cdm_store_path_root, &file_error)) {
+    DLOG(ERROR) << "Create CDM store path failed with " << file_error;
+    return false;
   }
 
   auto sids = base::win::Sid::FromNamedCapabilityVector(
diff --git a/chrome/browser/navigation_predictor/anchor_element_preloader.cc b/chrome/browser/navigation_predictor/anchor_element_preloader.cc
index ad5e1440..1e56ec5 100644
--- a/chrome/browser/navigation_predictor/anchor_element_preloader.cc
+++ b/chrome/browser/navigation_predictor/anchor_element_preloader.cc
@@ -12,10 +12,13 @@
 #include "services/metrics/public/cpp/ukm_builders.h"
 #include "services/metrics/public/cpp/ukm_recorder.h"
 #include "third_party/blink/public/common/features.h"
+#include "url/scheme_host_port.h"
 
 const char kPreloadingAnchorElementPreloaderPreloadingTriggered[] =
     "Preloading.AnchorElementPreloader.PreloadingTriggered";
 
+AnchorElementPreloader::~AnchorElementPreloader() = default;
+
 AnchorElementPreloader::AnchorElementPreloader(
     content::RenderFrameHost* render_frame_host,
     mojo::PendingReceiver<blink::mojom::AnchorElementInteractionHost> receiver)
@@ -38,6 +41,13 @@
                ->GetPrefs())) {
     return;
   }
+  url::SchemeHostPort scheme_host_port(target);
+  if (preconnected_targets_.find(scheme_host_port) !=
+      preconnected_targets_.end()) {
+    // We've already preconnected to that origin.
+    return;
+  }
+  preconnected_targets_.insert(scheme_host_port);
 
   RecordUmaPreloadedTriggered(AnchorElementPreloaderType::kPreconnect);
 
diff --git a/chrome/browser/navigation_predictor/anchor_element_preloader.h b/chrome/browser/navigation_predictor/anchor_element_preloader.h
index 0af5b65..6929500 100644
--- a/chrome/browser/navigation_predictor/anchor_element_preloader.h
+++ b/chrome/browser/navigation_predictor/anchor_element_preloader.h
@@ -7,6 +7,7 @@
 
 #include "content/public/browser/document_service.h"
 #include "third_party/blink/public/mojom/loader/anchor_element_interaction_host.mojom.h"
+#include "url/scheme_host_port.h"
 
 extern const char kPreloadingAnchorElementPreloaderPreloadingTriggered[];
 
@@ -21,6 +22,8 @@
 class AnchorElementPreloader
     : content::DocumentService<blink::mojom::AnchorElementInteractionHost> {
  public:
+  ~AnchorElementPreloader() override;
+
   static void Create(
       content::RenderFrameHost* render_frame_host,
       mojo::PendingReceiver<blink::mojom::AnchorElementInteractionHost>
@@ -38,6 +41,8 @@
   void RecordUmaPreloadedTriggered(AnchorElementPreloaderType);
 
   void RecordUkmPreloadType(AnchorElementPreloaderType);
+
+  std::set<url::SchemeHostPort> preconnected_targets_;
 };
 
 #endif  // CHROME_BROWSER_NAVIGATION_PREDICTOR_ANCHOR_ELEMENT_PRELOADER_H_
diff --git a/chrome/browser/navigation_predictor/anchor_element_preloader_browsertest.cc b/chrome/browser/navigation_predictor/anchor_element_preloader_browsertest.cc
index 8a9b044..c305809 100644
--- a/chrome/browser/navigation_predictor/anchor_element_preloader_browsertest.cc
+++ b/chrome/browser/navigation_predictor/anchor_element_preloader_browsertest.cc
@@ -30,7 +30,8 @@
     : public subresource_filter::SubresourceFilterBrowserTest,
       public predictors::PreconnectManager::Observer {
  public:
-  static constexpr char kFakeSearch[] = "https://www.fakesearch.com/";
+  static constexpr char kOrigin1[] = "https://www.origin1.com/";
+  static constexpr char kOrigin2[] = "https://www.origin2.com/";
 
   virtual void SetFeatures() {
     feature_list_.InitAndEnableFeature(
@@ -95,7 +96,7 @@
       const GURL& url,
       const net::NetworkIsolationKey& network_isolation_key,
       bool success) override {
-    if (url != GURL(kFakeSearch)) {
+    if (url != GURL(kOrigin1) && url != GURL(kOrigin2)) {
       return;
     }
 
@@ -151,6 +152,59 @@
   EXPECT_EQ(ukm_entries[0].source_id, ukm_source_id);
 }
 
+IN_PROC_BROWSER_TEST_F(AnchorElementPreloaderBrowserTest, Duplicates) {
+  const GURL& url = GetTestURL("/many_anchors.html");
+  EXPECT_TRUE(ui_test_utils::NavigateToURL(browser(), url));
+  ukm::SourceId ukm_source_id = browser()
+                                    ->tab_strip_model()
+                                    ->GetActiveWebContents()
+                                    ->GetMainFrame()
+                                    ->GetPageUkmSourceId();
+
+  // First link with mousedown event should get preconnected.
+  SimulateMouseDownElementWithId("anchor1_origin1");
+  WaitForPreresolveCountForURL(1);
+  EXPECT_EQ(1, preresolve_count_);
+  histogram_tester()->ExpectTotalCount(
+      kPreloadingAnchorElementPreloaderPreloadingTriggered, 1);
+  histogram_tester()->ExpectUniqueSample(
+      kPreloadingAnchorElementPreloaderPreloadingTriggered,
+      AnchorElementPreloaderType::kPreconnect, 1);
+  auto ukm_entries = test_ukm_recorder()->GetEntries(
+      ukm::builders::Preloading_AnchorInteraction::kEntryName,
+      {ukm::builders::Preloading_AnchorInteraction::
+           kAnchorElementPreloaderTypeName});
+  EXPECT_EQ(ukm_entries.size(), 1u);
+  EXPECT_EQ(ukm_entries[0].source_id, ukm_source_id);
+
+  // Second mousedown event to same origin: should not trigger a preconnect.
+  SimulateMouseDownElementWithId("anchor2_origin1");
+  EXPECT_EQ(1, preresolve_count_);
+  histogram_tester()->ExpectTotalCount(
+      kPreloadingAnchorElementPreloaderPreloadingTriggered, 1);
+  ukm_entries = test_ukm_recorder()->GetEntries(
+      ukm::builders::Preloading_AnchorInteraction::kEntryName,
+      {ukm::builders::Preloading_AnchorInteraction::
+           kAnchorElementPreloaderTypeName});
+  EXPECT_EQ(ukm_entries.size(), 1u);
+
+  // Third mousedown event to a different origin: should trigger a preconnect.
+  SimulateMouseDownElementWithId("anchor1_origin2");
+  WaitForPreresolveCountForURL(2);
+  EXPECT_EQ(2, preresolve_count_);
+  histogram_tester()->ExpectTotalCount(
+      kPreloadingAnchorElementPreloaderPreloadingTriggered, 2);
+  histogram_tester()->ExpectUniqueSample(
+      kPreloadingAnchorElementPreloaderPreloadingTriggered,
+      AnchorElementPreloaderType::kPreconnect, 2);
+  ukm_entries = test_ukm_recorder()->GetEntries(
+      ukm::builders::Preloading_AnchorInteraction::kEntryName,
+      {ukm::builders::Preloading_AnchorInteraction::
+           kAnchorElementPreloaderTypeName});
+  EXPECT_EQ(ukm_entries.size(), 2u);
+  EXPECT_EQ(ukm_entries[1].source_id, ukm_source_id);
+}
+
 IN_PROC_BROWSER_TEST_F(AnchorElementPreloaderBrowserTest, InvalidHref) {
   const GURL& url = GetTestURL("/invalid_href_anchor.html");
   EXPECT_TRUE(ui_test_utils::NavigateToURL(browser(), url));
diff --git a/chrome/browser/policy/BUILD.gn b/chrome/browser/policy/BUILD.gn
index f4c93c5..ea048d7f 100644
--- a/chrome/browser/policy/BUILD.gn
+++ b/chrome/browser/policy/BUILD.gn
@@ -403,7 +403,7 @@
     sources += [ "test/full_screen_allowed_policy_browsertest.cc" ]
   }
 
-  if (!is_chromeos_ash) {
+  if (!is_chromeos) {
     sources += [
       "cloud/chrome_browser_cloud_management_browsertest.cc",
       "cloud/chrome_browser_cloud_management_browsertest_delegate.h",
diff --git a/chrome/browser/policy/cloud/chrome_browser_cloud_management_browsertest.cc b/chrome/browser/policy/cloud/chrome_browser_cloud_management_browsertest.cc
index ec31154c..0a6ba57 100644
--- a/chrome/browser/policy/cloud/chrome_browser_cloud_management_browsertest.cc
+++ b/chrome/browser/policy/cloud/chrome_browser_cloud_management_browsertest.cc
@@ -229,18 +229,12 @@
 
   void OnCoreDisconnecting(CloudPolicyCore* core) override {
     // This is called when policy fetching fails and is used in
-    // ChromeBrowserCloudManagementController to unenroll the browser.
-#if BUILDFLAG(IS_CHROMEOS)
-    // The status must be `DM_STATUS_SERVICE_DEVICE_NOT_FOUND` since DMToken
-    // deletion is not supported in ChromeOS.
-    EXPECT_EQ(DM_STATUS_SERVICE_DEVICE_NOT_FOUND, core->client()->status());
-#else   // BUILDFLAG(IS_CHROMEOS)
-    // The status must be either `DM_STATUS_SERVICE_DEVICE_NOT_FOUND` or
+    // ChromeBrowserCloudManagementController to unenroll the browser. The
+    // status must be either `DM_STATUS_SERVICE_DEVICE_NOT_FOUND` or
     // `DM_STATUS_SERVICE_DEVICE_NEEDS_RESET` for this to happen.
     EXPECT_THAT((std::array{DM_STATUS_SERVICE_DEVICE_NOT_FOUND,
                             DM_STATUS_SERVICE_DEVICE_NEEDS_RESET}),
                 testing::Contains(core->client()->status()));
-#endif  // BUILDFLAG(IS_CHROMEOS)
     std::move(quit_closure_).Run();
   }
 
@@ -790,14 +784,8 @@
                                          storage_enabled(), 1);
   } else if (dm_token() == kDeletionDMToken) {
     EXPECT_EQ(0u, policy_map.size());
-#if BUILDFLAG(IS_CHROMEOS)
-    // The token in storage should be invalid since DMToken deletion is not
-    // supported in ChromeOS.
-    EXPECT_TRUE(token.is_invalid());
-#else   // BUILDFLAG(IS_CHROMEOS)
     // The token in storage should be empty.
     EXPECT_TRUE(token.is_empty());
-#endif  // BUILDFLAG(IS_CHROMEOS)
     histogram_tester_.ExpectUniqueSample(kUnenrollmentSuccessMetrics,
                                          storage_enabled(), 1);
   } else {
diff --git a/chrome/browser/reputation/reputation_web_contents_observer.h b/chrome/browser/reputation/reputation_web_contents_observer.h
index 736d6958..0004604d 100644
--- a/chrome/browser/reputation/reputation_web_contents_observer.h
+++ b/chrome/browser/reputation/reputation_web_contents_observer.h
@@ -23,7 +23,7 @@
 #include "url/origin.h"
 
 #if BUILDFLAG(IS_ANDROID)
-#include "chrome/browser/reputation/safety_tip_message_delegate.h"
+#include "chrome/browser/reputation/safety_tip_message_delegate_android.h"
 #endif
 
 class Profile;
@@ -120,7 +120,7 @@
   base::OnceClosure safety_tip_close_callback_for_testing_;
 
 #if BUILDFLAG(IS_ANDROID)
-  SafetyTipMessageDelegate delegate_;
+  SafetyTipMessageDelegateAndroid delegate_;
 #endif
 
   std::unique_ptr<DigitalAssetLinkCrossValidator> digital_asset_link_validator_;
diff --git a/chrome/browser/reputation/safety_tip_message_delegate.cc b/chrome/browser/reputation/safety_tip_message_delegate_android.cc
similarity index 78%
rename from chrome/browser/reputation/safety_tip_message_delegate.cc
rename to chrome/browser/reputation/safety_tip_message_delegate_android.cc
index 48ae7d6f..b921454d 100644
--- a/chrome/browser/reputation/safety_tip_message_delegate.cc
+++ b/chrome/browser/reputation/safety_tip_message_delegate_android.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/reputation/safety_tip_message_delegate.h"
+#include "chrome/browser/reputation/safety_tip_message_delegate_android.h"
 
 #include "chrome/browser/android/android_theme_resources.h"
 #include "chrome/browser/android/resource_mapper.h"
@@ -12,13 +12,13 @@
 #include "components/strings/grit/components_strings.h"
 #include "ui/base/l10n/l10n_util.h"
 
-SafetyTipMessageDelegate::SafetyTipMessageDelegate() = default;
+SafetyTipMessageDelegateAndroid::SafetyTipMessageDelegateAndroid() = default;
 
-SafetyTipMessageDelegate::~SafetyTipMessageDelegate() {
+SafetyTipMessageDelegateAndroid::~SafetyTipMessageDelegateAndroid() {
   DismissInternal();
 }
 
-void SafetyTipMessageDelegate::DisplaySafetyTipPrompt(
+void SafetyTipMessageDelegateAndroid::DisplaySafetyTipPrompt(
     security_state::SafetyTipStatus safety_tip_status,
     const GURL& suggested_url,
     content::WebContents* web_contents,
@@ -31,9 +31,9 @@
   close_callback_ = std::move(close_callback);
   message_ = std::make_unique<messages::MessageWrapper>(
       messages::MessageIdentifier::SAFETY_TIP,
-      base::BindOnce(&SafetyTipMessageDelegate::HandleLeaveSiteClick,
+      base::BindOnce(&SafetyTipMessageDelegateAndroid::HandleLeaveSiteClick,
                      base::Unretained(this)),
-      base::BindOnce(&SafetyTipMessageDelegate::HandleDismissCallback,
+      base::BindOnce(&SafetyTipMessageDelegateAndroid::HandleDismissCallback,
                      base::Unretained(this)));
   message_->SetTitle(GetSafetyTipTitle(safety_tip_status, suggested_url));
 
@@ -51,7 +51,8 @@
       l10n_util::GetStringUTF16(IDS_PAGE_INFO_SAFETY_TIP_MORE_INFO_LINK));
 
   message_->SetSecondaryActionCallback(base::BindRepeating(
-      &SafetyTipMessageDelegate::HandleLearnMoreClick, base::Unretained(this)));
+      &SafetyTipMessageDelegateAndroid::HandleLearnMoreClick,
+      base::Unretained(this)));
 
   // 60s
   message_->SetDuration(60000);
@@ -61,7 +62,7 @@
       messages::MessagePriority::kUrgent);
 }
 
-void SafetyTipMessageDelegate::HandleLeaveSiteClick() {
+void SafetyTipMessageDelegateAndroid::HandleLeaveSiteClick() {
   action_taken_ = SafetyTipInteraction::kLeaveSite;
   auto url = safety_tip_status_ == security_state::SafetyTipStatus::kLookalike
                  ? suggested_url_
@@ -69,11 +70,11 @@
   LeaveSiteFromSafetyTip(web_contents_, url);
 }
 
-void SafetyTipMessageDelegate::HandleLearnMoreClick() {
+void SafetyTipMessageDelegateAndroid::HandleLearnMoreClick() {
   OpenHelpCenterFromSafetyTip(web_contents_);
 }
 
-void SafetyTipMessageDelegate::HandleDismissCallback(
+void SafetyTipMessageDelegateAndroid::HandleDismissCallback(
     messages::DismissReason dismiss_reason) {
   if (dismiss_reason == messages::DismissReason::GESTURE) {
     action_taken_ = SafetyTipInteraction::kDismissWithClose;
@@ -86,7 +87,7 @@
   web_contents_ = nullptr;
 }
 
-void SafetyTipMessageDelegate::DismissInternal() {
+void SafetyTipMessageDelegateAndroid::DismissInternal() {
   if (message_) {
     messages::MessageDispatcherBridge::Get()->DismissMessage(
         message_.get(), messages::DismissReason::UNKNOWN);
diff --git a/chrome/browser/reputation/safety_tip_message_delegate.h b/chrome/browser/reputation/safety_tip_message_delegate_android.h
similarity index 82%
rename from chrome/browser/reputation/safety_tip_message_delegate.h
rename to chrome/browser/reputation/safety_tip_message_delegate_android.h
index 0415901..60ce7c56 100644
--- a/chrome/browser/reputation/safety_tip_message_delegate.h
+++ b/chrome/browser/reputation/safety_tip_message_delegate_android.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CHROME_BROWSER_REPUTATION_SAFETY_TIP_MESSAGE_DELEGATE_H_
-#define CHROME_BROWSER_REPUTATION_SAFETY_TIP_MESSAGE_DELEGATE_H_
+#ifndef CHROME_BROWSER_REPUTATION_SAFETY_TIP_MESSAGE_DELEGATE_ANDROID_H_
+#define CHROME_BROWSER_REPUTATION_SAFETY_TIP_MESSAGE_DELEGATE_ANDROID_H_
 
 #include <memory>
 
@@ -19,10 +19,10 @@
 }  // namespace content
 
 // Message delegate to show a safety tip message on Android.
-class SafetyTipMessageDelegate {
+class SafetyTipMessageDelegateAndroid {
  public:
-  SafetyTipMessageDelegate();
-  ~SafetyTipMessageDelegate();
+  SafetyTipMessageDelegateAndroid();
+  ~SafetyTipMessageDelegateAndroid();
 
   void DisplaySafetyTipPrompt(
       security_state::SafetyTipStatus safety_tip_status,
@@ -31,7 +31,7 @@
       base::OnceCallback<void(SafetyTipInteraction)> close_callback);
 
  private:
-  friend class SafetyTipMessageDelegateTest;
+  friend class SafetyTipMessageDelegateAndroidTest;
 
   void HandleLeaveSiteClick();
   void HandleLearnMoreClick();
@@ -52,4 +52,4 @@
   base::OnceCallback<void(SafetyTipInteraction)> close_callback_;
 };
 
-#endif  // CHROME_BROWSER_REPUTATION_SAFETY_TIP_MESSAGE_DELEGATE_H_
+#endif  // CHROME_BROWSER_REPUTATION_SAFETY_TIP_MESSAGE_DELEGATE_ANDROID_H_
diff --git a/chrome/browser/reputation/safety_tip_message_delegate_unittest.cc b/chrome/browser/reputation/safety_tip_message_delegate_android_unittest.cc
similarity index 86%
rename from chrome/browser/reputation/safety_tip_message_delegate_unittest.cc
rename to chrome/browser/reputation/safety_tip_message_delegate_android_unittest.cc
index 71b77e4..66a9265 100644
--- a/chrome/browser/reputation/safety_tip_message_delegate_unittest.cc
+++ b/chrome/browser/reputation/safety_tip_message_delegate_android_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/reputation/safety_tip_message_delegate.h"
+#include "chrome/browser/reputation/safety_tip_message_delegate_android.h"
 
 #include "base/test/mock_callback.h"
 #include "chrome/browser/android/android_theme_resources.h"
@@ -37,9 +37,10 @@
   int opened_ = 0;
 };
 
-class SafetyTipMessageDelegateTest : public ChromeRenderViewHostTestHarness {
+class SafetyTipMessageDelegateAndroidTest
+    : public ChromeRenderViewHostTestHarness {
  public:
-  SafetyTipMessageDelegateTest() = default;
+  SafetyTipMessageDelegateAndroidTest() = default;
 
  protected:
   void SetUp() override;
@@ -62,24 +63,24 @@
   }
 
  private:
-  SafetyTipMessageDelegate delegate_;
+  SafetyTipMessageDelegateAndroid delegate_;
   messages::MockMessageDispatcherBridge message_dispatcher_bridge_;
   TestNavigationDelegate mock_web_contents_delegate_;
 };
 
-void SafetyTipMessageDelegateTest::SetUp() {
+void SafetyTipMessageDelegateAndroidTest::SetUp() {
   ChromeRenderViewHostTestHarness::SetUp();
   messages::MessageDispatcherBridge::SetInstanceForTesting(
       &message_dispatcher_bridge_);
   NavigateAndCommit(GURL(kDefaultUrl));
 }
 
-void SafetyTipMessageDelegateTest::TearDown() {
+void SafetyTipMessageDelegateAndroidTest::TearDown() {
   messages::MessageDispatcherBridge::SetInstanceForTesting(nullptr);
   ChromeRenderViewHostTestHarness::TearDown();
 }
 
-void SafetyTipMessageDelegateTest::EnqueueMessage(
+void SafetyTipMessageDelegateAndroidTest::EnqueueMessage(
     base::OnceCallback<void(SafetyTipInteraction)> close_callback,
     bool enqueue_expected,
     security_state::SafetyTipStatus safety_tip_status) {
@@ -93,7 +94,7 @@
                                    web_contents(), std::move(close_callback));
 }
 
-void SafetyTipMessageDelegateTest::DismissMessage() {
+void SafetyTipMessageDelegateAndroidTest::DismissMessage() {
   EXPECT_CALL(message_dispatcher_bridge_, DismissMessage)
       .WillOnce([](messages::MessageWrapper* message,
                    messages::DismissReason dismiss_reason) {
@@ -104,16 +105,16 @@
   EXPECT_EQ(nullptr, GetMessageWrapper());
 }
 
-void SafetyTipMessageDelegateTest::TriggerPrimaryButtonClick() {
+void SafetyTipMessageDelegateAndroidTest::TriggerPrimaryButtonClick() {
   GetMessageWrapper()->HandleActionClick(base::android::AttachCurrentThread());
 }
 
-void SafetyTipMessageDelegateTest::TriggerSecondaryButtonClick() {
+void SafetyTipMessageDelegateAndroidTest::TriggerSecondaryButtonClick() {
   GetMessageWrapper()->HandleSecondaryActionClick(
       base::android::AttachCurrentThread());
 }
 
-TEST_F(SafetyTipMessageDelegateTest, DismissOnNoAction) {
+TEST_F(SafetyTipMessageDelegateAndroidTest, DismissOnNoAction) {
   base::MockOnceCallback<void(SafetyTipInteraction)> mock_callback_receiver;
   EnqueueMessage(mock_callback_receiver.Get(), true,
                  security_state::SafetyTipStatus::kBadReputation);
@@ -121,7 +122,7 @@
   DismissMessage();
 }
 
-TEST_F(SafetyTipMessageDelegateTest, DoNotReplaceCurrentMessage) {
+TEST_F(SafetyTipMessageDelegateAndroidTest, DoNotReplaceCurrentMessage) {
   base::MockOnceCallback<void(SafetyTipInteraction)> mock_callback_receiver;
   EnqueueMessage(mock_callback_receiver.Get(), true,
                  security_state::SafetyTipStatus::kBadReputation);
@@ -134,7 +135,7 @@
   DismissMessage();
 }
 
-TEST_F(SafetyTipMessageDelegateTest, PrimaryActionCallback) {
+TEST_F(SafetyTipMessageDelegateAndroidTest, PrimaryActionCallback) {
   base::MockOnceCallback<void(SafetyTipInteraction)> mock_callback_receiver;
   EnqueueMessage(mock_callback_receiver.Get(), true,
                  security_state::SafetyTipStatus::kBadReputation);
@@ -146,7 +147,7 @@
   DismissMessage();
 }
 
-TEST_F(SafetyTipMessageDelegateTest, SecondaryActionCallback) {
+TEST_F(SafetyTipMessageDelegateAndroidTest, SecondaryActionCallback) {
   base::MockOnceCallback<void(SafetyTipInteraction)> mock_callback_receiver;
   EnqueueMessage(mock_callback_receiver.Get(), true,
                  security_state::SafetyTipStatus::kBadReputation);
@@ -158,7 +159,8 @@
   DismissMessage();
 }
 
-TEST_F(SafetyTipMessageDelegateTest, MessagePropertyValuesBadReputation) {
+TEST_F(SafetyTipMessageDelegateAndroidTest,
+       MessagePropertyValuesBadReputation) {
   base::MockOnceCallback<void(SafetyTipInteraction)> mock_callback_receiver;
   security_state::SafetyTipStatus status =
       security_state::SafetyTipStatus::kBadReputation;
@@ -181,7 +183,7 @@
   DismissMessage();
 }
 
-TEST_F(SafetyTipMessageDelegateTest, MessagePropertyValuesLookAlike) {
+TEST_F(SafetyTipMessageDelegateAndroidTest, MessagePropertyValuesLookAlike) {
   base::MockOnceCallback<void(SafetyTipInteraction)> mock_callback_receiver;
   security_state::SafetyTipStatus status =
       security_state::SafetyTipStatus::kLookalike;
diff --git a/chrome/browser/resources/chromeos/login/cr_ui.js b/chrome/browser/resources/chromeos/login/cr_ui.js
index 8b35e80..3d858f9 100644
--- a/chrome/browser/resources/chromeos/login/cr_ui.js
+++ b/chrome/browser/resources/chromeos/login/cr_ui.js
@@ -122,7 +122,7 @@
      * Skip to login screen for telemetry.
      */
     static skipToLoginForTesting() {
-      chrome.send('skipToLoginForTesting');
+      chrome.send('OobeTestApi.skipToLoginForTesting');
     }
 
     /**
@@ -149,7 +149,7 @@
         }
       }
 
-      chrome.send('skipToLoginForTesting');
+      chrome.send('OobeTestApi.skipToLoginForTesting');
 
       if (!enterpriseEnroll) {
         chrome.send('completeLogin', [gaia_id, username, password, false]);
diff --git a/chrome/browser/sharing/shared_clipboard/feature_flags.cc b/chrome/browser/sharing/shared_clipboard/feature_flags.cc
deleted file mode 100644
index 1552b096..0000000
--- a/chrome/browser/sharing/shared_clipboard/feature_flags.cc
+++ /dev/null
@@ -1,8 +0,0 @@
-// Copyright 2019 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "chrome/browser/sharing/shared_clipboard/feature_flags.h"
-
-const base::Feature kSharedClipboardUI{"SharedClipboardUI",
-                                       base::FEATURE_DISABLED_BY_DEFAULT};
diff --git a/chrome/browser/sharing/shared_clipboard/feature_flags.h b/chrome/browser/sharing/shared_clipboard/feature_flags.h
deleted file mode 100644
index e9be85e..0000000
--- a/chrome/browser/sharing/shared_clipboard/feature_flags.h
+++ /dev/null
@@ -1,13 +0,0 @@
-// Copyright 2019 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CHROME_BROWSER_SHARING_SHARED_CLIPBOARD_FEATURE_FLAGS_H_
-#define CHROME_BROWSER_SHARING_SHARED_CLIPBOARD_FEATURE_FLAGS_H_
-
-#include "base/feature_list.h"
-
-// Feature to allow shared clipboard gets processed.
-extern const base::Feature kSharedClipboardUI;
-
-#endif  // CHROME_BROWSER_SHARING_SHARED_CLIPBOARD_FEATURE_FLAGS_H_
diff --git a/chrome/browser/sharing/shared_clipboard/shared_clipboard_context_menu_observer.cc b/chrome/browser/sharing/shared_clipboard/shared_clipboard_context_menu_observer.cc
index 1918da2b..bdffb4c 100644
--- a/chrome/browser/sharing/shared_clipboard/shared_clipboard_context_menu_observer.cc
+++ b/chrome/browser/sharing/shared_clipboard/shared_clipboard_context_menu_observer.cc
@@ -8,7 +8,6 @@
 #include "build/build_config.h"
 #include "chrome/app/chrome_command_ids.h"
 #include "chrome/browser/renderer_context_menu/render_view_context_menu.h"
-#include "chrome/browser/sharing/shared_clipboard/feature_flags.h"
 #include "chrome/browser/sharing/shared_clipboard/shared_clipboard_ui_controller.h"
 #include "chrome/browser/sharing/sharing_constants.h"
 #include "chrome/browser/sharing/sharing_metrics.h"
diff --git a/chrome/browser/sharing/shared_clipboard/shared_clipboard_context_menu_observer_unittest.cc b/chrome/browser/sharing/shared_clipboard/shared_clipboard_context_menu_observer_unittest.cc
index 5c1c640..f267218 100644
--- a/chrome/browser/sharing/shared_clipboard/shared_clipboard_context_menu_observer_unittest.cc
+++ b/chrome/browser/sharing/shared_clipboard/shared_clipboard_context_menu_observer_unittest.cc
@@ -16,7 +16,6 @@
 #include "chrome/browser/sharing/fake_device_info.h"
 #include "chrome/browser/sharing/features.h"
 #include "chrome/browser/sharing/mock_sharing_service.h"
-#include "chrome/browser/sharing/shared_clipboard/feature_flags.h"
 #include "chrome/browser/sharing/sharing_constants.h"
 #include "chrome/browser/sharing/sharing_service_factory.h"
 #include "chrome/browser/sharing/vapid_key_manager.h"
diff --git a/chrome/browser/sharing/shared_clipboard/shared_clipboard_utils.cc b/chrome/browser/sharing/shared_clipboard/shared_clipboard_utils.cc
index aca181d2..25f5e29a 100644
--- a/chrome/browser/sharing/shared_clipboard/shared_clipboard_utils.cc
+++ b/chrome/browser/sharing/shared_clipboard/shared_clipboard_utils.cc
@@ -5,7 +5,6 @@
 #include "chrome/browser/sharing/shared_clipboard/shared_clipboard_utils.h"
 
 #include "chrome/browser/profiles/profile.h"
-#include "chrome/browser/sharing/shared_clipboard/feature_flags.h"
 #include "chrome/browser/sharing/sharing_service.h"
 #include "chrome/browser/sharing/sharing_service_factory.h"
 #include "chrome/common/pref_names.h"
@@ -14,15 +13,7 @@
 
 bool ShouldOfferSharedClipboard(content::BrowserContext* browser_context,
                                 const std::u16string& text) {
-  // Check Chrome enterprise policy for Shared Clipboard.
-  Profile* profile = Profile::FromBrowserContext(browser_context);
-  if (profile &&
-      !profile->GetPrefs()->GetBoolean(prefs::kSharedClipboardEnabled)) {
-    return false;
-  }
-
-  SharingService* sharing_service =
-      SharingServiceFactory::GetForBrowserContext(browser_context);
-  return sharing_service && base::FeatureList::IsEnabled(kSharedClipboardUI) &&
-         !text.empty();
+  // TODO(https://crbug.com/1311675): Remove this function and all its call
+  // sites.
+  return false;
 }
diff --git a/chrome/browser/sharing/shared_clipboard/shared_clipboard_utils_unittest.cc b/chrome/browser/sharing/shared_clipboard/shared_clipboard_utils_unittest.cc
index c34ead9..3e77479 100644
--- a/chrome/browser/sharing/shared_clipboard/shared_clipboard_utils_unittest.cc
+++ b/chrome/browser/sharing/shared_clipboard/shared_clipboard_utils_unittest.cc
@@ -9,7 +9,6 @@
 #include "base/strings/utf_string_conversions.h"
 #include "base/test/scoped_feature_list.h"
 #include "chrome/browser/sharing/mock_sharing_service.h"
-#include "chrome/browser/sharing/shared_clipboard/feature_flags.h"
 #include "chrome/browser/sharing/shared_clipboard/shared_clipboard_utils.h"
 #include "chrome/browser/sharing/sharing_device_source.h"
 #include "chrome/browser/sharing/sharing_fcm_handler.h"
@@ -34,7 +33,6 @@
 
 namespace {
 
-const char16_t kEmptyText[] = u"";
 const char16_t kText[] = u"Some text to copy to phone device.";
 
 class MockSharingDeviceRegistration : public SharingDeviceRegistration {
@@ -84,36 +82,8 @@
 
 }  // namespace
 
+// TODO(https://crbug.com/1311675): Remove this test and file once all the
+// shared clipboard implementation is gone.
 TEST_F(SharedClipboardUtilsTest, UIFlagDisabled_DoNotShowMenu) {
-  scoped_feature_list_.InitAndDisableFeature(kSharedClipboardUI);
-  EXPECT_FALSE(ShouldOfferSharedClipboard(&profile_, kText));
-}
-
-TEST_F(SharedClipboardUtilsTest, IncognitoProfile_DoNotShowMenu) {
-  scoped_feature_list_.InitAndEnableFeature(kSharedClipboardUI);
-  EXPECT_FALSE(ShouldOfferSharedClipboard(
-      profile_.GetPrimaryOTRProfile(/*create_if_needed=*/true), kText));
-}
-
-TEST_F(SharedClipboardUtilsTest, EmptyClipboardProtocol_DoNotShowMenu) {
-  scoped_feature_list_.InitAndEnableFeature(kSharedClipboardUI);
-  EXPECT_FALSE(ShouldOfferSharedClipboard(&profile_, kEmptyText));
-}
-
-TEST_F(SharedClipboardUtilsTest, ClipboardProtocol_ShowMenu) {
-  scoped_feature_list_.InitAndEnableFeature(kSharedClipboardUI);
-  EXPECT_TRUE(ShouldOfferSharedClipboard(&profile_, kText));
-}
-
-TEST_F(SharedClipboardUtilsTest, NoSharingService_DoNotShowMenu) {
-  scoped_feature_list_.InitAndEnableFeature(kSharedClipboardUI);
-  create_service_ = false;
-  EXPECT_FALSE(ShouldOfferSharedClipboard(&profile_, kText));
-}
-
-TEST_F(SharedClipboardUtilsTest, EnterprisePolicy_Disabled) {
-  scoped_feature_list_.InitAndEnableFeature(kSharedClipboardUI);
-  // Set the enterprise policy to false:
-  profile_.GetPrefs()->SetBoolean(prefs::kSharedClipboardEnabled, false);
   EXPECT_FALSE(ShouldOfferSharedClipboard(&profile_, kText));
 }
diff --git a/chrome/browser/sharing/sharing_device_registration.cc b/chrome/browser/sharing/sharing_device_registration.cc
index 72dd055..a28578d2 100644
--- a/chrome/browser/sharing/sharing_device_registration.cc
+++ b/chrome/browser/sharing/sharing_device_registration.cc
@@ -13,7 +13,6 @@
 #include "base/time/time.h"
 #include "build/build_config.h"
 #include "chrome/browser/sharing/buildflags.h"
-#include "chrome/browser/sharing/shared_clipboard/feature_flags.h"
 #include "chrome/browser/sharing/sharing_constants.h"
 #include "chrome/browser/sharing/sharing_device_registration_result.h"
 #include "chrome/browser/sharing/sharing_sync_preference.h"
diff --git a/chrome/browser/ui/app_list/app_service/app_service_app_item.cc b/chrome/browser/ui/app_list/app_service/app_service_app_item.cc
index 8fcd5ad..e8617c0 100644
--- a/chrome/browser/ui/app_list/app_service/app_service_app_item.cc
+++ b/chrome/browser/ui/app_list/app_service/app_service_app_item.cc
@@ -50,6 +50,7 @@
     case apps::AppType::kBuiltIn:
     case apps::AppType::kStandaloneBrowser:
     case apps::AppType::kSystemWeb:
+    case apps::AppType::kRemote:
       // Chrome, Lacros, Settings, etc. are built-in.
       return false;
     case apps::AppType::kMacOs:
@@ -61,7 +62,6 @@
     case apps::AppType::kExtension:
     case apps::AppType::kWeb:
     case apps::AppType::kPluginVm:
-    case apps::AppType::kRemote:
     case apps::AppType::kBorealis:
     case apps::AppType::kStandaloneBrowserChromeApp:
     case apps::AppType::kStandaloneBrowserExtension:
@@ -91,8 +91,15 @@
     if (app_type_ == apps::AppType::kRemote) {
       ash::RemoteAppsManager* remote_manager =
           ash::RemoteAppsManagerFactory::GetForProfile(profile);
-      if (remote_manager->ShouldAddToFront(app_update.AppId()))
+      if (remote_manager->ShouldAddToFront(app_update.AppId())) {
         SetPosition(model_updater->GetPositionBeforeFirstItem());
+      } else {
+        // Add the app at the end of the app list to preserve behavior from
+        // before productivity launcher, so the positions in which remote apps
+        // are added are consistent with old launcher order (which may be
+        // assumed by extensions using remote apps API).
+        SetPosition(model_updater->GetFirstAvailablePosition());
+      }
 
       const ash::RemoteAppsModel::AppInfo* app_info =
           remote_manager->GetAppInfo(app_update.AppId());
diff --git a/chrome/browser/ui/startup/startup_browser_creator_browsertest.cc b/chrome/browser/ui/startup/startup_browser_creator_browsertest.cc
index dc9ab5b..328ef97 100644
--- a/chrome/browser/ui/startup/startup_browser_creator_browsertest.cc
+++ b/chrome/browser/ui/startup/startup_browser_creator_browsertest.cc
@@ -657,16 +657,16 @@
     }
   }
 
-  void ExpectBlockLaunchAndLaunchAnyways(const std::string& app_id,
-                                         bool force_install_dialog,
-                                         bool tab_launch_expected) {
+  void ExpectBlockLaunch(
+      const std::string& force_installed_app_id = std::string()) {
     ASSERT_EQ(2u, chrome::GetBrowserCount(browser()->profile()));
 #if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_LINUX) || \
     BUILDFLAG(IS_FUCHSIA)
     auto waiter = views::NamedWidgetShownWaiter(
         views::test::AnyWidgetTestPasskey{},
-        force_install_dialog ? "ForceInstalledDeprecatedAppsDialogView"
-                             : "DeprecatedAppsDialogView");
+        force_installed_app_id.empty()
+            ? "DeprecatedAppsDialogView"
+            : "ForceInstalledDeprecatedAppsDialogView");
 #endif
     // Should have opened the requested homepage about:blank in 1st window.
     TabStripModel* tab_strip = browser()->tab_strip_model();
@@ -686,48 +686,15 @@
     BUILDFLAG(IS_FUCHSIA)
 
     GURL expected_url =
-        force_install_dialog
-            ? GURL(chrome::kChromeUIAppsWithForceInstalledDeprecationDialogURL +
-                   app_id)
-            : GURL(chrome::kChromeUIAppsWithDeprecationDialogURL + app_id);
+        force_installed_app_id.empty()
+            ? GURL(chrome::kChromeUIAppsWithDeprecationDialogURL)
+            : GURL(chrome::kChromeUIAppsWithForceInstalledDeprecationDialogURL +
+                   force_installed_app_id);
     EXPECT_EQ(expected_url,
               other_tab_strip->GetWebContentsAt(0)->GetVisibleURL());
 
-    std::set<Browser*> initial_browsers;
-    for (auto* initial_browser : *BrowserList::GetInstance())
-      initial_browsers.insert(initial_browser);
-
-    content::TestNavigationObserver same_tab_observer(
-        other_tab_strip->GetActiveWebContents(), 1,
-        content::MessageLoopRunner::QuitMode::DEFERRED,
-        /*ignore_uncommitted_navigations=*/false);
-
     // Verify that the Deprecated Apps Dialog View also shows up.
-    auto* dialog = waiter.WaitIfNeededAndGet();
-    EXPECT_TRUE(dialog != nullptr);
-    if (force_install_dialog) {
-      // The 'accept' option in the force-install dialog is "launch anyways".
-      dialog->widget_delegate()->AsDialogDelegate()->Accept();
-    } else {
-      // The 'cancel' option in the deprecation dialog is "launch anyways".
-      dialog->widget_delegate()->AsDialogDelegate()->Cancel();
-    }
-    if (tab_launch_expected) {
-      same_tab_observer.Wait();
-    } else {
-      Browser* app_browser =
-          ui_test_utils::GetBrowserNotInSet(initial_browsers);
-      if (!app_browser) {
-        app_browser = ui_test_utils::WaitForBrowserToOpen();
-        // The new browser should never be in |excluded_browsers|.
-        DCHECK(!base::Contains(initial_browsers, app_browser));
-      }
-      ASSERT_TRUE(app_browser);
-      TabStripModel* app_tab_strip = app_browser->tab_strip_model();
-      EXPECT_EQ(1, app_tab_strip->count());
-      EXPECT_TRUE(app_browser->is_type_app());
-      EXPECT_FALSE(app_browser->is_type_normal());
-    }
+    EXPECT_TRUE(waiter.WaitIfNeededAndGet() != nullptr);
 #endif
   }
 
@@ -775,19 +742,7 @@
       command_line, base::FilePath(), chrome::startup::IsProcessStartup::kNo,
       {browser()->profile(), StartupProfileMode::kBrowserWindow}, {}));
 
-  Browser* expected_launch_browser = browser();
-
-  if (!IsExpectedToAllowLaunch()) {
-    ExpectBlockLaunchAndLaunchAnyways(extension_app->id(),
-                                      /*force_install_dialog=*/false,
-                                      /*tab_launch_expected=*/true);
-    // When we block the launch, we always create a new browser window to
-    // display chrome://apps and the dialog.
-    ASSERT_EQ(2u, chrome::GetBrowserCount(browser()->profile()));
-    Browser* expected_launch_browser = FindOneOtherBrowser(browser());
-    tab_strip = expected_launch_browser->tab_strip_model();
-    EXPECT_EQ(1, tab_strip->count());
-  } else {
+  if (IsExpectedToAllowLaunch()) {
     // No pref was set, so the app should have opened in a tab in the existing
     // window.
     tab_waiter.Wait();
@@ -795,18 +750,20 @@
     EXPECT_EQ(2, tab_strip->count());
     EXPECT_EQ(tab_strip->GetActiveWebContents(),
               tab_strip->GetWebContentsAt(1));
+
+    // It should be a standard tabbed window, not an app window.
+    EXPECT_FALSE(browser()->is_type_app());
+    EXPECT_TRUE(browser()->is_type_normal());
+
+    // It should have loaded the requested app.
+    const std::u16string expected_title(
+        u"app_with_tab_container/empty.html title");
+    content::TitleWatcher title_watcher(tab_strip->GetActiveWebContents(),
+                                        expected_title);
+    EXPECT_EQ(expected_title, title_watcher.WaitAndGetTitle());
+  } else {
+    ExpectBlockLaunch();
   }
-
-  // It should be a standard tabbed window, not an app window.
-  EXPECT_FALSE(expected_launch_browser->is_type_app());
-  EXPECT_TRUE(expected_launch_browser->is_type_normal());
-
-  // It should have loaded the requested app.
-  const std::u16string expected_title(
-      u"app_with_tab_container/empty.html title");
-  content::TitleWatcher title_watcher(tab_strip->GetActiveWebContents(),
-                                      expected_title);
-  EXPECT_EQ(expected_title, title_watcher.WaitAndGetTitle());
 }
 
 IN_PROC_BROWSER_TEST_P(StartupBrowserCreatorChromeAppShortcutTest,
@@ -826,27 +783,23 @@
       command_line, base::FilePath(), chrome::startup::IsProcessStartup::kNo,
       {browser()->profile(), StartupProfileMode::kBrowserWindow}, {}));
 
-  if (!IsExpectedToAllowLaunch()) {
-    ExpectBlockLaunchAndLaunchAnyways(extension_app->id(),
-                                      /*force_install_dialog=*/false,
-                                      /*tab_launch_expected=*/false);
-    // When we block the launch, we always create a new browser window to
-    // display chrome://apps and the dialog, and then another to launch the app.
-    ASSERT_EQ(3u, chrome::GetBrowserCount(browser()->profile()));
+  if (IsExpectedToAllowLaunch()) {
+    // Pref was set to open in a window, so the app should have opened in a
+    // window.  The launch should have created a new browser. Find the new
+    // browser.
+    Browser* new_browser = browser_waiter.Wait();
+    ASSERT_TRUE(new_browser);
+
+    // Expect an app window.
+    EXPECT_TRUE(new_browser->is_type_app());
+
+    // The browser's app_name should include the app's ID.
+    EXPECT_NE(new_browser->app_name().find(extension_app->id()),
+              std::string::npos)
+        << new_browser->app_name();
+  } else {
+    ExpectBlockLaunch();
   }
-  // Pref was set to open in a window, so the app should have opened in a
-  // window.  The launch should have created a new browser. Find the new
-  // browser.
-  Browser* new_browser = browser_waiter.Wait();
-  ASSERT_TRUE(new_browser);
-
-  // Expect an app window.
-  EXPECT_TRUE(new_browser->is_type_app());
-
-  // The browser's app_name should include the app's ID.
-  EXPECT_NE(new_browser->app_name().find(extension_app->id()),
-            std::string::npos)
-      << new_browser->app_name();
 }
 
 IN_PROC_BROWSER_TEST_P(StartupBrowserCreatorChromeAppShortcutTest,
@@ -869,19 +822,7 @@
       command_line, base::FilePath(), chrome::startup::IsProcessStartup::kNo,
       {browser()->profile(), StartupProfileMode::kBrowserWindow}, {}));
 
-  Browser* expected_launch_browser = browser();
-
-  if (!IsExpectedToAllowLaunch()) {
-    ExpectBlockLaunchAndLaunchAnyways(extension_app->id(),
-                                      /*force_install_dialog=*/false,
-                                      /*tab_launch_expected=*/true);
-    // When we block the launch, we always create a new browser window to
-    // display chrome://apps and the dialog.
-    ASSERT_EQ(2u, chrome::GetBrowserCount(browser()->profile()));
-    Browser* expected_launch_browser = FindOneOtherBrowser(browser());
-    tab_strip = expected_launch_browser->tab_strip_model();
-    EXPECT_EQ(1, tab_strip->count());
-  } else {
+  if (IsExpectedToAllowLaunch()) {
     // When an app shortcut is open and the pref indicates a tab should open,
     // the tab is open in the existing browser window.
     tab_waiter.Wait();
@@ -889,20 +830,22 @@
     EXPECT_EQ(2, tab_strip->count());
     EXPECT_EQ(tab_strip->GetActiveWebContents(),
               tab_strip->GetWebContentsAt(1));
+
+    // The browser's app_name should not include the app's ID: it is in a normal
+    // tabbed browser.
+    EXPECT_EQ(browser()->app_name().find(extension_app->id()),
+              std::string::npos)
+        << browser()->app_name();
+
+    // It should have loaded the requested app.
+    const std::u16string expected_title(
+        u"app_with_tab_container/empty.html title");
+    content::TitleWatcher title_watcher(tab_strip->GetActiveWebContents(),
+                                        expected_title);
+    EXPECT_EQ(expected_title, title_watcher.WaitAndGetTitle());
+  } else {
+    ExpectBlockLaunch();
   }
-
-  // The browser's app_name should not include the app's ID: it is in a normal
-  // tabbed browser.
-  EXPECT_EQ(expected_launch_browser->app_name().find(extension_app->id()),
-            std::string::npos)
-      << browser()->app_name();
-
-  // It should have loaded the requested app.
-  const std::u16string expected_title(
-      u"app_with_tab_container/empty.html title");
-  content::TitleWatcher title_watcher(tab_strip->GetActiveWebContents(),
-                                      expected_title);
-  EXPECT_EQ(expected_title, title_watcher.WaitAndGetTitle());
 }
 
 IN_PROC_BROWSER_TEST_P(StartupBrowserCreatorChromeAppShortcutTest,
@@ -931,36 +874,32 @@
       command_line, base::FilePath(), chrome::startup::IsProcessStartup::kNo,
       {browser()->profile(), StartupProfileMode::kBrowserWindow}, {}));
 
-  Browser* expected_launch_browser = browser();
-
-  if (!IsExpectedToAllowLaunch()) {
-    ExpectBlockLaunchAndLaunchAnyways(extension_app->id(), true, true);
-    // When we block the launch, we always create a new browser window to
-    // display chrome://apps and the dialog.
-    ASSERT_EQ(2u, chrome::GetBrowserCount(browser()->profile()));
-    Browser* expected_launch_browser = FindOneOtherBrowser(browser());
-    tab_strip = expected_launch_browser->tab_strip_model();
-    EXPECT_EQ(1, tab_strip->count());
-  } else {
+  if (IsExpectedToAllowLaunch()) {
     tab_waiter.Wait();
-    ASSERT_EQ(1u, chrome::GetBrowserCount(browser()->profile()));
+
+    // Policy force-installed app should be allowed regardless of Chrome App
+    // Deprecation status.
+    //
     // No app launch pref was set, so the app should have opened in a tab in the
     // existing window.
+    ASSERT_EQ(1u, chrome::GetBrowserCount(browser()->profile()));
     EXPECT_EQ(2, tab_strip->count());
     EXPECT_EQ(tab_strip->GetActiveWebContents(),
               tab_strip->GetWebContentsAt(1));
+
+    // It should be a standard tabbed window, not an app window.
+    EXPECT_FALSE(browser()->is_type_app());
+    EXPECT_TRUE(browser()->is_type_normal());
+
+    // It should have loaded the requested app.
+    const std::u16string expected_title(
+        u"app_with_tab_container/empty.html title");
+    content::TitleWatcher title_watcher(tab_strip->GetActiveWebContents(),
+                                        expected_title);
+    EXPECT_EQ(expected_title, title_watcher.WaitAndGetTitle());
+  } else {
+    ExpectBlockLaunch(extension_app->id());
   }
-
-  // It should be a standard tabbed window, not an app window.
-  EXPECT_FALSE(expected_launch_browser->is_type_app());
-  EXPECT_TRUE(expected_launch_browser->is_type_normal());
-
-  // It should have loaded the requested app.
-  const std::u16string expected_title(
-      u"app_with_tab_container/empty.html title");
-  content::TitleWatcher title_watcher(tab_strip->GetActiveWebContents(),
-                                      expected_title);
-  EXPECT_EQ(expected_title, title_watcher.WaitAndGetTitle());
 }
 
 INSTANTIATE_TEST_SUITE_P(
diff --git a/chrome/browser/ui/tab_dialogs.h b/chrome/browser/ui/tab_dialogs.h
index 2b3732e..3b7f8ec 100644
--- a/chrome/browser/ui/tab_dialogs.h
+++ b/chrome/browser/ui/tab_dialogs.h
@@ -52,16 +52,13 @@
 
   // Shows the deprecated app dialog.
   virtual void ShowDeprecatedAppsDialog(
-      const extensions::ExtensionId& optional_launched_extension_id,
       const std::set<extensions::ExtensionId>& deprecated_app_ids,
-      content::WebContents* web_contents,
-      base::OnceClosure launch_anyways) = 0;
+      content::WebContents* web_contents) = 0;
 
   // Shows the force installed and deprecated app dialog.
   virtual void ShowForceInstalledDeprecatedAppsDialog(
       const extensions::ExtensionId& app_id,
-      content::WebContents* web_contents,
-      base::OnceClosure launch_anyways) = 0;
+      content::WebContents* web_contents) = 0;
 
   // Shows or hides the ManagePasswords bubble.
   // Pass true for |user_action| if this is a user initiated action.
diff --git a/chrome/browser/ui/views/location_bar/location_bar_view.cc b/chrome/browser/ui/views/location_bar/location_bar_view.cc
index fbd35fd..69240304 100644
--- a/chrome/browser/ui/views/location_bar/location_bar_view.cc
+++ b/chrome/browser/ui/views/location_bar/location_bar_view.cc
@@ -31,7 +31,6 @@
 #include "chrome/browser/search_engines/template_url_service_factory.h"
 #include "chrome/browser/send_tab_to_self/send_tab_to_self_util.h"
 #include "chrome/browser/sharing/features.h"
-#include "chrome/browser/sharing/shared_clipboard/feature_flags.h"
 #include "chrome/browser/sharing/sms/sms_flags.h"
 #include "chrome/browser/sharing_hub/sharing_hub_features.h"
 #include "chrome/browser/ssl/security_state_tab_helper.h"
@@ -307,8 +306,6 @@
     params.types_enabled.push_back(PageActionIconType::kSendTabToSelf);
     params.types_enabled.push_back(PageActionIconType::kClickToCall);
     params.types_enabled.push_back(PageActionIconType::kQRCodeGenerator);
-    if (base::FeatureList::IsEnabled(kSharedClipboardUI))
-      params.types_enabled.push_back(PageActionIconType::kSharedClipboard);
     if (base::FeatureList::IsEnabled(kWebOTPCrossDevice))
       params.types_enabled.push_back(PageActionIconType::kSmsRemoteFetcher);
     if (!base::FeatureList::IsEnabled(
diff --git a/chrome/browser/ui/views/profiles/profile_picker_view_browsertest.cc b/chrome/browser/ui/views/profiles/profile_picker_view_browsertest.cc
index 5d31ad4..e2484c78 100644
--- a/chrome/browser/ui/views/profiles/profile_picker_view_browsertest.cc
+++ b/chrome/browser/ui/views/profiles/profile_picker_view_browsertest.cc
@@ -281,14 +281,11 @@
   void ShowManagePasswordsBubble(bool user_action) override {}
   void HideManagePasswordsBubble() override {}
   void ShowDeprecatedAppsDialog(
-      const extensions::ExtensionId& optional_launched_extension_id,
       const std::set<extensions::ExtensionId>& deprecated_app_ids,
-      content::WebContents* web_contents,
-      base::OnceClosure launch_anyways) override {}
+      content::WebContents* web_contents) override {}
   void ShowForceInstalledDeprecatedAppsDialog(
       const extensions::ExtensionId& app_id,
-      content::WebContents* web_contents,
-      base::OnceClosure launch_anyways) override {}
+      content::WebContents* web_contents) override {}
 
  private:
   raw_ptr<content::WebContents> contents_;
@@ -437,11 +434,11 @@
     WaitForLoadStop(kNewProfileUrl);
 
     // Fake clicking the "Next"/"Sign in" button.
-    base::ListValue args;
+    base::Value::List args;
     args.Append(/*color=*/static_cast<int>(kProfileColor));
-    args.Append(/*gaiaId=*/base::Value(base::Value::Type::STRING));
+    args.Append(/*gaiaId=*/std::string());
     web_contents()->GetWebUI()->ProcessWebUIMessage(
-        kNewProfileUrl, "selectAccountLacros", args);
+        kNewProfileUrl, "selectAccountLacros", std::move(args));
 
     // Wait for the Ash UI to show up.
     FakeAccountManagerUI* fake_ui = GetFakeAccountManagerUI();
diff --git a/chrome/browser/ui/views/sharing/shared_clipboard_browsertest.cc b/chrome/browser/ui/views/sharing/shared_clipboard_browsertest.cc
index 0e5b3bd..70d3988 100644
--- a/chrome/browser/ui/views/sharing/shared_clipboard_browsertest.cc
+++ b/chrome/browser/ui/views/sharing/shared_clipboard_browsertest.cc
@@ -11,7 +11,6 @@
 #include "chrome/app/chrome_command_ids.h"
 #include "chrome/browser/renderer_context_menu/render_view_context_menu_test_util.h"
 #include "chrome/browser/sharing/features.h"
-#include "chrome/browser/sharing/shared_clipboard/feature_flags.h"
 #include "chrome/browser/sharing/sharing_constants.h"
 #include "chrome/browser/sharing/sharing_metrics.h"
 #include "chrome/browser/sharing/sharing_sync_preference.h"
@@ -53,113 +52,8 @@
   base::test::ScopedFeatureList feature_list_;
 };
 
-class SharedClipboardBrowserTest : public SharedClipboardBrowserTestBase {
- public:
-  SharedClipboardBrowserTest() {
-    feature_list_.InitAndEnableFeature(kSharedClipboardUI);
-  }
-};
-
-IN_PROC_BROWSER_TEST_F(SharedClipboardBrowserTest, ContextMenu_SingleDevice) {
-  Init(sync_pb::SharingSpecificFields::SHARED_CLIPBOARD_V2,
-       sync_pb::SharingSpecificFields::UNKNOWN);
-  auto devices = sharing_service()->GetDeviceCandidates(
-      sync_pb::SharingSpecificFields::SHARED_CLIPBOARD_V2);
-  ASSERT_EQ(1u, devices.size());
-
-  std::unique_ptr<TestRenderViewContextMenu> menu =
-      InitContextMenu(GURL(), "", kSelectedText);
-  ASSERT_TRUE(menu->IsItemPresent(
-      IDC_CONTENT_CONTEXT_SHARING_SHARED_CLIPBOARD_SINGLE_DEVICE));
-  ASSERT_FALSE(menu->IsItemPresent(
-      IDC_CONTENT_CONTEXT_SHARING_SHARED_CLIPBOARD_MULTIPLE_DEVICES));
-
-  menu->ExecuteCommand(
-      IDC_CONTENT_CONTEXT_SHARING_SHARED_CLIPBOARD_SINGLE_DEVICE, 0);
-  CheckLastReceiver(*devices[0]);
-  CheckLastSharingMessageSent(kSelectedText);
-}
-
-IN_PROC_BROWSER_TEST_F(SharedClipboardBrowserTest,
-                       ContextMenu_MultipleDevices) {
-  Init(sync_pb::SharingSpecificFields::SHARED_CLIPBOARD_V2,
-       sync_pb::SharingSpecificFields::SHARED_CLIPBOARD_V2);
-  auto devices = sharing_service()->GetDeviceCandidates(
-      sync_pb::SharingSpecificFields::SHARED_CLIPBOARD_V2);
-  ASSERT_EQ(2u, devices.size());
-
-  std::unique_ptr<TestRenderViewContextMenu> menu =
-      InitContextMenu(GURL(), "", kSelectedText);
-  ASSERT_FALSE(menu->IsItemPresent(
-      IDC_CONTENT_CONTEXT_SHARING_SHARED_CLIPBOARD_SINGLE_DEVICE));
-  ASSERT_TRUE(menu->IsItemPresent(
-      IDC_CONTENT_CONTEXT_SHARING_SHARED_CLIPBOARD_MULTIPLE_DEVICES));
-
-  ui::MenuModel* sub_menu_model = nullptr;
-  int device_id = -1;
-  ASSERT_TRUE(menu->GetMenuModelAndItemIndex(kSubMenuFirstDeviceCommandId,
-                                             &sub_menu_model, &device_id));
-  EXPECT_EQ(2, sub_menu_model->GetItemCount());
-  EXPECT_EQ(0, device_id);
-
-  for (auto& device : devices) {
-    EXPECT_EQ(kSubMenuFirstDeviceCommandId + device_id,
-              sub_menu_model->GetCommandIdAt(device_id));
-    sub_menu_model->ActivatedAt(device_id);
-
-    CheckLastReceiver(*device);
-    CheckLastSharingMessageSent(kSelectedText);
-    device_id++;
-  }
-}
-
-IN_PROC_BROWSER_TEST_F(SharedClipboardBrowserTest, ContextMenu_NoDevices) {
-  Init(sync_pb::SharingSpecificFields::UNKNOWN,
-       sync_pb::SharingSpecificFields::UNKNOWN);
-  auto devices = sharing_service()->GetDeviceCandidates(
-      sync_pb::SharingSpecificFields::SHARED_CLIPBOARD_V2);
-  ASSERT_EQ(0u, devices.size());
-
-  std::unique_ptr<TestRenderViewContextMenu> menu =
-      InitContextMenu(GURL(), "", kSelectedText);
-  ASSERT_FALSE(menu->IsItemPresent(
-      IDC_CONTENT_CONTEXT_SHARING_SHARED_CLIPBOARD_SINGLE_DEVICE));
-  ASSERT_FALSE(menu->IsItemPresent(
-      IDC_CONTENT_CONTEXT_SHARING_SHARED_CLIPBOARD_MULTIPLE_DEVICES));
-}
-
-IN_PROC_BROWSER_TEST_F(SharedClipboardBrowserTest, ContextMenu_SyncTurnedOff) {
-  if (base::FeatureList::IsEnabled(kSharingSendViaSync)) {
-    // Turning off sync will have no effect when Shared Clipboard is available
-    // on sign-in.
-    return;
-  }
-
-  Init(sync_pb::SharingSpecificFields::SHARED_CLIPBOARD_V2,
-       sync_pb::SharingSpecificFields::UNKNOWN);
-  auto devices = sharing_service()->GetDeviceCandidates(
-      sync_pb::SharingSpecificFields::SHARED_CLIPBOARD_V2);
-  ASSERT_EQ(1u, devices.size());
-
-  // Disable syncing preferences which is necessary for Sharing.
-  GetSyncService(0)->GetUserSettings()->SetSelectedTypes(false, {});
-  ASSERT_TRUE(AwaitQuiescence());
-
-  std::unique_ptr<TestRenderViewContextMenu> menu =
-      InitContextMenu(GURL(), "", kSelectedText);
-  ASSERT_FALSE(menu->IsItemPresent(
-      IDC_CONTENT_CONTEXT_SHARING_SHARED_CLIPBOARD_SINGLE_DEVICE));
-  ASSERT_FALSE(menu->IsItemPresent(
-      IDC_CONTENT_CONTEXT_SHARING_SHARED_CLIPBOARD_MULTIPLE_DEVICES));
-}
-
-class SharedClipboardUIFeatureDisabledBrowserTest
-    : public SharedClipboardBrowserTestBase {
- public:
-  SharedClipboardUIFeatureDisabledBrowserTest() {
-    feature_list_.InitAndDisableFeature(kSharedClipboardUI);
-  }
-};
+using SharedClipboardUIFeatureDisabledBrowserTest =
+    SharedClipboardBrowserTestBase;
 
 IN_PROC_BROWSER_TEST_F(SharedClipboardUIFeatureDisabledBrowserTest,
                        ContextMenu_UIFeatureDisabled) {
diff --git a/chrome/browser/ui/views/side_panel/user_note/user_note_ui_coordinator.cc b/chrome/browser/ui/views/side_panel/user_note/user_note_ui_coordinator.cc
index 962fbfc7..ab76270 100644
--- a/chrome/browser/ui/views/side_panel/user_note/user_note_ui_coordinator.cc
+++ b/chrome/browser/ui/views/side_panel/user_note/user_note_ui_coordinator.cc
@@ -241,8 +241,8 @@
     TabStripModel* tab_strip_model,
     const TabStripModelChange& change,
     const TabStripSelectionChange& selection) {
-  if (selection.active_tab_changed())
-    Invalidate();
+  // TODO(cheickcisse): Call invalidate on active tab changed. It seems to
+  // fail when the tab is deleted.
 }
 
 std::unique_ptr<views::View> UserNoteUICoordinator::CreateUserNotesView() {
diff --git a/chrome/browser/ui/views/tab_dialogs_views.cc b/chrome/browser/ui/views/tab_dialogs_views.cc
index 6f520cc..26a47d3 100644
--- a/chrome/browser/ui/views/tab_dialogs_views.cc
+++ b/chrome/browser/ui/views/tab_dialogs_views.cc
@@ -80,23 +80,19 @@
 }
 
 void TabDialogsViews::ShowDeprecatedAppsDialog(
-    const extensions::ExtensionId& optional_launched_extension_id,
     const std::set<extensions::ExtensionId>& deprecated_app_ids,
-    content::WebContents* web_contents,
-    base::OnceClosure launch_anyways) {
+    content::WebContents* web_contents) {
 #if defined(TOOLKIT_VIEWS) && !BUILDFLAG(IS_CHROMEOS)
-  DeprecatedAppsDialogView::CreateAndShowDialog(
-      optional_launched_extension_id, deprecated_app_ids, web_contents,
-      std::move(launch_anyways));
+  DeprecatedAppsDialogView::CreateAndShowDialog(deprecated_app_ids,
+                                                web_contents);
 #endif
 }
 
 void TabDialogsViews::ShowForceInstalledDeprecatedAppsDialog(
     const extensions::ExtensionId& app_id,
-    content::WebContents* web_contents,
-    base::OnceClosure launch_anyways) {
+    content::WebContents* web_contents) {
 #if defined(TOOLKIT_VIEWS) && !BUILDFLAG(IS_CHROMEOS)
-  ForceInstalledDeprecatedAppsDialogView::CreateAndShowDialog(
-      app_id, web_contents, std::move(launch_anyways));
+  ForceInstalledDeprecatedAppsDialogView::CreateAndShowDialog(app_id,
+                                                              web_contents);
 #endif
 }
diff --git a/chrome/browser/ui/views/tab_dialogs_views.h b/chrome/browser/ui/views/tab_dialogs_views.h
index de2c205..3dc1d0d 100644
--- a/chrome/browser/ui/views/tab_dialogs_views.h
+++ b/chrome/browser/ui/views/tab_dialogs_views.h
@@ -30,14 +30,11 @@
   void ShowManagePasswordsBubble(bool user_action) override;
   void HideManagePasswordsBubble() override;
   void ShowDeprecatedAppsDialog(
-      const extensions::ExtensionId& optional_launched_extension_id,
       const std::set<extensions::ExtensionId>& deprecated_app_ids,
-      content::WebContents* web_contents,
-      base::OnceClosure launch_anyways) override;
+      content::WebContents* web_contents) override;
   void ShowForceInstalledDeprecatedAppsDialog(
       const extensions::ExtensionId& app_id,
-      content::WebContents* web_contents,
-      base::OnceClosure launch_anyways) override;
+      content::WebContents* web_contents) override;
 
  private:
   raw_ptr<content::WebContents> web_contents_;  // Weak. Owns this.
diff --git a/chrome/browser/ui/views/web_apps/deprecated_apps_dialog_view.cc b/chrome/browser/ui/views/web_apps/deprecated_apps_dialog_view.cc
index d0866a9..cafc58b 100644
--- a/chrome/browser/ui/views/web_apps/deprecated_apps_dialog_view.cc
+++ b/chrome/browser/ui/views/web_apps/deprecated_apps_dialog_view.cc
@@ -111,13 +111,10 @@
 
 // static
 DeprecatedAppsDialogView* DeprecatedAppsDialogView::CreateAndShowDialog(
-    const extensions::ExtensionId& optional_launched_extension_id,
     const std::set<extensions::ExtensionId>& deprecated_app_ids,
-    content::WebContents* web_contents,
-    base::OnceClosure launch_anyways) {
-  DeprecatedAppsDialogView* view = new DeprecatedAppsDialogView(
-      optional_launched_extension_id, deprecated_app_ids, web_contents,
-      std::move(launch_anyways));
+    content::WebContents* web_contents) {
+  DeprecatedAppsDialogView* view =
+      new DeprecatedAppsDialogView(deprecated_app_ids, web_contents);
   view->InitDialog();
   constrained_window::ShowWebModalDialogViews(view, web_contents);
   return view;
@@ -128,42 +125,15 @@
 }
 
 std::u16string DeprecatedAppsDialogView::GetWindowTitle() const {
-  if (launched_extension_name_) {
-    return l10n_util::GetStringFUTF16(
-        IDS_DEPRECATED_APPS_RENDERER_TITLE_WITH_APP_NAME,
-        launched_extension_name_.value());
-  }
-  if (single_app_name_) {
-    return l10n_util::GetStringFUTF16(
-        IDS_DEPRECATED_APPS_RENDERER_TITLE_WITH_APP_NAME,
-        single_app_name_.value());
-  }
-  return l10n_util::GetStringFUTF16Int(
-      IDS_DEPRECATED_APPS_RENDERER_TITLE_PLURAL,
+  return l10n_util::GetPluralStringFUTF16(
+      IDS_DEPRECATED_APPS_RENDERER_TITLE,
       deprecated_apps_table_model_->RowCount());
 }
 
 DeprecatedAppsDialogView::DeprecatedAppsDialogView(
-    const extensions::ExtensionId& optional_launched_extension_id,
     const std::set<extensions::ExtensionId>& deprecated_app_ids,
-    content::WebContents* web_contents,
-    base::OnceClosure launch_anyways)
-    : deprecated_app_ids_(deprecated_app_ids),
-      launch_anyways_(std::move(launch_anyways)),
-      web_contents_(web_contents) {
-  if (!optional_launched_extension_id.empty()) {
-    const extensions::Extension* extension =
-        extensions::ExtensionRegistry::Get(web_contents_->GetBrowserContext())
-            ->GetInstalledExtension(optional_launched_extension_id);
-    launched_extension_name_ = base::UTF8ToUTF16(extension->name());
-  }
-  if (deprecated_app_ids_.size() == 1) {
-    const extensions::Extension* extension =
-        extensions::ExtensionRegistry::Get(web_contents_->GetBrowserContext())
-            ->GetInstalledExtension(*deprecated_app_ids_.begin());
-    DCHECK(extension);
-    single_app_name_ = base::UTF8ToUTF16(extension->name());
-  }
+    content::WebContents* web_contents)
+    : deprecated_app_ids_(deprecated_app_ids), web_contents_(web_contents) {
   deprecated_apps_table_model_ = std::make_unique<DeprecatedAppsTableModel>(
       deprecated_app_ids, web_contents,
       base::BindRepeating(&DeprecatedAppsDialogView::OnIconsLoadedForTable,
@@ -183,29 +153,24 @@
           views::DISTANCE_UNRELATED_CONTROL_VERTICAL)));
   set_fixed_width(views::LayoutProvider::Get()->GetDistanceMetric(
       views::DISTANCE_MODAL_DIALOG_PREFERRED_WIDTH));
+
   // Set up buttons.
   SetButtonLabel(ui::DIALOG_BUTTON_OK,
                  l10n_util::GetPluralStringFUTF16(
                      IDS_DEPRECATED_APPS_OK_LABEL,
                      deprecated_apps_table_model_->RowCount()));
-  SetAcceptCallback(base::BindOnce(&DeprecatedAppsDialogView::OnAccept,
+  SetButtonLabel(ui::DIALOG_BUTTON_CANCEL,
+                 l10n_util::GetStringUTF16(IDS_DEPRECATED_APPS_CANCEL_LABEL));
+  SetDefaultButton(ui::DIALOG_BUTTON_NONE);
+  SetCancelCallback(base::BindOnce(&DeprecatedAppsDialogView::CloseDialog,
                                    base::Unretained(this)));
+  SetAcceptCallback(base::BindOnce(
+      &DeprecatedAppsDialogView::UninstallExtensions, base::Unretained(this)));
 
-  if (launched_extension_name_) {
-    SetButtonLabel(
-        ui::DIALOG_BUTTON_CANCEL,
-        l10n_util::GetStringUTF16(IDS_DEPRECATED_APPS_LAUNCH_ANYWAY_LABEL));
-  } else {
-    SetButtonLabel(ui::DIALOG_BUTTON_CANCEL,
-                   l10n_util::GetStringUTF16(IDS_DEPRECATED_APPS_CANCEL_LABEL));
-  }
-  SetCancelCallback(base::BindOnce(&DeprecatedAppsDialogView::OnCancel,
-                                   base::Unretained(this)));
-
-  SetDefaultButton(ui::DIALOG_BUTTON_OK);
-
-  info_label_ = AddChildView(std::make_unique<views::Label>(
-      l10n_util::GetStringUTF16(IDS_DEPRECATED_APPS_MONITOR_RENDERER)));
+  info_label_ = AddChildView(
+      std::make_unique<views::Label>(l10n_util::GetPluralStringFUTF16(
+          IDS_DEPRECATED_APPS_MONITOR_RENDERER,
+          deprecated_apps_table_model_->RowCount())));
   info_label_->SetMultiLine(true);
   info_label_->SetHorizontalAlignment(gfx::ALIGN_LEFT);
 
@@ -221,8 +186,8 @@
             ui::PAGE_TRANSITION_LINK, /*is_renderer_initiated=*/false));
       },
       web_contents_));
-  learn_more->SetAccessibleName(
-      l10n_util::GetStringUTF16(IDS_DEPRECATED_APPS_LEARN_MORE_AX_LABEL));
+  learn_more->SetAccessibleName(l10n_util::GetStringUTF16(
+      IDS_FORCE_INSTALLED_DEPRECATED_APPS_LEARN_MORE_AX_LABEL));
   learn_more->SetHorizontalAlignment(gfx::ALIGN_LEFT);
 
   // Set up the table view.
@@ -253,7 +218,7 @@
   deprecated_apps_table_view_->SchedulePaint();
 }
 
-void DeprecatedAppsDialogView::OnAccept() {
+void DeprecatedAppsDialogView::UninstallExtensions() {
   for (extensions::ExtensionId id : deprecated_app_ids_) {
     extensions::ExtensionSystem::Get(web_contents_->GetBrowserContext())
         ->extension_service()
@@ -263,10 +228,5 @@
   CloseDialog();
 }
 
-void DeprecatedAppsDialogView::OnCancel() {
-  std::move(launch_anyways_).Run();
-  CloseDialog();
-}
-
 BEGIN_METADATA(DeprecatedAppsDialogView, views::DialogDelegateView)
 END_METADATA
diff --git a/chrome/browser/ui/views/web_apps/deprecated_apps_dialog_view.h b/chrome/browser/ui/views/web_apps/deprecated_apps_dialog_view.h
index 4fce17c..9cc73c9 100644
--- a/chrome/browser/ui/views/web_apps/deprecated_apps_dialog_view.h
+++ b/chrome/browser/ui/views/web_apps/deprecated_apps_dialog_view.h
@@ -7,13 +7,11 @@
 
 #include <memory>
 #include <set>
-#include <string>
 #include <vector>
 
 #include "base/callback_forward.h"
 #include "base/memory/raw_ptr.h"
 #include "base/memory/weak_ptr.h"
-#include "chrome/browser/ui/tab_dialogs.h"
 #include "extensions/browser/extension_registry.h"
 #include "extensions/browser/extension_system.h"
 #include "extensions/common/extension_id.h"
@@ -42,20 +40,10 @@
   DeprecatedAppsDialogView& operator=(const DeprecatedAppsDialogView&) = delete;
   ~DeprecatedAppsDialogView() override;
 
-  // Create the dialog metadata and show it. Some behavior specializations:
-  // * If the `optional_launched_extension_id` is passed, then the dialog will
-  //   show the name of that chrome app in the title.
-  // * If `optional_launched_extension_id` is empty and `deprecated_app_ids`
-  //   only has one entry, then the dialog will display the name of the one
-  //   deprecated chrome app.
-  // * If `optional_launched_extension_id` is empty and `deprecated_app_ids` has
-  //   more than one entry, then the title will just contain the number of
-  //   deprecated chrome apps.
+  // Create the dialog metadata and show it.
   static DeprecatedAppsDialogView* CreateAndShowDialog(
-      const extensions::ExtensionId& optional_launched_extension_id,
       const std::set<extensions::ExtensionId>& deprecated_app_ids,
-      content::WebContents* web_contents,
-      base::OnceClosure launch_anyways);
+      content::WebContents* web_contents);
 
   base::WeakPtr<DeprecatedAppsDialogView> AsWeakPtr();
 
@@ -67,10 +55,8 @@
  private:
   class DeprecatedAppsTableModel;
   DeprecatedAppsDialogView(
-      const extensions::ExtensionId& optional_launched_extension_id,
       const std::set<extensions::ExtensionId>& deprecated_app_ids,
-      content::WebContents* web_contents,
-      base::OnceClosure launch_anyways);
+      content::WebContents* web_contents);
 
   // Initialize the dialog when the object is instantiated.
   void InitDialog();
@@ -83,8 +69,7 @@
 
   // Callback that runs when accept button is clicked to
   // uninstall all extensions.
-  void OnAccept();
-  void OnCancel();
+  void UninstallExtensions();
 
   // Controls the table view within the dialog box.
   raw_ptr<views::TableView> deprecated_apps_table_view_;
@@ -94,10 +79,7 @@
 
   raw_ptr<views::Label> info_label_;
 
-  absl::optional<std::u16string> launched_extension_name_;
   std::set<extensions::ExtensionId> deprecated_app_ids_;
-  absl::optional<std::u16string> single_app_name_;
-  base::OnceClosure launch_anyways_;
 
   raw_ptr<content::WebContents> web_contents_;
 
diff --git a/chrome/browser/ui/views/web_apps/deprecated_apps_dialog_view_browsertest.cc b/chrome/browser/ui/views/web_apps/deprecated_apps_dialog_view_browsertest.cc
index 207bc1d..7e90e04 100644
--- a/chrome/browser/ui/views/web_apps/deprecated_apps_dialog_view_browsertest.cc
+++ b/chrome/browser/ui/views/web_apps/deprecated_apps_dialog_view_browsertest.cc
@@ -2,7 +2,6 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "base/callback_helpers.h"
 #include "chrome/browser/ui/views/web_apps/deprecated_apps_dialog_view.h"
 
 #include <set>
@@ -10,7 +9,6 @@
 #include "base/feature_list.h"
 #include "base/run_loop.h"
 #include "base/test/bind.h"
-#include "base/test/mock_callback.h"
 #include "chrome/browser/extensions/extension_browsertest.h"
 #include "chrome/browser/platform_util.h"
 #include "chrome/browser/ui/browser.h"
@@ -152,8 +150,7 @@
 
   InstallExtensionForTesting(mock_app_manifest1, mock_url1);
   test_dialog_view_ = DeprecatedAppsDialogView::CreateAndShowDialog(
-                          std::string(), deprecated_app_ids_for_testing_,
-                          web_contents, base::DoNothing())
+                          deprecated_app_ids_for_testing_, web_contents)
                           ->AsWeakPtr();
 
   EXPECT_TRUE(IsDialogShown());
@@ -168,8 +165,7 @@
   InstallExtensionForTesting(mock_app_manifest1, mock_url1);
   InstallExtensionForTesting(mock_app_manifest2, mock_url2);
   test_dialog_view_ = DeprecatedAppsDialogView::CreateAndShowDialog(
-                          std::string(), deprecated_app_ids_for_testing_,
-                          web_contents, base::DoNothing())
+                          deprecated_app_ids_for_testing_, web_contents)
                           ->AsWeakPtr();
 
   EXPECT_TRUE(IsDialogShown());
@@ -181,14 +177,11 @@
                        AcceptDialogAndVerify) {
   auto* web_contents = browser()->tab_strip_model()->GetActiveWebContents();
 
-  base::MockCallback<base::OnceClosure> mock_callback;
   extensions::ExtensionId test_id(
       InstallExtensionForTesting(mock_app_manifest1, mock_url1));
   test_dialog_view_ = DeprecatedAppsDialogView::CreateAndShowDialog(
-                          std::string(), deprecated_app_ids_for_testing_,
-                          web_contents, mock_callback.Get())
+                          deprecated_app_ids_for_testing_, web_contents)
                           ->AsWeakPtr();
-  EXPECT_CALL(mock_callback, Run()).Times(0);
 
   // Verify dialog is shown.
   ASSERT_TRUE(IsDialogShown());
@@ -210,11 +203,9 @@
                        CloseDialogAndVerify) {
   auto* web_contents = browser()->tab_strip_model()->GetActiveWebContents();
 
-  base::MockCallback<base::OnceClosure> mock_callback;
   InstallExtensionForTesting(mock_app_manifest1, mock_url1);
   test_dialog_view_ = DeprecatedAppsDialogView::CreateAndShowDialog(
-                          std::string(), deprecated_app_ids_for_testing_,
-                          web_contents, mock_callback.Get())
+                          deprecated_app_ids_for_testing_, web_contents)
                           ->AsWeakPtr();
 
   // Verify dialog is shown.
@@ -222,8 +213,6 @@
   EXPECT_EQ(static_cast<int>(deprecated_app_ids_for_testing_.size()),
             GetRowCountForDialog());
 
-  EXPECT_CALL(mock_callback, Run()).Times(1);
-
   // Verify dialog is closed on cancellation
   ASSERT_TRUE(test_dialog_view_->Cancel());
   WaitForDialogToBeDestroyed();
diff --git a/chrome/browser/ui/views/web_apps/force_installed_deprecated_apps_dialog_view.cc b/chrome/browser/ui/views/web_apps/force_installed_deprecated_apps_dialog_view.cc
index d0b0236..49868a3 100644
--- a/chrome/browser/ui/views/web_apps/force_installed_deprecated_apps_dialog_view.cc
+++ b/chrome/browser/ui/views/web_apps/force_installed_deprecated_apps_dialog_view.cc
@@ -13,7 +13,6 @@
 #include "extensions/browser/extension_registry.h"
 #include "ui/base/l10n/l10n_util.h"
 #include "ui/base/metadata/metadata_impl_macros.h"
-#include "ui/base/ui_base_types.h"
 #include "ui/base/window_open_disposition.h"
 #include "ui/views/controls/link.h"
 #include "ui/views/controls/styled_label.h"
@@ -24,10 +23,9 @@
 // static
 void ForceInstalledDeprecatedAppsDialogView::CreateAndShowDialog(
     extensions::ExtensionId app_id,
-    content::WebContents* web_contents,
-    base::OnceClosure launch_anyways) {
+    content::WebContents* web_contents) {
   auto delegate = std::make_unique<views::DialogDelegate>();
-  //   delegate->SetButtons(ui::DIALOG_BUTTON_OK);
+  delegate->SetButtons(ui::DIALOG_BUTTON_OK);
   delegate->SetModalType(ui::MODAL_TYPE_CHILD);
   delegate->SetShowCloseButton(false);
   delegate->SetOwnedByWidget(true);
@@ -36,16 +34,18 @@
       extensions::ExtensionRegistry::Get(browser_context)
           ->GetInstalledExtension(app_id);
   std::u16string app_name = base::UTF8ToUTF16(extension->name());
-  delegate->SetTitle(l10n_util::GetStringFUTF16(
-      IDS_DEPRECATED_APPS_RENDERER_TITLE_WITH_APP_NAME, app_name));
-  delegate->SetButtonLabel(
-      ui::DIALOG_BUTTON_OK,
-      l10n_util::GetStringUTF16(IDS_DEPRECATED_APPS_LAUNCH_ANYWAY_LABEL));
-  delegate->SetAcceptCallback(std::move(launch_anyways));
-
+  bool is_preinstalled_app = extensions::IsPreinstalledAppId(app_id);
+  delegate->SetTitle(
+      is_preinstalled_app
+          ? l10n_util::GetStringFUTF16(
+                IDS_FORCE_INSTALLED_PREINSTALLED_DEPRECATED_APPS_TITLE,
+                app_name)
+          : l10n_util::GetPluralStringFUTF16(IDS_DEPRECATED_APPS_RENDERER_TITLE,
+                                             1));
   delegate->SetContentsView(
       base::WrapUnique<ForceInstalledDeprecatedAppsDialogView>(
-          new ForceInstalledDeprecatedAppsDialogView(app_name, web_contents)));
+          new ForceInstalledDeprecatedAppsDialogView(
+              app_name, is_preinstalled_app, web_contents)));
   delegate->set_fixed_width(views::LayoutProvider::Get()->GetDistanceMetric(
       views::DISTANCE_MODAL_DIALOG_PREFERRED_WIDTH));
   delegate->set_margins(
@@ -56,6 +56,7 @@
 
 ForceInstalledDeprecatedAppsDialogView::ForceInstalledDeprecatedAppsDialogView(
     std::u16string app_name,
+    bool is_preinstalled_app,
     content::WebContents* web_contents)
     : app_name_(app_name), web_contents_(web_contents) {
   SetLayoutManager(std::make_unique<views::BoxLayout>(
@@ -63,7 +64,11 @@
       ChromeLayoutProvider::Get()->GetDistanceMetric(
           views::DISTANCE_RELATED_CONTROL_VERTICAL)));
   auto* info_label = AddChildView(std::make_unique<views::Label>(
-      l10n_util::GetStringUTF16(IDS_FORCE_INSTALLED_DEPRECATED_APPS_CONTENT)));
+      is_preinstalled_app
+          ? l10n_util::GetStringUTF16(
+                IDS_FORCE_INSTALLED_PREINSTALLED_DEPRECATED_APPS_CONTENT)
+          : l10n_util::GetStringFUTF16(
+                IDS_FORCE_INSTALLED_DEPRECATED_APPS_CONTENT, app_name_)));
   info_label->SetMultiLine(true);
   info_label->SetHorizontalAlignment(gfx::ALIGN_LEFT);
 
@@ -79,8 +84,8 @@
             ui::PAGE_TRANSITION_LINK, /*is_renderer_initiated=*/false));
       },
       web_contents_));
-  learn_more->SetAccessibleName(
-      l10n_util::GetStringUTF16(IDS_DEPRECATED_APPS_LEARN_MORE_AX_LABEL));
+  learn_more->SetAccessibleName(l10n_util::GetStringUTF16(
+      IDS_FORCE_INSTALLED_DEPRECATED_APPS_LEARN_MORE_AX_LABEL));
   learn_more->SetHorizontalAlignment(gfx::ALIGN_LEFT);
 }
 
diff --git a/chrome/browser/ui/views/web_apps/force_installed_deprecated_apps_dialog_view.h b/chrome/browser/ui/views/web_apps/force_installed_deprecated_apps_dialog_view.h
index 21feadf8..2c32b644 100644
--- a/chrome/browser/ui/views/web_apps/force_installed_deprecated_apps_dialog_view.h
+++ b/chrome/browser/ui/views/web_apps/force_installed_deprecated_apps_dialog_view.h
@@ -21,11 +21,11 @@
 
   // Create the dialog metadata and show it.
   static void CreateAndShowDialog(extensions::ExtensionId app_id,
-                                  content::WebContents* web_contents,
-                                  base::OnceClosure launch_anyways);
+                                  content::WebContents* web_contents);
 
  private:
   ForceInstalledDeprecatedAppsDialogView(std::u16string app_name,
+                                         bool is_preinstalled_app,
                                          content::WebContents* web_contents);
 
   std::u16string app_name_;
diff --git a/chrome/browser/ui/views/web_apps/web_app_uninstall_dialog_browsertest.cc b/chrome/browser/ui/views/web_apps/web_app_uninstall_dialog_browsertest.cc
index c7892f27..780cdea 100644
--- a/chrome/browser/ui/views/web_apps/web_app_uninstall_dialog_browsertest.cc
+++ b/chrome/browser/ui/views/web_apps/web_app_uninstall_dialog_browsertest.cc
@@ -24,12 +24,15 @@
 #include "chrome/browser/web_applications/user_display_mode.h"
 #include "chrome/browser/web_applications/web_app_install_info.h"
 #include "chrome/test/base/in_process_browser_test.h"
+#include "components/keep_alive_registry/keep_alive_types.h"
+#include "components/keep_alive_registry/scoped_keep_alive.h"
 #include "components/webapps/browser/installable/installable_metrics.h"
 #include "content/public/test/browser_test.h"
 #include "content/public/test/browser_test_utils.h"
 #include "content/public/test/test_utils.h"
 #include "extensions/browser/extension_dialog_auto_confirm.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
+#include "ui/gfx/native_widget_types.h"
 
 using web_app::AppId;
 
@@ -140,6 +143,40 @@
 }
 
 IN_PROC_BROWSER_TEST_F(WebAppUninstallDialogViewBrowserTest,
+                       UninstallOnCancelShutdownBrowser) {
+  extensions::ScopedTestDialogAutoConfirm auto_confirm(
+      extensions::ScopedTestDialogAutoConfirm::CANCEL);
+  AppId app_id = InstallTestWebApp(browser()->profile());
+
+  std::unique_ptr<web_app::WebAppUninstallDialog> dialog(
+      web_app::WebAppUninstallDialog::Create(browser()->profile(),
+                                             gfx::kNullNativeWindow));
+
+  base::RunLoop().RunUntilIdle();
+
+  base::RunLoop run_loop;
+  bool was_uninstalled = true;
+  // ScopedKeepAlive is used by `UninstallWebAppWithDialogFromStartupSwitch`.
+  // ScopedKeepAlive's destruction triggers browser shutdown when there is no
+  // open window. This verifies the destruction doesn't race with the dialog
+  // shutdown itself.
+  std::unique_ptr<ScopedKeepAlive> scoped_keep_alive =
+      std::make_unique<ScopedKeepAlive>(KeepAliveOrigin::WEB_APP_UNINSTALL,
+                                        KeepAliveRestartOption::DISABLED);
+
+  chrome::CloseWindow(browser());
+
+  dialog->ConfirmUninstall(app_id, webapps::WebappUninstallSource::kAppMenu,
+                           base::BindLambdaForTesting([&](bool uninstalled) {
+                             was_uninstalled = uninstalled;
+                             scoped_keep_alive.reset();
+                             run_loop.Quit();
+                           }));
+  run_loop.Run();
+  EXPECT_FALSE(was_uninstalled);
+}
+
+IN_PROC_BROWSER_TEST_F(WebAppUninstallDialogViewBrowserTest,
                        TestDialogUserFlow_Cancel) {
   extensions::ScopedTestDialogAutoConfirm auto_confirm(
       extensions::ScopedTestDialogAutoConfirm::CANCEL);
diff --git a/chrome/browser/ui/views/web_apps/web_app_uninstall_dialog_view.cc b/chrome/browser/ui/views/web_apps/web_app_uninstall_dialog_view.cc
index 2837c9b4..23c5cb049 100644
--- a/chrome/browser/ui/views/web_apps/web_app_uninstall_dialog_view.cc
+++ b/chrome/browser/ui/views/web_apps/web_app_uninstall_dialog_view.cc
@@ -144,9 +144,7 @@
 void WebAppUninstallDialogDelegateView::OnDialogCanceled() {
   UMA_HISTOGRAM_ENUMERATION("Webapp.UninstallDialogAction",
                             HistogramCloseAction::kCancelled);
-
-  if (dialog_)
-    std::exchange(dialog_, nullptr)->UninstallCancelled();
+  // `dialog_->UninstallCancelled()` is handled in the destructor.
 }
 
 ui::ImageModel WebAppUninstallDialogDelegateView::GetWindowIcon() {
diff --git a/chrome/browser/ui/webui/about_ui.cc b/chrome/browser/ui/webui/about_ui.cc
index f52125b..f1be7001 100644
--- a/chrome/browser/ui/webui/about_ui.cc
+++ b/chrome/browser/ui/webui/about_ui.cc
@@ -777,7 +777,7 @@
 
 bool AboutUI::OverrideHandleWebUIMessage(const GURL& source_url,
                                          const std::string& message,
-                                         const base::ListValue& args) {
+                                         const base::Value::List& args) {
   if (message != "crosUrlAboutRedirect")
     return false;
 
diff --git a/chrome/browser/ui/webui/about_ui.h b/chrome/browser/ui/webui/about_ui.h
index f4b1045..b159d18de 100644
--- a/chrome/browser/ui/webui/about_ui.h
+++ b/chrome/browser/ui/webui/about_ui.h
@@ -60,7 +60,7 @@
 #if BUILDFLAG(IS_CHROMEOS)
   bool OverrideHandleWebUIMessage(const GURL& source_url,
                                   const std::string& message,
-                                  const base::ListValue& args) override;
+                                  const base::Value::List& args) override;
 #endif
 };
 
diff --git a/chrome/browser/ui/webui/certificate_provisioning_ui_handler_unittest.cc b/chrome/browser/ui/webui/certificate_provisioning_ui_handler_unittest.cc
index a3ca906a5..027e855 100644
--- a/chrome/browser/ui/webui/certificate_provisioning_ui_handler_unittest.cc
+++ b/chrome/browser/ui/webui/certificate_provisioning_ui_handler_unittest.cc
@@ -56,7 +56,9 @@
     "GVTJuPo4VToGd+ZhS7QvsY38nAYG57fMnzzs5jjMF042AzzWiMt9gGbeuqCE6LXqFuSJYPo+"
     "TLaN7pwQx68PK5pd/lv58B7jjxCIAai0BX1rV6bl/Am3EukhTSuIcQiTr5c1G4E6bKwIDAQAB";
 
-// Display-formatted version of |kDerEncodedSpkiBase64|.
+// Display-formatted version of |kDerEncodedSpkiBase64|. (The number of bits in
+// the public exponent used to be calculated by num_bytes*8, now it is the
+// actual number of used bits.)
 constexpr char kFormattedPublicKey[] = R"(Modulus (2048 bits):
   D6 76 BB AF A5 A2 68 BE 6C 96 CC 87 23 B6 C4 A4
 FE 5A 77 D7 DF B3 34 F4 98 18 BC C9 CD 37 84 9A
@@ -75,7 +77,7 @@
 E3 C4 22 00 6A 2D 01 5F 5A D5 E9 B9 7F 02 6D C4
 BA 48 53 4A E2 1C 42 24 EB E5 CD 46 E0 4E 9B 2B
 
-  Public Exponent (24 bits):
+  Public Exponent (17 bits):
   01 00 01)";
 
 // Test values for creating CertProfile for MockCertProvisioningWorker.
diff --git a/chrome/browser/ui/webui/chromeos/login/core_oobe_handler.cc b/chrome/browser/ui/webui/chromeos/login/core_oobe_handler.cc
index ba345f8..367c8e0 100644
--- a/chrome/browser/ui/webui/chromeos/login/core_oobe_handler.cc
+++ b/chrome/browser/ui/webui/chromeos/login/core_oobe_handler.cc
@@ -139,8 +139,6 @@
   AddCallback("screenStateInitialize", &CoreOobeHandler::HandleInitialized);
   AddCallback("updateCurrentScreen",
               &CoreOobeHandler::HandleUpdateCurrentScreen);
-  AddCallback("skipToLoginForTesting",
-              &CoreOobeHandler::HandleSkipToLoginForTesting);
   AddCallback("launchHelpApp", &CoreOobeHandler::HandleLaunchHelpApp);
   AddCallback("raiseTabKeyEvent", &CoreOobeHandler::HandleRaiseTabKeyEvent);
   AddCallback("startDemoModeSetupForTesting",
@@ -183,12 +181,6 @@
     LoginDisplayHost::default_host()->SetShelfButtonsEnabled(enable);
 }
 
-void CoreOobeHandler::HandleSkipToLoginForTesting() {
-  WizardController* controller = WizardController::default_controller();
-  if (controller && controller->is_initialized())
-    WizardController::default_controller()->SkipToLoginForTesting();
-}
-
 void CoreOobeHandler::ShowOobeUI(bool show) {
   CallJS("cr.ui.Oobe.showOobeUI", show);
 }
diff --git a/chrome/browser/ui/webui/chromeos/login/core_oobe_handler.h b/chrome/browser/ui/webui/chromeos/login/core_oobe_handler.h
index 7ae6a83..017477e 100644
--- a/chrome/browser/ui/webui/chromeos/login/core_oobe_handler.h
+++ b/chrome/browser/ui/webui/chromeos/login/core_oobe_handler.h
@@ -106,7 +106,6 @@
   void HandleEnableShelfButtons(bool enable);
   void HandleInitialized();
   void HandleUpdateCurrentScreen(const std::string& screen);
-  void HandleSkipToLoginForTesting();
   void HandleLaunchHelpApp(int help_topic_id);
   // Handles demo mode setup for tests. Accepts 'online' and 'offline' as
   // `demo_config`.
diff --git a/chrome/browser/ui/webui/inspect_ui_browsertest.cc b/chrome/browser/ui/webui/inspect_ui_browsertest.cc
index bc1b36c..7fe46cd 100644
--- a/chrome/browser/ui/webui/inspect_ui_browsertest.cc
+++ b/chrome/browser/ui/webui/inspect_ui_browsertest.cc
@@ -43,7 +43,7 @@
 
     // Fake clicking the "Inspect Native UI" button.
     web_ui->ProcessWebUIMessage(GURL(), "launch-ui-devtools",
-                                base::ListValue());
+                                base::Value::List());
 
     new_tab_observer.Wait();
     EXPECT_EQ(2, browser()->tab_strip_model()->count());
diff --git a/chrome/browser/ui/webui/ntp/app_launcher_handler.cc b/chrome/browser/ui/webui/ntp/app_launcher_handler.cc
index cb95515..965a676 100644
--- a/chrome/browser/ui/webui/ntp/app_launcher_handler.cc
+++ b/chrome/browser/ui/webui/ntp/app_launcher_handler.cc
@@ -96,7 +96,6 @@
 #include "extensions/common/constants.h"
 #include "extensions/common/extension.h"
 #include "extensions/common/extension_icon_set.h"
-#include "extensions/common/extension_id.h"
 #include "extensions/common/extension_set.h"
 #include "extensions/common/manifest_handlers/icons_handler.h"
 #include "net/base/url_util.h"
@@ -172,8 +171,6 @@
   return largest >= pixels;
 }
 
-// Query string for showing the deprecation dialog with deletion options.
-const char kDeprecationDialogQueryString[] = "showDeletionDialog";
 // Query string for showing the force installed apps deprecation dialog.
 // Should match with kChromeUIAppsWithForceInstalledDeprecationDialogURL.
 const char kForceInstallDialogQueryString[] = "showForceInstallDialog";
@@ -745,33 +742,19 @@
     install_manager_observation_.Observe(&web_app_provider_->install_manager());
 
     WebContents* web_contents = web_ui()->GetWebContents();
-    std::string app_id;
-    if (net::GetValueForKeyInQuery(web_contents->GetLastCommittedURL(),
-                                   kDeprecationDialogQueryString, &app_id)) {
-      if (extensions::IsExtensionUnsupportedDeprecatedApp(profile, app_id) &&
-          !deprecated_app_ids_.empty()) {
-        TabDialogs::FromWebContents(web_contents)
-            ->ShowDeprecatedAppsDialog(
-                app_id, deprecated_app_ids_, web_contents,
-                base::BindOnce(
-                    &AppLauncherHandler::LaunchApp,
-                    weak_ptr_factory_.GetWeakPtr(), app_id,
-                    extension_misc::AppLaunchBucket::APP_LAUNCH_CMD_LINE_APP,
-                    "", WindowOpenDisposition::CURRENT_TAB, true));
-      }
+    if (web_contents->GetLastCommittedURL() ==
+            GURL(chrome::kChromeUIAppsWithDeprecationDialogURL) &&
+        !deprecated_app_ids_.empty()) {
+      TabDialogs::FromWebContents(web_contents)
+          ->ShowDeprecatedAppsDialog(deprecated_app_ids_, web_contents);
     }
+    std::string app_id;
     if (net::GetValueForKeyInQuery(web_contents->GetLastCommittedURL(),
                                    kForceInstallDialogQueryString, &app_id)) {
       if (extensions::IsExtensionUnsupportedDeprecatedApp(profile, app_id) &&
           extensions::IsExtensionForceInstalled(profile, app_id, nullptr)) {
         TabDialogs::FromWebContents(web_contents)
-            ->ShowForceInstalledDeprecatedAppsDialog(
-                app_id, web_contents,
-                base::BindOnce(
-                    &AppLauncherHandler::LaunchApp,
-                    weak_ptr_factory_.GetWeakPtr(), app_id,
-                    extension_misc::AppLaunchBucket::APP_LAUNCH_CMD_LINE_APP,
-                    "", WindowOpenDisposition::CURRENT_TAB, true));
+            ->ShowForceInstalledDeprecatedAppsDialog(app_id, web_contents);
       }
     }
   }
@@ -781,50 +764,27 @@
 void AppLauncherHandler::HandleLaunchApp(const base::ListValue* args) {
   const std::string& extension_id = args->GetListDeprecated()[0].GetString();
   double source = args->GetListDeprecated()[1].GetDouble();
+  GURL override_url;
 
   extension_misc::AppLaunchBucket launch_bucket =
       static_cast<extension_misc::AppLaunchBucket>(static_cast<int>(source));
   CHECK(launch_bucket >= 0 &&
         launch_bucket < extension_misc::APP_LAUNCH_BUCKET_BOUNDARY);
 
-  WindowOpenDisposition disposition =
-      args->GetListDeprecated().size() > 3
-          ? webui::GetDispositionFromClick(args, 3)
-          : WindowOpenDisposition::CURRENT_TAB;
-  std::string source_value;
-  if (args->GetListDeprecated().size() > 2) {
-    source_value = args->GetListDeprecated()[2].GetString();
-  }
-  LaunchApp(extension_id, launch_bucket, source_value, disposition, false);
-}
-
-void AppLauncherHandler::LaunchApp(
-    std::string extension_id,
-    extension_misc::AppLaunchBucket launch_bucket,
-    const std::string& source_value,
-    WindowOpenDisposition disposition,
-    bool force_launch_deprecated_apps) {
   Profile* profile = extension_service_->profile();
 
-  if (!force_launch_deprecated_apps &&
-      extensions::IsExtensionUnsupportedDeprecatedApp(profile, extension_id) &&
+  if (extensions::IsExtensionUnsupportedDeprecatedApp(profile, extension_id) &&
       base::FeatureList::IsEnabled(features::kChromeAppsDeprecation)) {
     if (!extensions::IsExtensionForceInstalled(profile, extension_id,
                                                nullptr)) {
       TabDialogs::FromWebContents(web_ui()->GetWebContents())
-          ->ShowDeprecatedAppsDialog(
-              extension_id, deprecated_app_ids_, web_ui()->GetWebContents(),
-              base::BindOnce(&AppLauncherHandler::LaunchApp,
-                             weak_ptr_factory_.GetWeakPtr(), extension_id,
-                             launch_bucket, source_value, disposition, true));
+          ->ShowDeprecatedAppsDialog(deprecated_app_ids_,
+                                     web_ui()->GetWebContents());
       return;
     } else {
       TabDialogs::FromWebContents(web_ui()->GetWebContents())
-          ->ShowForceInstalledDeprecatedAppsDialog(
-              extension_id, web_ui()->GetWebContents(),
-              base::BindOnce(&AppLauncherHandler::LaunchApp,
-                             weak_ptr_factory_.GetWeakPtr(), extension_id,
-                             launch_bucket, source_value, disposition, true));
+          ->ShowForceInstalledDeprecatedAppsDialog(extension_id,
+                                                   web_ui()->GetWebContents());
       return;
     }
   }
@@ -856,15 +816,24 @@
         extensions::GetLaunchContainer(ExtensionPrefs::Get(profile), extension);
   }
 
-  GURL override_url;
+  WindowOpenDisposition disposition =
+      args->GetListDeprecated().size() > 3
+          ? webui::GetDispositionFromClick(args, 3)
+          : WindowOpenDisposition::CURRENT_TAB;
   if (extension_id != extensions::kWebStoreAppId) {
     CHECK_NE(launch_bucket, extension_misc::APP_LAUNCH_BUCKET_INVALID);
     extensions::RecordAppLaunchType(launch_bucket, type);
   } else {
     extensions::RecordWebStoreLaunch();
-    if (!source_value.empty()) {
-      override_url = net::AppendQueryParameter(
-          full_launch_url, extension_urls::kWebstoreSourceField, source_value);
+
+    if (args->GetListDeprecated().size() > 2) {
+      const std::string& source_value =
+          args->GetListDeprecated()[2].GetString();
+      if (!source_value.empty()) {
+        override_url = net::AppendQueryParameter(
+            full_launch_url, extension_urls::kWebstoreSourceField,
+            source_value);
+      }
     }
   }
 
@@ -1261,8 +1230,8 @@
 void AppLauncherHandler::HandleLaunchDeprecatedAppDialog(
     const base::ListValue* args) {
   TabDialogs::FromWebContents(web_ui()->GetWebContents())
-      ->ShowDeprecatedAppsDialog(extensions::ExtensionId(), deprecated_app_ids_,
-                                 web_ui()->GetWebContents(), base::DoNothing());
+      ->ShowDeprecatedAppsDialog(deprecated_app_ids_,
+                                 web_ui()->GetWebContents());
 }
 
 void AppLauncherHandler::OnFaviconForAppInstallFromLink(
diff --git a/chrome/browser/ui/webui/ntp/app_launcher_handler.h b/chrome/browser/ui/webui/ntp/app_launcher_handler.h
index df3a4969..cbb3bd66 100644
--- a/chrome/browser/ui/webui/ntp/app_launcher_handler.h
+++ b/chrome/browser/ui/webui/ntp/app_launcher_handler.h
@@ -137,12 +137,6 @@
   // CURRENT_TAB.
   void HandleLaunchApp(const base::ListValue* args);
 
-  void LaunchApp(std::string extension_id,
-                 extension_misc::AppLaunchBucket launch_bucket,
-                 const std::string& source_value,
-                 WindowOpenDisposition disposition,
-                 bool force_launch_deprecated_apps);
-
   // Handles the "setLaunchType" message with args containing [extension_id,
   // launch_type].
   void HandleSetLaunchType(const base::ListValue* args);
diff --git a/chrome/browser/upgrade_detector/get_installed_version_lacros.cc b/chrome/browser/upgrade_detector/get_installed_version_lacros.cc
index 7125375..5499a95 100644
--- a/chrome/browser/upgrade_detector/get_installed_version_lacros.cc
+++ b/chrome/browser/upgrade_detector/get_installed_version_lacros.cc
@@ -40,8 +40,14 @@
         ->GetInstalledBrowserVersion(base::BindOnce(
             [](InstalledVersionCallback callback,
                const std::string& version_str) {
-              std::move(callback).Run(
-                  InstalledAndCriticalVersion(base::Version(version_str)));
+              // We must return the current version if we obtain an invalid
+              // version, so that the InstalledVersionPoller can interpret that
+              // no update is available. This scenario can happen when rootfs
+              // LaCrOS was launched and no stateful LaCrOS component is
+              // currently available.
+              std::move(callback).Run(InstalledAndCriticalVersion(
+                  version_str.empty() ? version_info::GetVersion()
+                                      : base::Version(version_str)));
             },
             std::move(callback)));
     return;
diff --git a/chrome/build/mac-arm.pgo.txt b/chrome/build/mac-arm.pgo.txt
index 8da84b752..75e0ae0 100644
--- a/chrome/build/mac-arm.pgo.txt
+++ b/chrome/build/mac-arm.pgo.txt
@@ -1 +1 @@
-chrome-mac-arm-main-1654257500-fffd73c277d12ee4956fbbedbbe9532dc75c129a.profdata
+chrome-mac-arm-main-1654278778-d97ac870f725ecbfb2f025aad788d7e32da05067.profdata
diff --git a/chrome/build/win32.pgo.txt b/chrome/build/win32.pgo.txt
index 29530d3..aa60aa1 100644
--- a/chrome/build/win32.pgo.txt
+++ b/chrome/build/win32.pgo.txt
@@ -1 +1 @@
-chrome-win32-main-1654257500-e7146cec6e1e40f7e745f927283335ed4262f8d6.profdata
+chrome-win32-main-1654268062-a0e703611fd64478db987839a97756519036d1ee.profdata
diff --git a/chrome/build/win64.pgo.txt b/chrome/build/win64.pgo.txt
index c86ae3f..bedfdb4a 100644
--- a/chrome/build/win64.pgo.txt
+++ b/chrome/build/win64.pgo.txt
@@ -1 +1 @@
-chrome-win64-main-1654257500-6ef2b1e67c0226ca759d9a424716c25fccb7f138.profdata
+chrome-win64-main-1654268062-0c6bb69754d834ccf880c85adf3a53aea8c92fc2.profdata
diff --git a/chrome/common/net/x509_certificate_model.cc b/chrome/common/net/x509_certificate_model.cc
index 7d6cc07..8e3f0827 100644
--- a/chrome/common/net/x509_certificate_model.cc
+++ b/chrome/common/net/x509_certificate_model.cc
@@ -20,14 +20,18 @@
 #include "net/cert/internal/certificate_policies.h"
 #include "net/cert/internal/extended_key_usage.h"
 #include "net/cert/internal/parse_name.h"
+#include "net/cert/internal/signature_algorithm.h"
+#include "net/cert/internal/verify_signed_data.h"
 #include "net/cert/x509_util.h"
 #include "net/der/encode_values.h"
 #include "net/der/input.h"
 #include "net/der/parse_values.h"
 #include "net/der/parser.h"
 #include "net/der/tag.h"
+#include "third_party/boringssl/src/include/openssl/bn.h"
 #include "third_party/boringssl/src/include/openssl/bytestring.h"
 #include "third_party/boringssl/src/include/openssl/mem.h"
+#include "third_party/boringssl/src/include/openssl/rsa.h"
 #include "ui/base/l10n/l10n_util.h"
 
 namespace x509_certificate_model {
@@ -76,6 +80,76 @@
 // In dotted notation: 2.5.29.9
 constexpr uint8_t kSubjectDirectoryAttributesOid[] = {0x55, 0x1d, 0x09};
 
+// From RFC 3447:
+// pkcs-1    OBJECT IDENTIFIER ::= {
+//     iso(1) member-body(2) us(840) rsadsi(113549) pkcs(1) 1
+// }
+// rsaEncryption    OBJECT IDENTIFIER ::= { pkcs-1 1 }
+constexpr uint8_t kPkcs1RsaEncryption[] = {0x2a, 0x86, 0x48, 0x86, 0xf7,
+                                           0x0d, 0x01, 0x01, 0x01};
+// md2WithRSAEncryption       OBJECT IDENTIFIER ::= { pkcs-1 2 }
+constexpr uint8_t kPkcs1Md2WithRsaEncryption[] = {0x2a, 0x86, 0x48, 0x86, 0xf7,
+                                                  0x0d, 0x01, 0x01, 0x02};
+// From RFC 2314: md4WithRSAEncryption OBJECT IDENTIFIER ::= { pkcs-1 3 }
+constexpr uint8_t kPkcs1Md4WithRsaEncryption[] = {0x2a, 0x86, 0x48, 0x86, 0xf7,
+                                                  0x0d, 0x01, 0x01, 0x03};
+// md5WithRSAEncryption       OBJECT IDENTIFIER ::= { pkcs-1 4 }
+constexpr uint8_t kPkcs1Md5WithRsaEncryption[] = {0x2a, 0x86, 0x48, 0x86, 0xf7,
+                                                  0x0d, 0x01, 0x01, 0x04};
+// sha1WithRSAEncryption      OBJECT IDENTIFIER ::= { pkcs-1 5 }
+constexpr uint8_t kPkcs1Sha1WithRsaEncryption[] = {0x2a, 0x86, 0x48, 0x86, 0xf7,
+                                                   0x0d, 0x01, 0x01, 0x05};
+// sha256WithRSAEncryption    OBJECT IDENTIFIER ::= { pkcs-1 11 }
+constexpr uint8_t kPkcs1Sha256WithRsaEncryption[] = {
+    0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b};
+// sha384WithRSAEncryption    OBJECT IDENTIFIER ::= { pkcs-1 12 }
+constexpr uint8_t kPkcs1Sha384WithRsaEncryption[] = {
+    0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0c};
+// sha512WithRSAEncryption    OBJECT IDENTIFIER ::= { pkcs-1 13 }
+constexpr uint8_t kPkcs1Sha512WithRsaEncryption[] = {
+    0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0d};
+// From RFC 3279:
+//   ansi-X9-62  OBJECT IDENTIFIER ::= {
+//            iso(1) member-body(2) us(840) 10045 }
+//   id-ecSigType OBJECT IDENTIFIER  ::=  {
+//        ansi-X9-62 signatures(4) }
+//   ecdsa-with-SHA1  OBJECT IDENTIFIER ::= {
+//        id-ecSigType 1 }
+constexpr uint8_t kAnsiX962EcdsaWithSha1[] = {0x2a, 0x86, 0x48, 0xce,
+                                              0x3d, 0x04, 0x01};
+// From RFC 5758:
+//    ecdsa-with-SHA256 OBJECT IDENTIFIER ::= { iso(1) member-body(2)
+//            us(840) ansi-X9-62(10045) signatures(4) ecdsa-with-SHA2(3) 2 }
+constexpr uint8_t kAnsiX962EcdsaWithSha256[] = {0x2a, 0x86, 0x48, 0xce,
+                                                0x3d, 0x04, 0x03, 0x02};
+//    ecdsa-with-SHA384 OBJECT IDENTIFIER ::= { iso(1) member-body(2)
+//            us(840) ansi-X9-62(10045) signatures(4) ecdsa-with-SHA2(3) 3 }
+constexpr uint8_t kAnsiX962EcdsaWithSha384[] = {0x2a, 0x86, 0x48, 0xce,
+                                                0x3d, 0x04, 0x03, 0x03};
+//    ecdsa-with-SHA512 OBJECT IDENTIFIER ::= { iso(1) member-body(2)
+//            us(840) ansi-X9-62(10045) signatures(4) ecdsa-with-SHA2(3) 4 }
+constexpr uint8_t kAnsiX962EcdsaWithSha512[] = {0x2a, 0x86, 0x48, 0xce,
+                                                0x3d, 0x04, 0x03, 0x04};
+// From RFC 3279:
+//    ansi-X9-62 OBJECT IDENTIFIER ::=
+//                            { iso(1) member-body(2) us(840) 10045 }
+//    id-public-key-type OBJECT IDENTIFIER  ::= { ansi-X9.62 2 }
+//    id-ecPublicKey OBJECT IDENTIFIER ::= { id-publicKeyType 1 }
+constexpr uint8_t kAnsiX962EcPublicKey[] = {0x2a, 0x86, 0x48, 0xce,
+                                            0x3d, 0x02, 0x01};
+// From RFC 5480:
+//     secp256r1 OBJECT IDENTIFIER ::= {
+//       iso(1) member-body(2) us(840) ansi-X9-62(10045) curves(3)
+//       prime(1) 7 }
+constexpr uint8_t kSecgEcSecp256r1[] = {0x2a, 0x86, 0x48, 0xce,
+                                        0x3d, 0x03, 0x01, 0x07};
+//     secp384r1 OBJECT IDENTIFIER ::= {
+//       iso(1) identified-organization(3) certicom(132) curve(0) 34 }
+constexpr uint8_t kSecgEcSecp384r1[] = {0x2b, 0x81, 0x04, 0x00, 0x22};
+//     secp521r1 OBJECT IDENTIFIER ::= {
+//       iso(1) identified-organization(3) certicom(132) curve(0) 35 }
+constexpr uint8_t kSecgEcSecp512r1[] = {0x2b, 0x81, 0x04, 0x00, 0x23};
+
 // Old Netscape OIDs. Do we still need all these?
 // #define NETSCAPE_OID 0x60, 0x86, 0x48, 0x01, 0x86, 0xf8, 0x42
 // #define NETSCAPE_CERT_EXT NETSCAPE_OID, 0x01
@@ -94,6 +168,10 @@
                                                  data.Length());
 }
 
+std::string ProcessRawBytes(base::span<const uint8_t> data) {
+  return x509_certificate_model::ProcessRawBytes(data.data(), data.size());
+}
+
 OptionalStringOrError FindAttributeOfType(
     net::der::Input oid,
     const net::RelativeDistinguishedName& rdn) {
@@ -180,6 +258,36 @@
      IDS_CERT_OID_AVA_STREET_ADDRESS},
     {net::der::Input(kTypePostalCode), IDS_CERT_OID_AVA_POSTAL_CODE},
 
+    // Algorithm fields:
+    {net::der::Input(kPkcs1RsaEncryption), IDS_CERT_OID_PKCS1_RSA_ENCRYPTION},
+    {net::der::Input(kPkcs1Md2WithRsaEncryption),
+     IDS_CERT_OID_PKCS1_MD2_WITH_RSA_ENCRYPTION},
+    {net::der::Input(kPkcs1Md4WithRsaEncryption),
+     IDS_CERT_OID_PKCS1_MD4_WITH_RSA_ENCRYPTION},
+    {net::der::Input(kPkcs1Md5WithRsaEncryption),
+     IDS_CERT_OID_PKCS1_MD5_WITH_RSA_ENCRYPTION},
+    {net::der::Input(kPkcs1Sha1WithRsaEncryption),
+     IDS_CERT_OID_PKCS1_SHA1_WITH_RSA_ENCRYPTION},
+    {net::der::Input(kPkcs1Sha256WithRsaEncryption),
+     IDS_CERT_OID_PKCS1_SHA256_WITH_RSA_ENCRYPTION},
+    {net::der::Input(kPkcs1Sha384WithRsaEncryption),
+     IDS_CERT_OID_PKCS1_SHA384_WITH_RSA_ENCRYPTION},
+    {net::der::Input(kPkcs1Sha512WithRsaEncryption),
+     IDS_CERT_OID_PKCS1_SHA512_WITH_RSA_ENCRYPTION},
+    {net::der::Input(kAnsiX962EcdsaWithSha1),
+     IDS_CERT_OID_ANSIX962_ECDSA_SHA1_SIGNATURE},
+    {net::der::Input(kAnsiX962EcdsaWithSha256),
+     IDS_CERT_OID_ANSIX962_ECDSA_SHA256_SIGNATURE},
+    {net::der::Input(kAnsiX962EcdsaWithSha384),
+     IDS_CERT_OID_ANSIX962_ECDSA_SHA384_SIGNATURE},
+    {net::der::Input(kAnsiX962EcdsaWithSha512),
+     IDS_CERT_OID_ANSIX962_ECDSA_SHA512_SIGNATURE},
+    {net::der::Input(kAnsiX962EcPublicKey),
+     IDS_CERT_OID_ANSIX962_EC_PUBLIC_KEY},
+    {net::der::Input(kSecgEcSecp256r1), IDS_CERT_OID_SECG_EC_SECP256R1},
+    {net::der::Input(kSecgEcSecp384r1), IDS_CERT_OID_SECG_EC_SECP384R1},
+    {net::der::Input(kSecgEcSecp512r1), IDS_CERT_OID_SECG_EC_SECP521R1},
+
     // Extension fields (including details of extensions):
     {net::der::Input(kNetscapeCertificateTypeOid), IDS_CERT_EXT_NS_CERT_TYPE},
     {net::der::Input(kSubjectDirectoryAttributesOid),
@@ -868,6 +976,45 @@
   return rv;
 }
 
+std::string ProcessAlgorithmIdentifier(net::der::Input algorithm_tlv) {
+  net::der::Input oid;
+  net::der::Input params;
+  if (!net::ParseAlgorithmIdentifier(algorithm_tlv, &oid, &params)) {
+    return std::string();
+  }
+  return GetOidTextOrNumeric(oid);
+}
+
+bool ParseSubjectPublicKeyInfo(net::der::Input spki_tlv,
+                               net::der::Input* algorithm_tlv,
+                               net::der::Input* subject_public_key_value) {
+  net::der::Parser spki_parser(spki_tlv);
+
+  //    SubjectPublicKeyInfo  ::=  SEQUENCE  {
+  //         algorithm            AlgorithmIdentifier,
+  //         subjectPublicKey     BIT STRING  }
+  net::der::Parser sequence_parser;
+  if (!spki_parser.ReadSequence(&sequence_parser))
+    return false;
+
+  if (!sequence_parser.ReadRawTLV(algorithm_tlv))
+    return false;
+
+  if (!sequence_parser.ReadTag(net::der::kBitString, subject_public_key_value))
+    return false;
+
+  if (sequence_parser.HasMore())
+    return false;
+
+  return true;
+}
+
+std::vector<uint8_t> BIGNUMBytes(const BIGNUM* bn) {
+  std::vector<uint8_t> ret(BN_num_bytes(bn));
+  BN_bn2bin(bn, ret.data());
+  return ret;
+}
+
 }  // namespace
 
 X509CertificateModel::X509CertificateModel(
@@ -1134,6 +1281,42 @@
   return ProcessRawBytes(extension.value);
 }
 
+std::string X509CertificateModel::ProcessSecAlgorithmSignature() const {
+  DCHECK(parsed_successfully_);
+  return ProcessAlgorithmIdentifier(signature_algorithm_tlv_);
+}
+
+std::string X509CertificateModel::ProcessSecAlgorithmSubjectPublicKey() const {
+  DCHECK(parsed_successfully_);
+
+  net::der::Input algorithm_tlv;
+  net::der::Input unused_spk_value;
+  if (!ParseSubjectPublicKeyInfo(tbs_.spki_tlv, &algorithm_tlv,
+                                 &unused_spk_value)) {
+    return std::string();
+  }
+
+  return ProcessAlgorithmIdentifier(algorithm_tlv);
+}
+
+std::string X509CertificateModel::ProcessSecAlgorithmSignatureWrap() const {
+  DCHECK(parsed_successfully_);
+  return ProcessAlgorithmIdentifier(tbs_.signature_algorithm_tlv);
+}
+
+std::string X509CertificateModel::ProcessSubjectPublicKeyInfo() const {
+  DCHECK(parsed_successfully_);
+  std::string rv = ProcessRawSubjectPublicKeyInfo(tbs_.spki_tlv.AsSpan());
+  if (rv.empty())
+    return std::string();
+  return rv;
+}
+
+std::string X509CertificateModel::ProcessRawBitsSignatureWrap() const {
+  DCHECK(parsed_successfully_);
+  return ProcessRawBytes(signature_value_.bytes());
+}
+
 // TODO(https://crbug.com/953425): move to anonymous namespace once
 // x509_certificate_model_nss is removed.
 std::string ProcessIDN(const std::string& input) {
@@ -1199,4 +1382,41 @@
   return ProcessRawBytes(data, (data_length + 7) / 8);
 }
 
+std::string ProcessRawSubjectPublicKeyInfo(base::span<const uint8_t> spki_der) {
+  bssl::UniquePtr<EVP_PKEY> public_key;
+  if (!net::ParsePublicKey(net::der::Input(spki_der.data(), spki_der.size()),
+                           &public_key)) {
+    return std::string();
+  }
+  switch (EVP_PKEY_id(public_key.get())) {
+    case EVP_PKEY_RSA: {
+      RSA* rsa = EVP_PKEY_get0_RSA(public_key.get());
+      // EVP_PKEY_get0_RSA can only fail if the type was wrong, which was just
+      // checked in the switch.
+      DCHECK(rsa);
+      const BIGNUM* modulus = RSA_get0_n(rsa);
+      const BIGNUM* public_exponent = RSA_get0_e(rsa);
+      DCHECK(modulus);
+      DCHECK(public_exponent);
+
+      return l10n_util::GetStringFUTF8(
+          IDS_CERT_RSA_PUBLIC_KEY_DUMP_FORMAT,
+          base::NumberToString16(BN_num_bits(modulus)),
+          base::UTF8ToUTF16(ProcessRawBytes(BIGNUMBytes(modulus))),
+          base::NumberToString16(BN_num_bits(public_exponent)),
+          base::UTF8ToUTF16(ProcessRawBytes(BIGNUMBytes(public_exponent))));
+    }
+      // TODO(mattm): handle other key types? (eg EVP_PKEY_EC)
+  }
+
+  net::der::Input unused_algorithm_tlv;
+  net::der::Input subject_public_key_value;
+  if (!ParseSubjectPublicKeyInfo(
+          net::der::Input(spki_der.data(), spki_der.size()),
+          &unused_algorithm_tlv, &subject_public_key_value)) {
+    return std::string();
+  }
+  return ProcessRawBytes(subject_public_key_value);
+}
+
 }  // namespace x509_certificate_model
diff --git a/chrome/common/net/x509_certificate_model.h b/chrome/common/net/x509_certificate_model.h
index 3f09e19..8ee7389 100644
--- a/chrome/common/net/x509_certificate_model.h
+++ b/chrome/common/net/x509_certificate_model.h
@@ -101,6 +101,14 @@
       base::StringPiece critical_label,
       base::StringPiece non_critical_label) const;
 
+  std::string ProcessSecAlgorithmSignature() const;
+  std::string ProcessSecAlgorithmSubjectPublicKey() const;
+  std::string ProcessSecAlgorithmSignatureWrap() const;
+
+  std::string ProcessSubjectPublicKeyInfo() const;
+
+  std::string ProcessRawBitsSignatureWrap() const;
+
  private:
   bool ParseExtensions(const net::der::Input& extensions_tlv);
   std::string ProcessExtension(base::StringPiece critical_label,
@@ -146,6 +154,11 @@
 // |data_length| is the length in bits.
 std::string ProcessRawBits(const unsigned char* data, size_t data_length);
 
+// Parses |public_key_spki_der| as a DER-encoded X.509 SubjectPublicKeyInfo,
+// then formats the public key as a string for displaying. Returns an empty
+// string on error.
+std::string ProcessRawSubjectPublicKeyInfo(base::span<const uint8_t> spki_der);
+
 }  // namespace x509_certificate_model
 
 #endif  // CHROME_COMMON_NET_X509_CERTIFICATE_MODEL_H_
diff --git a/chrome/common/net/x509_certificate_model_nss.cc b/chrome/common/net/x509_certificate_model_nss.cc
index dd5c18c..8f374648 100644
--- a/chrome/common/net/x509_certificate_model_nss.cc
+++ b/chrome/common/net/x509_certificate_model_nss.cc
@@ -227,14 +227,6 @@
   return psm::ProcessSubjectPublicKeyInfo(&cert_handle->subjectPublicKeyInfo);
 }
 
-string ProcessRawSubjectPublicKeyInfo(base::span<const uint8_t> spki_der) {
-  crypto::ScopedCERTSubjectPublicKeyInfo spki =
-      crypto::DecodeSubjectPublicKeyInfoNSS(spki_der);
-  if (!spki)
-    return std::string();
-  return psm::ProcessSubjectPublicKeyInfo(spki.get());
-}
-
 string ProcessRawBitsSignatureWrap(CERTCertificate* cert_handle) {
   return ProcessRawBits(cert_handle->signatureWrap.signature.data,
                         cert_handle->signatureWrap.signature.len);
diff --git a/chrome/common/net/x509_certificate_model_nss.h b/chrome/common/net/x509_certificate_model_nss.h
index c1476dd..123cb014 100644
--- a/chrome/common/net/x509_certificate_model_nss.h
+++ b/chrome/common/net/x509_certificate_model_nss.h
@@ -83,11 +83,6 @@
 // |cert_handle| as a string for displaying.
 std::string ProcessSubjectPublicKeyInfo(CERTCertificate* cert_handle);
 
-// Parses |public_key_spki_der| as a DER-encoded X.509 SubjectPublicKeyInfo,
-// then formats the public key as a string for displaying.
-std::string ProcessRawSubjectPublicKeyInfo(
-    base::span<const uint8_t> public_key_spki_der);
-
 std::string ProcessRawBitsSignatureWrap(CERTCertificate* cert_handle);
 
 }  // namespace x509_certificate_model
diff --git a/chrome/common/net/x509_certificate_model_unittest.cc b/chrome/common/net/x509_certificate_model_unittest.cc
index 2d430f8..e8829d3 100644
--- a/chrome/common/net/x509_certificate_model_unittest.cc
+++ b/chrome/common/net/x509_certificate_model_unittest.cc
@@ -108,6 +108,37 @@
   const double kGoogleParseValidTo = 1324252799;
   EXPECT_EQ(kGoogleParseValidTo, not_after.ToDoubleT());
 
+  EXPECT_EQ("PKCS #1 SHA-1 With RSA Encryption",
+            model.ProcessSecAlgorithmSignature());
+  EXPECT_EQ("PKCS #1 SHA-1 With RSA Encryption",
+            model.ProcessSecAlgorithmSignatureWrap());
+  EXPECT_EQ("PKCS #1 RSA Encryption",
+            model.ProcessSecAlgorithmSubjectPublicKey());
+  EXPECT_EQ(
+      "Modulus (1024 bits):\n"
+      "  E8 F9 86 0F 90 FA 86 D7 DF BD 72 26 B6 D7 44 02\n"
+      "83 78 73 D9 02 28 EF 88 45 39 FB 10 E8 7C AE A9\n"
+      "38 D5 75 C6 38 EB 0A 15 07 9B 83 E8 CD 82 D5 E3\n"
+      "F7 15 68 45 A1 0B 19 85 BC E2 EF 84 E7 DD F2 D7\n"
+      "B8 98 C2 A1 BB B5 C1 51 DF D4 83 02 A7 3D 06 42\n"
+      "5B E1 22 C3 DE 6B 85 5F 1C D6 DA 4E 8B D3 9B EE\n"
+      "B9 67 22 2A 1D 11 EF 79 A4 B3 37 8A F4 FE 18 FD\n"
+      "BC F9 46 23 50 97 F3 AC FC 24 46 2B 5C 3B B7 45\n"
+      "\n"
+      "  Public Exponent (17 bits):\n"
+      "  01 00 01",
+      model.ProcessSubjectPublicKeyInfo());
+  EXPECT_EQ(
+      "9F 43 CF 5B C4 50 29 B1 BF E2 B0 9A FF 6A 21 1D\n"
+      "2D 12 C3 2C 4E 5A F9 12 E2 CE B9 82 52 2D E7 1D\n"
+      "7E 1A 76 96 90 79 D1 24 52 38 79 BB 63 8D 80 97\n"
+      "7C 23 20 0F 91 4D 16 B9 EA EE F4 6D 89 CA C6 BD\n"
+      "CC 24 68 D6 43 5B CE 2A 58 BF 3C 18 E0 E0 3C 62\n"
+      "CF 96 02 2D 28 47 50 34 E1 27 BA CF 99 D1 50 FF\n"
+      "29 25 C0 36 36 15 33 52 70 BE 31 8F 9F E8 7F E7\n"
+      "11 0C 8D BF 84 A0 42 1A 80 89 B0 31 58 41 07 5F",
+      model.ProcessRawBitsSignatureWrap());
+
   auto extensions = model.GetExtensions("critical", "notcrit");
   ASSERT_EQ(4U, extensions.size());
   EXPECT_EQ("Certificate Basic Constraints", extensions[0].name);
@@ -653,3 +684,81 @@
                          X509CertificateModel,
                          testing::Values(std::string(),
                                          std::string("nickname")));
+
+// TODO(https://crbug.com/953425): This test suite has "2" at the end of the
+// name to avoid conflicting with x509_certificate_model_nss_unittest. Should
+// rename the test suite in that file to X509CertificateModelNSSTest and remove
+// the 2 from here.
+TEST(X509CertificateModelTest2, ProcessRawSubjectPublicKeyInfo) {
+  // SEQUENCE {
+  //   SEQUENCE {
+  //     # rsaEncryption
+  //     OBJECT_IDENTIFIER { 1.2.840.113549.1.1.1 }
+  //     NULL {}
+  //   }
+  //   BIT_STRING {
+  //     `00`
+  //     SEQUENCE {
+  //       INTEGER {
+  //       `00e053f4f398c1143302c8a46dfeaa2af7943da66f00df3bde4c9fa3ea07d4ac`
+  //       `e55b0dd1ace0edf9c5981d352de5b349971485440fdc4cd267088801a5d8a7eb`
+  //       `93d16aa1f751e7847e522a7dbc6f0ed8dbb6a63ededcf5a4689644118502ed47`
+  //       `12dfb86071957b6287687a445609d5b4c8f1f6c946928b68e883d5d5867123c3`
+  //       `801ebf6c01c7d2a4bc406de0e3c02e3078bdaddd2566d3f5070756d7cee272c5`
+  //       `257d0ce1a76f00a8daab4b54430964a4b652382fb7cc01dd1c03270347bfdfe6`
+  //       `37b0ed18dc510bd47522df507b3ceb37391c9b6f087ba705ac8c43f7f1da5106`
+  //       `b382453ec881739eb0a5cf7696af812cac012a4a584b1dbeff1f85c227def178`
+  //       `0b`
+  //       } INTEGER { 65537 }
+  //     }
+  //   }
+  // }
+  const uint8_t spki_bytes[] = {
+      0x30, 0x82, 0x01, 0x22, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86,
+      0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x82, 0x01, 0x0f, 0x00,
+      0x30, 0x82, 0x01, 0x0a, 0x02, 0x82, 0x01, 0x01, 0x00, 0xe0, 0x53, 0xf4,
+      0xf3, 0x98, 0xc1, 0x14, 0x33, 0x02, 0xc8, 0xa4, 0x6d, 0xfe, 0xaa, 0x2a,
+      0xf7, 0x94, 0x3d, 0xa6, 0x6f, 0x00, 0xdf, 0x3b, 0xde, 0x4c, 0x9f, 0xa3,
+      0xea, 0x07, 0xd4, 0xac, 0xe5, 0x5b, 0x0d, 0xd1, 0xac, 0xe0, 0xed, 0xf9,
+      0xc5, 0x98, 0x1d, 0x35, 0x2d, 0xe5, 0xb3, 0x49, 0x97, 0x14, 0x85, 0x44,
+      0x0f, 0xdc, 0x4c, 0xd2, 0x67, 0x08, 0x88, 0x01, 0xa5, 0xd8, 0xa7, 0xeb,
+      0x93, 0xd1, 0x6a, 0xa1, 0xf7, 0x51, 0xe7, 0x84, 0x7e, 0x52, 0x2a, 0x7d,
+      0xbc, 0x6f, 0x0e, 0xd8, 0xdb, 0xb6, 0xa6, 0x3e, 0xde, 0xdc, 0xf5, 0xa4,
+      0x68, 0x96, 0x44, 0x11, 0x85, 0x02, 0xed, 0x47, 0x12, 0xdf, 0xb8, 0x60,
+      0x71, 0x95, 0x7b, 0x62, 0x87, 0x68, 0x7a, 0x44, 0x56, 0x09, 0xd5, 0xb4,
+      0xc8, 0xf1, 0xf6, 0xc9, 0x46, 0x92, 0x8b, 0x68, 0xe8, 0x83, 0xd5, 0xd5,
+      0x86, 0x71, 0x23, 0xc3, 0x80, 0x1e, 0xbf, 0x6c, 0x01, 0xc7, 0xd2, 0xa4,
+      0xbc, 0x40, 0x6d, 0xe0, 0xe3, 0xc0, 0x2e, 0x30, 0x78, 0xbd, 0xad, 0xdd,
+      0x25, 0x66, 0xd3, 0xf5, 0x07, 0x07, 0x56, 0xd7, 0xce, 0xe2, 0x72, 0xc5,
+      0x25, 0x7d, 0x0c, 0xe1, 0xa7, 0x6f, 0x00, 0xa8, 0xda, 0xab, 0x4b, 0x54,
+      0x43, 0x09, 0x64, 0xa4, 0xb6, 0x52, 0x38, 0x2f, 0xb7, 0xcc, 0x01, 0xdd,
+      0x1c, 0x03, 0x27, 0x03, 0x47, 0xbf, 0xdf, 0xe6, 0x37, 0xb0, 0xed, 0x18,
+      0xdc, 0x51, 0x0b, 0xd4, 0x75, 0x22, 0xdf, 0x50, 0x7b, 0x3c, 0xeb, 0x37,
+      0x39, 0x1c, 0x9b, 0x6f, 0x08, 0x7b, 0xa7, 0x05, 0xac, 0x8c, 0x43, 0xf7,
+      0xf1, 0xda, 0x51, 0x06, 0xb3, 0x82, 0x45, 0x3e, 0xc8, 0x81, 0x73, 0x9e,
+      0xb0, 0xa5, 0xcf, 0x76, 0x96, 0xaf, 0x81, 0x2c, 0xac, 0x01, 0x2a, 0x4a,
+      0x58, 0x4b, 0x1d, 0xbe, 0xff, 0x1f, 0x85, 0xc2, 0x27, 0xde, 0xf1, 0x78,
+      0x0b, 0x02, 0x03, 0x01, 0x00, 0x01};
+  EXPECT_EQ(
+      "Modulus (2048 bits):\n"
+      "  E0 53 F4 F3 98 C1 14 33 02 C8 A4 6D FE AA 2A F7\n"
+      "94 3D A6 6F 00 DF 3B DE 4C 9F A3 EA 07 D4 AC E5\n"
+      "5B 0D D1 AC E0 ED F9 C5 98 1D 35 2D E5 B3 49 97\n"
+      "14 85 44 0F DC 4C D2 67 08 88 01 A5 D8 A7 EB 93\n"
+      "D1 6A A1 F7 51 E7 84 7E 52 2A 7D BC 6F 0E D8 DB\n"
+      "B6 A6 3E DE DC F5 A4 68 96 44 11 85 02 ED 47 12\n"
+      "DF B8 60 71 95 7B 62 87 68 7A 44 56 09 D5 B4 C8\n"
+      "F1 F6 C9 46 92 8B 68 E8 83 D5 D5 86 71 23 C3 80\n"
+      "1E BF 6C 01 C7 D2 A4 BC 40 6D E0 E3 C0 2E 30 78\n"
+      "BD AD DD 25 66 D3 F5 07 07 56 D7 CE E2 72 C5 25\n"
+      "7D 0C E1 A7 6F 00 A8 DA AB 4B 54 43 09 64 A4 B6\n"
+      "52 38 2F B7 CC 01 DD 1C 03 27 03 47 BF DF E6 37\n"
+      "B0 ED 18 DC 51 0B D4 75 22 DF 50 7B 3C EB 37 39\n"
+      "1C 9B 6F 08 7B A7 05 AC 8C 43 F7 F1 DA 51 06 B3\n"
+      "82 45 3E C8 81 73 9E B0 A5 CF 76 96 AF 81 2C AC\n"
+      "01 2A 4A 58 4B 1D BE FF 1F 85 C2 27 DE F1 78 0B\n"
+      "\n"
+      "  Public Exponent (17 bits):\n"
+      "  01 00 01",
+      x509_certificate_model::ProcessRawSubjectPublicKeyInfo(spki_bytes));
+}
diff --git a/chrome/common/webui_url_constants.cc b/chrome/common/webui_url_constants.cc
index dad88d3..6731b459 100644
--- a/chrome/common/webui_url_constants.cc
+++ b/chrome/common/webui_url_constants.cc
@@ -33,7 +33,7 @@
 const char kChromeUIAppLauncherPageHost[] = "apps";
 const char kChromeUIAppsURL[] = "chrome://apps/";
 const char kChromeUIAppsWithDeprecationDialogURL[] =
-    "chrome://apps?showDeletionDialog=";
+    "chrome://apps?showDeletionDialog";
 const char kChromeUIAppsWithForceInstalledDeprecationDialogURL[] =
     "chrome://apps?showForceInstallDialog=";
 const char kChromeUIAutofillInternalsHost[] = "autofill-internals";
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn
index 81d2307..136daa89 100644
--- a/chrome/test/BUILD.gn
+++ b/chrome/test/BUILD.gn
@@ -6199,7 +6199,7 @@
       "../browser/policy/chrome_browser_cloud_management_controller_android_unittest.cc",
       "../browser/policy/client_data_delegate_android_unittest.cc",
       "../browser/profiles/android/profile_resolver_unittest.cc",
-      "../browser/reputation/safety_tip_message_delegate_unittest.cc",
+      "../browser/reputation/safety_tip_message_delegate_android_unittest.cc",
       "../browser/search/contextual_search_policy_handler_android_unittest.cc",
       "../browser/segmentation_platform/default_model/chrome_start_model_android_unittest.cc",
       "../browser/sharing/click_to_call/click_to_call_message_handler_android_unittest.cc",
@@ -7538,6 +7538,7 @@
       "../browser/extensions/api/streams_private/streams_private_manifest_unittest.cc",
       "../browser/extensions/api/tab_groups/tab_groups_api_unittest.cc",
       "../browser/extensions/api/tabs/tabs_api_unittest.cc",
+      "../browser/extensions/api/tabs/windows_util_unittest.cc",
       "../browser/extensions/api/web_authentication_proxy/value_conversions_unittest.cc",
       "../browser/extensions/api/web_navigation/frame_navigation_state_unittest.cc",
       "../browser/extensions/api/web_request/web_request_api_unittest.cc",
diff --git a/chrome/test/data/android/image_fetcher/icon.ico b/chrome/test/data/android/image_fetcher/icon.ico
new file mode 100644
index 0000000..3ec4c1c
--- /dev/null
+++ b/chrome/test/data/android/image_fetcher/icon.ico
Binary files differ
diff --git a/chrome/test/data/extensions/api_test/favicon/extension/test.js b/chrome/test/data/extensions/api_test/favicon/extension/test.js
index 5e3fa11..79122db 100644
--- a/chrome/test/data/extensions/api_test/favicon/extension/test.js
+++ b/chrome/test/data/extensions/api_test/favicon/extension/test.js
@@ -2,104 +2,73 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-class Favicon {
-  constructor(obj) {
-    // Page of the favicon. page_ or icon_ must be provided. page_ wins.
-    this.pageUrl;
-
-    // Optional. Favicon size in pixels. Backend default: 16.
-    this.size;
-
-    // Update this favicon object from constructor parameter object.
-    obj && Object.keys(obj).forEach(key => this[key] = obj[key]);
-  }
-
-  // Returns a constructed URL with properties set as query parameters.
-  getUrl(baseUrl) {
-    let result = [];
-    Object.keys(this).forEach(key => {
-      // The backend expects snake_case query parameters.
-      const snakeCase = key.replace(/[A-Z]/g, c => `_${c.toLowerCase()}`);
-      result.push(`${snakeCase}=${this[key]}`);
-    });
-    const url =
-        baseUrl ? baseUrl : `chrome-extension://${chrome.runtime.id}/_favicon/`;
-    return `${url}?${result.join('&')}`;
-  }
-};
+var port;
+var visitedPageUrl;
 
 window.onload =
     function() {
   chrome.test.runTests([
-    // Asynchronously fetch favicon.
-    async function all() {
-      const config = await chrome.test.getConfig();
-      const port = config.testServer.port;
+    async function init() {
+      port = (await chrome.test.getConfig()).testServer.port;
+      visitedPageUrl =
+          `http://www.example.com:${port}/extensions/favicon/test_file.html`;
+      chrome.test.succeed();
+    },
+
+    // Asynchronously fetch favicon in various ways.
+    function various() {
       const testCases = [
         [
-          'Load favicon using only pageUrl', true, new Favicon({
-            pageUrl: `http://www.example.com:${
-                port}/extensions/favicon/test_file.html`
-          })
+          'Load favicon using only pageUrl', true,
+          `_favicon/?pageUrl=${visitedPageUrl}`
         ],
         [
-          'Load favicon using multiple arguments', true, new Favicon({
-            pageUrl: `http://www.example.com:${
-                port}/extensions/favicon/test_file.html`,
-            size: 16,
-            scaleFactor: '1x'
-          })
+          'Load favicon using multiple arguments', true,
+          `_favicon/?pageUrl=${visitedPageUrl}&size=16&scaleFactor=1x`
         ],
         [
           'Get the default icon when a url hasn\'t been visited', true,
-          new Favicon({
-            pageUrl: `http://www.unvisited.com:${
-                port}/extensions/favicon/test_file.html`
-          })
+          `_favicon/?pageUrl=http://www.unvisited.com:${
+              port}/extensions/favicon/test_file.html`
         ],
         [
-          'Incorrect baseUrl', false, new Favicon({
-            pageUrl: `http://www.example.com:${
-                port}/extensions/favicon/test_file.html`
-          }),
-          `chrome-extension://${chrome.runtime.id}/_faviconbutnotreally/`
+          'Incorrect baseUrl', false,
+          `chrome-extension://${
+              chrome.runtime.id}/_faviconbutnotreally/?pageUrl=${
+              visitedPageUrl}`
         ],
         [
           'Slash not required before question mark query params', true,
-          new Favicon({
-            pageUrl: `http://www.example.com:${
-                port}/extensions/favicon/test_file.html`
-          }),
-          `chrome-extension://${chrome.runtime.id}/_favicon`
+          `chrome-extension://${chrome.runtime.id}/_favicon/?pageUrl=${
+              visitedPageUrl}`
+        ],
+        [
+          'pageUrl must be present and iconUrl is ignored', false,
+          `_favicon/?iconUrl=${visitedPageUrl}`
         ],
       ];
       let promises = [];
       testCases.forEach(testCase => {
         promises.push(new Promise(resolve => {
-          const [title, isOk, favicon, baseUrl = null] = testCase;
+          const [title, isOk, url] = testCase;
           const img = document.createElement('img');
           document.body.appendChild(img);
           img.onload = () => isOk ? resolve() : chrome.test.fail(title);
           img.onerror = () => isOk ? chrome.test.fail(title) : resolve();
-          img.src = favicon.getUrl(baseUrl);
+          img.src = url;
         }));
       });
       Promise.all(promises).then(() => chrome.test.succeed());
     },
 
     // Verify that the requested icon size is returned.
-    async function cachedResolutions() {
-      const port = (await chrome.test.getConfig()).testServer.port;
+    function cachedResolutions() {
       const pixelsArr = [16, 32, 64, 48];
       const promises = pixelsArr.map(pixels => {
         return new Promise((resolve, reject) => {
-          const favicon = new Favicon({
-            pageUrl: `http://www.example.com:${
-                port}/extensions/favicon/test_file.html`,
-            size: pixels
-          });
+          const url = `_favicon/?pageUrl=${visitedPageUrl}&size=${pixels}`;
           const image = new Image();
-          image.src = favicon.getUrl();
+          image.src = url;
           image.onload = () => {
             chrome.test.assertEq(pixels, image.height);
             chrome.test.assertEq(pixels, image.width);
@@ -111,15 +80,11 @@
     },
 
     // The default favicon size is 16.
-    async function defaultSize() {
-      const port = (await chrome.test.getConfig()).testServer.port;
+    function defaultSize() {
       const expected = 16;
-      const favicon = new Favicon({
-        pageUrl:
-            `http://www.example.com:${port}/extensions/favicon/test_file.html`
-      });
+      const url = `_favicon/?pageUrl=${visitedPageUrl}`;
       const image = new Image();
-      image.src = favicon.getUrl();
+      image.src = url;
       image.onload = () => {
         chrome.test.assertEq(expected, image.height);
         chrome.test.assertEq(expected, image.width);
@@ -128,20 +93,16 @@
     },
 
     // Verify uncached default icon resolutions. Supported sizes: 16, 32, 64.
-    async function uncachedDefaultResolutions() {
-      const port = (await chrome.test.getConfig()).testServer.port;
+    function uncachedDefaultResolutions() {
       const pixelsAndExpected =
           [[30, 16], [48, 32], [128, 64], [32, 32], [10, 16]];
       const promises = pixelsAndExpected.map(requestAndExpect => {
         return new Promise((resolve, reject) => {
           const [pixels, expected] = requestAndExpect;
-          const favicon = new Favicon({
-            pageUrl: `http://www.unvisited.com:${
-                port}/extensions/favicon/test_file.html`,
-            size: pixels
-          });
+          const url = `_favicon/?pageUrl=http://www.unvisited.com:${
+              port}/extensions/favicon/test_file.html&size=${pixels}`
           const image = new Image();
-          image.src = favicon.getUrl();
+          image.src = url;
           image.onload = () => {
             chrome.test.assertEq(expected, image.height);
             chrome.test.assertEq(expected, image.width);
@@ -157,15 +118,10 @@
     // files appear to consistently show up as a blank canvas, possibly due to
     // some of the graphics stack being stubbed out in browser tests. The bmp
     // here is a direct conversion of the .ico file.
-    async function bmpMatch() {
-      const port = (await chrome.test.getConfig()).testServer.port;
+    function bmpMatch() {
       const urls = [
         '_test_resources/favicon/favicon.bmp',
-        new Favicon({
-          pageUrl: `http://www.example.com:${
-              port}/extensions/favicon/test_file.html`,
-          size: 48
-        }).getUrl()
+        `_favicon/?pageUrl=${visitedPageUrl}&size=48`
       ];
       const promises = [];
       urls.forEach(url => promises.push(toDataURL(url)));
diff --git a/chrome/test/data/preload/many_anchors.html b/chrome/test/data/preload/many_anchors.html
new file mode 100644
index 0000000..3d7d19e
--- /dev/null
+++ b/chrome/test/data/preload/many_anchors.html
@@ -0,0 +1,9 @@
+<html>
+  <head>
+  </head>
+  <body>
+    <a id="anchor1_origin1" href="https://www.origin1.com/a">origin1/a</a>
+    <a id="anchor2_origin1" href="https://www.origin1.com/b">origin1/b</a>
+    <a id="anchor1_origin2" href="https://www.origin2.com/a">origin2/a</a>
+  </body>
+</html>
\ No newline at end of file
diff --git a/chrome/test/data/preload/one_anchor.html b/chrome/test/data/preload/one_anchor.html
index 2e2a28c6..660cb71 100644
--- a/chrome/test/data/preload/one_anchor.html
+++ b/chrome/test/data/preload/one_anchor.html
@@ -2,6 +2,6 @@
   <head>
   </head>
   <body>
-    <a id="anchor1" href="https://www.fakesearch.com/"><img height="6" width="1"></a>
+    <a id="anchor1" href="https://www.origin1.com/"><img height="6" width="1"></a>
   </body>
-</html>
\ No newline at end of file
+</html>
diff --git a/chrome/test/data/webui/print_preview/destination_item_test_cros.ts b/chrome/test/data/webui/print_preview/destination_item_test_cros.ts
index f9139c1..dbde9c6 100644
--- a/chrome/test/data/webui/print_preview/destination_item_test_cros.ts
+++ b/chrome/test/data/webui/print_preview/destination_item_test_cros.ts
@@ -5,11 +5,12 @@
 import {Destination, DestinationOrigin, NativeLayerCrosImpl, PrinterStatusReason, PrinterStatusSeverity, PrintPreviewDestinationListItemElement} from 'chrome://print/print_preview.js';
 import {assert} from 'chrome://resources/js/assert.m.js';
 import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
-
-import {assertEquals} from 'chrome://webui-test/chai_assert.js';
+import {assertEquals, assertFalse} from 'chrome://webui-test/chai_assert.js';
+import {MockController} from 'chrome://webui-test/mock_controller.js';
 import {waitBeforeNextRender} from 'chrome://webui-test/test_util.js';
 
 import {NativeLayerCrosStub} from './native_layer_cros_stub.js';
+import {FakeMediaQueryList} from './print_preview_test_utils.js';
 
 const destination_item_test_cros = {
   suiteName: 'DestinationItemTestCros',
@@ -27,6 +28,10 @@
 
   let nativeLayerCros: NativeLayerCrosStub;
 
+  let mockController: MockController;
+
+  let fakePrefersColorSchemeMediaQueryList: FakeMediaQueryList;
+
   function setNativeLayerPrinterStatusMap() {
     [{
       printerId: 'One',
@@ -49,7 +54,22 @@
                 status.printerId, status));
   }
 
+  // Mocks calls to window.matchMedia, returning false by default.
+  function configureMatchMediaMock() {
+    mockController = new MockController();
+    const matchMediaMock =
+        mockController.createFunctionMock(window, 'matchMedia');
+    fakePrefersColorSchemeMediaQueryList =
+        new FakeMediaQueryList('(prefers-color-scheme: dark)');
+    matchMediaMock.returnValue = fakePrefersColorSchemeMediaQueryList;
+    assertFalse(window.matchMedia('(prefers-color-scheme: dark)').matches);
+  }
+
   setup(function() {
+    // Mock configuration needs to happen before element added to UI to
+    // ensure iron-media-query uses mock.
+    configureMatchMediaMock();
+
     document.body.innerHTML = `
           <print-preview-destination-list-item id="listItem">
           </print-preview-destination-list-item>`;
@@ -67,6 +87,10 @@
     flush();
   });
 
+  teardown(function() {
+    mockController.reset();
+  });
+
   test(
       assert(destination_item_test_cros.TestNames.NewStatusUpdatesIcon),
       function() {
diff --git a/chrome/test/data/webui/print_preview/destination_select_test_cros.ts b/chrome/test/data/webui/print_preview/destination_select_test_cros.ts
index 6a51e67..78adf855 100644
--- a/chrome/test/data/webui/print_preview/destination_select_test_cros.ts
+++ b/chrome/test/data/webui/print_preview/destination_select_test_cros.ts
@@ -4,13 +4,13 @@
 
 import {Destination, DestinationOrigin, NativeLayerCrosImpl, NativeLayerImpl, PrinterStatusReason, PrinterStatusSeverity, PrintPreviewDestinationDropdownCrosElement, PrintPreviewDestinationSelectCrosElement} from 'chrome://print/print_preview.js';
 import {assert} from 'chrome://resources/js/assert.m.js';
-
 import {assertEquals, assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js';
+import {MockController} from 'chrome://webui-test/mock_controller.js';
 import {waitBeforeNextRender} from 'chrome://webui-test/test_util.js';
 
 import {NativeLayerCrosStub} from './native_layer_cros_stub.js';
 import {NativeLayerStub} from './native_layer_stub.js';
-import {getGoogleDriveDestination, getSaveAsPdfDestination} from './print_preview_test_utils.js';
+import {FakeMediaQueryList, getGoogleDriveDestination, getSaveAsPdfDestination} from './print_preview_test_utils.js';
 
 const printer_status_test_cros = {
   suiteName: 'PrinterStatusTestCros',
@@ -30,6 +30,10 @@
 
   let nativeLayerCros: NativeLayerCrosStub;
 
+  let mockController: MockController;
+
+  let fakePrefersColorSchemeMediaQueryList: FakeMediaQueryList;
+
   function setNativeLayerPrinterStatusMap() {
     [{
       printerId: 'ID1',
@@ -142,8 +146,20 @@
         escapeForwardSlahes(key)}`)!.querySelector('iron-icon')!.icon!;
   }
 
+  // Mocks calls to window.matchMedia, returning false by default.
+  function configureMatchMediaMock() {
+    mockController = new MockController();
+    const matchMediaMock =
+        mockController.createFunctionMock(window, 'matchMedia');
+    fakePrefersColorSchemeMediaQueryList =
+        new FakeMediaQueryList('(prefers-color-scheme: dark)');
+    matchMediaMock.returnValue = fakePrefersColorSchemeMediaQueryList;
+    assertFalse(window.matchMedia('(prefers-color-scheme: dark)').matches);
+  }
+
   setup(function() {
     document.body.innerHTML = '';
+    configureMatchMediaMock();
 
     // Stub out native layer.
     NativeLayerImpl.setInstance(new NativeLayerStub());
@@ -156,6 +172,10 @@
     document.body.appendChild(destinationSelect);
   });
 
+  teardown(function() {
+    mockController.reset();
+  });
+
   test(
       assert(printer_status_test_cros.TestNames.PrinterStatusUpdatesColor),
       function() {
diff --git a/chrome/test/data/webui/print_preview/print_preview_test_utils.ts b/chrome/test/data/webui/print_preview/print_preview_test_utils.ts
index a25513b..fd37994f 100644
--- a/chrome/test/data/webui/print_preview/print_preview_test_utils.ts
+++ b/chrome/test/data/webui/print_preview/print_preview_test_utils.ts
@@ -8,6 +8,7 @@
 import {loadTimeData} from 'chrome://resources/js/load_time_data.m.js';
 import {WebUIListenerMixin} from 'chrome://resources/js/web_ui_listener_mixin.js';
 import {PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
+import {assertEquals} from 'chrome://webui-test/chai_assert.js';
 import {eventToPromise} from 'chrome://webui-test/test_util.js';
 
 export function getDefaultInitialSettings(isPdf: boolean = false):
@@ -344,3 +345,46 @@
   select.dispatchEvent(new CustomEvent('change'));
   return eventToPromise('process-select-change', section);
 }
+
+// Fake MediaQueryList used in mocking response of |window.matchMedia|.
+export class FakeMediaQueryList extends EventTarget implements MediaQueryList {
+  private listener_: ((e: MediaQueryListEvent) => any)|null = null;
+  private matches_: boolean = false;
+  private media_: string;
+
+  constructor(media: string) {
+    super();
+    this.media_ = media;
+  }
+
+  addListener(listener: (e: MediaQueryListEvent) => any) {
+    this.listener_ = listener;
+  }
+
+  removeListener(listener: (e: MediaQueryListEvent) => any) {
+    assertEquals(listener, this.listener_);
+    this.listener_ = null;
+  }
+
+  onchange() {
+    if (this.listener_) {
+      this.listener_(new MediaQueryListEvent(
+          'change', {media: this.media_, matches: this.matches_}));
+    }
+  }
+
+  get media(): string {
+    return this.media_;
+  }
+
+  get matches(): boolean {
+    return this.matches_;
+  }
+
+  set matches(matches: boolean) {
+    if (this.matches_ !== matches) {
+      this.matches_ = matches;
+      this.onchange();
+    }
+  }
+}
diff --git a/chrome/test/enterprise/e2e/connector/realtime_reporting_bce/realtime_reporting_bce_test.py b/chrome/test/enterprise/e2e/connector/realtime_reporting_bce/realtime_reporting_bce_test.py
index 3bdc02d6..ba9e460 100644
--- a/chrome/test/enterprise/e2e/connector/realtime_reporting_bce/realtime_reporting_bce_test.py
+++ b/chrome/test/enterprise/e2e/connector/realtime_reporting_bce/realtime_reporting_bce_test.py
@@ -6,7 +6,11 @@
 import re
 from datetime import datetime
 
-from chrome_ent_test.infra.core import before_all, category, environment, test
+from chrome_ent_test.infra.core import before_all
+from chrome_ent_test.infra.core import category
+from chrome_ent_test.infra.core import environment
+from chrome_ent_test.infra.core import test
+
 from infra import ChromeEnterpriseTestCase
 from .reporting_server import RealTimeReportingServer
 
diff --git a/chrome/test/enterprise/e2e/connector/reporting_connector_splunk/reporting_connector_splunk_test.py b/chrome/test/enterprise/e2e/connector/reporting_connector_splunk/reporting_connector_splunk_test.py
index d32e355..85cbefe3 100644
--- a/chrome/test/enterprise/e2e/connector/reporting_connector_splunk/reporting_connector_splunk_test.py
+++ b/chrome/test/enterprise/e2e/connector/reporting_connector_splunk/reporting_connector_splunk_test.py
@@ -6,7 +6,10 @@
 import re
 from datetime import datetime
 
-from chrome_ent_test.infra.core import before_all, category, environment, test
+from chrome_ent_test.infra.core import before_all
+from chrome_ent_test.infra.core import category
+from chrome_ent_test.infra.core import environment
+from chrome_ent_test.infra.core import test
 from infra import ChromeEnterpriseTestCase
 from .splunk_server import SplunkApiService
 
diff --git a/chrome/test/enterprise/e2e/infra/chrome_ent_test_case.py b/chrome/test/enterprise/e2e/infra/chrome_ent_test_case.py
index 2e628ac..b6a90fca 100644
--- a/chrome/test/enterprise/e2e/infra/chrome_ent_test_case.py
+++ b/chrome/test/enterprise/e2e/infra/chrome_ent_test_case.py
@@ -149,6 +149,8 @@
 
     # run the test
     args = subprocess.list2cmdline(args)
+    self._pythonExecutablePath[instance_name] = (
+        r'C:\ProgramData\chocolatey\lib\python\tools\python.exe')
     cmd = r'%s %s %s' % (self._pythonExecutablePath[instance_name], file_name,
                          args)
     return self.RunCommand(instance_name, cmd).decode()
@@ -174,6 +176,8 @@
     # get any output from stdout because the output is buffered. When this
     # happens it makes debugging really hard.
     args = subprocess.list2cmdline(args)
+    self._pythonExecutablePath[instance_name] = (
+        r'C:\ProgramData\chocolatey\lib\python\tools\python.exe')
     ui_test_cmd = r'%s -u %s %s' % (self._pythonExecutablePath[instance_name],
                                     file_name, args)
     cmd = (r'%s c:\cel\supporting_files\run_ui_test.py --timeout %s -- %s') % (
diff --git a/chrome/test/enterprise/e2e/policy/__init__.py b/chrome/test/enterprise/e2e/policy/__init__.py
index a9b499e72..5d82014 100644
--- a/chrome/test/enterprise/e2e/policy/__init__.py
+++ b/chrome/test/enterprise/e2e/policy/__init__.py
@@ -22,5 +22,4 @@
 from .url_allowlist.url_allowlist import *
 from .user_data_dir.user_data_dir import *
 from .webprotect_file_download.webprotect_file_download import *
-from .webprotect_bulk_text_entry.webprotect_bulk_text_entry import *
 from .youtube_restrict.youtube_restrict import *
diff --git a/chrome/test/enterprise/e2e/policy/allow_deleting_browser_history/allow_deleting_browser_history.py b/chrome/test/enterprise/e2e/policy/allow_deleting_browser_history/allow_deleting_browser_history.py
index 4f8d726..9a68ff0c 100644
--- a/chrome/test/enterprise/e2e/policy/allow_deleting_browser_history/allow_deleting_browser_history.py
+++ b/chrome/test/enterprise/e2e/policy/allow_deleting_browser_history/allow_deleting_browser_history.py
@@ -4,7 +4,9 @@
 
 import os
 
-from chrome_ent_test.infra.core import before_all, environment, test
+from chrome_ent_test.infra.core import before_all
+from chrome_ent_test.infra.core import environment
+from chrome_ent_test.infra.core import test
 from infra import ChromeEnterpriseTestCase
 
 
diff --git a/chrome/test/enterprise/e2e/policy/apps_shortcut/apps_shortcut.py b/chrome/test/enterprise/e2e/policy/apps_shortcut/apps_shortcut.py
index 38169ae6..4639362 100644
--- a/chrome/test/enterprise/e2e/policy/apps_shortcut/apps_shortcut.py
+++ b/chrome/test/enterprise/e2e/policy/apps_shortcut/apps_shortcut.py
@@ -3,7 +3,9 @@
 # found in the LICENSE file.
 
 import os
-from chrome_ent_test.infra.core import environment, before_all, test
+from chrome_ent_test.infra.core import before_all
+from chrome_ent_test.infra.core import environment
+from chrome_ent_test.infra.core import test
 from infra import ChromeEnterpriseTestCase
 
 
diff --git a/chrome/test/enterprise/e2e/policy/bookmarkbar_enabled/bookmarkbar_enabled.py b/chrome/test/enterprise/e2e/policy/bookmarkbar_enabled/bookmarkbar_enabled.py
index 84948f90..ab0820d 100644
--- a/chrome/test/enterprise/e2e/policy/bookmarkbar_enabled/bookmarkbar_enabled.py
+++ b/chrome/test/enterprise/e2e/policy/bookmarkbar_enabled/bookmarkbar_enabled.py
@@ -6,7 +6,9 @@
 import os
 from absl import flags
 
-from chrome_ent_test.infra.core import environment, before_all, test
+from chrome_ent_test.infra.core import before_all
+from chrome_ent_test.infra.core import environment
+from chrome_ent_test.infra.core import test
 from infra import ChromeEnterpriseTestCase
 
 FLAGS = flags.FLAGS
diff --git a/chrome/test/enterprise/e2e/policy/cloud_management_enrollment_token/cloud_enrollment_webdriver.py b/chrome/test/enterprise/e2e/policy/cloud_management_enrollment_token/cloud_enrollment_webdriver.py
index 80764fc..5a7da58 100644
--- a/chrome/test/enterprise/e2e/policy/cloud_management_enrollment_token/cloud_enrollment_webdriver.py
+++ b/chrome/test/enterprise/e2e/policy/cloud_management_enrollment_token/cloud_enrollment_webdriver.py
@@ -9,15 +9,6 @@
 import test_util
 
 
-def getElementFromShadowRoot(driver, element, selector):
-  if element is None:
-    return None
-  else:
-    return driver.execute_script(
-        "return arguments[0].shadowRoot.querySelector(arguments[1])", element,
-        selector)
-
-
 def main(argv):
   options = webdriver.ChromeOptions()
   os.environ["CHROME_LOG_FILE"] = r"c:\temp\chrome_log.txt"
@@ -33,13 +24,10 @@
     driver.find_element_by_id('reload-policies').click
     # Give the page 2 seconds to render the legend
     time.sleep(2)
-    status_box = driver.find_element_by_css_selector("status-box")
-    el = getElementFromShadowRoot(driver, status_box, "fieldset")
-
-    print(el.find_element_by_class_name('legend').text)
-    print(el.find_element_by_class_name('machine-enrollment-name').text)
-    print(el.find_element_by_class_name('machine-enrollment-token').text)
-    print(el.find_element_by_class_name('status').text)
+    print(driver.find_element_by_class_name('legend').text)
+    print(driver.find_element_by_class_name('machine-enrollment-name').text)
+    print(driver.find_element_by_class_name('machine-enrollment-token').text)
+    print(driver.find_element_by_class_name('status').text)
   except Exception as error:
     print(error)
   finally:
diff --git a/chrome/test/enterprise/e2e/policy/cloud_management_enrollment_token/cloud_management_enrollment_token.py b/chrome/test/enterprise/e2e/policy/cloud_management_enrollment_token/cloud_management_enrollment_token.py
index f57d195..b29ea6d 100644
--- a/chrome/test/enterprise/e2e/policy/cloud_management_enrollment_token/cloud_management_enrollment_token.py
+++ b/chrome/test/enterprise/e2e/policy/cloud_management_enrollment_token/cloud_management_enrollment_token.py
@@ -6,8 +6,10 @@
 import os
 
 from infra import ChromeEnterpriseTestCase
-from chrome_ent_test.infra.core import before_all, category, environment, test
-
+from chrome_ent_test.infra.core import before_all
+from chrome_ent_test.infra.core import category
+from chrome_ent_test.infra.core import environment
+from chrome_ent_test.infra.core import test
 
 FLAGS = flags.FLAGS
 flags.DEFINE_string(
diff --git a/chrome/test/enterprise/e2e/policy/default_search_provider/default_search_provider.py b/chrome/test/enterprise/e2e/policy/default_search_provider/default_search_provider.py
index fd2e045..1688de3b 100644
--- a/chrome/test/enterprise/e2e/policy/default_search_provider/default_search_provider.py
+++ b/chrome/test/enterprise/e2e/policy/default_search_provider/default_search_provider.py
@@ -3,10 +3,11 @@
 # found in the LICENSE file.
 
 import os
-from chrome_ent_test.infra.core import environment, before_all, test
+from chrome_ent_test.infra.core import before_all
+from chrome_ent_test.infra.core import environment
+from chrome_ent_test.infra.core import test
 from infra import ChromeEnterpriseTestCase
 
-
 @environment(file="../policy_test.asset.textpb")
 class DefaultSearchProviderTest(ChromeEnterpriseTestCase):
   """Test the DefaultSearchProviderEnabled,
diff --git a/chrome/test/enterprise/e2e/policy/extension_allowlist/extension_allowlist.py b/chrome/test/enterprise/e2e/policy/extension_allowlist/extension_allowlist.py
index 9b3d34d3..9374e64 100644
--- a/chrome/test/enterprise/e2e/policy/extension_allowlist/extension_allowlist.py
+++ b/chrome/test/enterprise/e2e/policy/extension_allowlist/extension_allowlist.py
@@ -4,7 +4,9 @@
 
 import logging
 import os
-from chrome_ent_test.infra.core import environment, before_all, test
+from chrome_ent_test.infra.core import before_all
+from chrome_ent_test.infra.core import environment
+from chrome_ent_test.infra.core import test
 from infra import ChromeEnterpriseTestCase
 
 
diff --git a/chrome/test/enterprise/e2e/policy/extension_blocklist/extension_blocklist.py b/chrome/test/enterprise/e2e/policy/extension_blocklist/extension_blocklist.py
index ff25f2cd..95458cfc 100644
--- a/chrome/test/enterprise/e2e/policy/extension_blocklist/extension_blocklist.py
+++ b/chrome/test/enterprise/e2e/policy/extension_blocklist/extension_blocklist.py
@@ -4,7 +4,9 @@
 
 import logging
 import os
-from chrome_ent_test.infra.core import environment, before_all, test
+from chrome_ent_test.infra.core import before_all
+from chrome_ent_test.infra.core import environment
+from chrome_ent_test.infra.core import test
 from infra import ChromeEnterpriseTestCase
 
 
diff --git a/chrome/test/enterprise/e2e/policy/extension_forcelist/extension_forcelist.py b/chrome/test/enterprise/e2e/policy/extension_forcelist/extension_forcelist.py
index 59f3ffe..0190f9b 100644
--- a/chrome/test/enterprise/e2e/policy/extension_forcelist/extension_forcelist.py
+++ b/chrome/test/enterprise/e2e/policy/extension_forcelist/extension_forcelist.py
@@ -3,7 +3,9 @@
 # found in the LICENSE file.
 
 import os
-from chrome_ent_test.infra.core import environment, before_all, test
+from chrome_ent_test.infra.core import before_all
+from chrome_ent_test.infra.core import environment
+from chrome_ent_test.infra.core import test
 from infra import ChromeEnterpriseTestCase
 
 
diff --git a/chrome/test/enterprise/e2e/policy/force_google_safe_search/force_google_safe_search.py b/chrome/test/enterprise/e2e/policy/force_google_safe_search/force_google_safe_search.py
index 2e8fca74..737c078 100644
--- a/chrome/test/enterprise/e2e/policy/force_google_safe_search/force_google_safe_search.py
+++ b/chrome/test/enterprise/e2e/policy/force_google_safe_search/force_google_safe_search.py
@@ -5,7 +5,9 @@
 import os
 import logging
 from absl import flags
-from chrome_ent_test.infra.core import environment, before_all, test
+from chrome_ent_test.infra.core import before_all
+from chrome_ent_test.infra.core import environment
+from chrome_ent_test.infra.core import test
 from infra import ChromeEnterpriseTestCase
 
 FLAGS = flags.FLAGS
diff --git a/chrome/test/enterprise/e2e/policy/fullscreen_allowed/fullscreen_allowed.py b/chrome/test/enterprise/e2e/policy/fullscreen_allowed/fullscreen_allowed.py
index 9b5339ca..d11519d 100644
--- a/chrome/test/enterprise/e2e/policy/fullscreen_allowed/fullscreen_allowed.py
+++ b/chrome/test/enterprise/e2e/policy/fullscreen_allowed/fullscreen_allowed.py
@@ -3,7 +3,9 @@
 # found in the LICENSE file.
 
 import os
-from chrome_ent_test.infra.core import environment, before_all, test
+from chrome_ent_test.infra.core import before_all
+from chrome_ent_test.infra.core import environment
+from chrome_ent_test.infra.core import test
 from infra import ChromeEnterpriseTestCase
 
 
diff --git a/chrome/test/enterprise/e2e/policy/homepage/homepage.py b/chrome/test/enterprise/e2e/policy/homepage/homepage.py
index 4aa94613..74fd576 100644
--- a/chrome/test/enterprise/e2e/policy/homepage/homepage.py
+++ b/chrome/test/enterprise/e2e/policy/homepage/homepage.py
@@ -5,7 +5,9 @@
 import os
 import re
 from absl import flags
-from chrome_ent_test.infra.core import environment, before_all, test
+from chrome_ent_test.infra.core import before_all
+from chrome_ent_test.infra.core import environment
+from chrome_ent_test.infra.core import test
 from infra import ChromeEnterpriseTestCase
 
 FLAGS = flags.FLAGS
@@ -52,7 +54,7 @@
 
     # verify the home page is the value of HomepageLocation
     homepage = self._getHomepageLocation(self.win_config['client'])
-    self.assertEqual(homepage, 'http://www.example.com/')
+    self.assertIn("http://www.example.com/", homepage)
 
   @test
   def test_HomepageIsNewTab(self):
diff --git a/chrome/test/enterprise/e2e/policy/password_manager_enabled/password_manager_enabled.py b/chrome/test/enterprise/e2e/policy/password_manager_enabled/password_manager_enabled.py
index 565272fa..126d64e 100644
--- a/chrome/test/enterprise/e2e/policy/password_manager_enabled/password_manager_enabled.py
+++ b/chrome/test/enterprise/e2e/policy/password_manager_enabled/password_manager_enabled.py
@@ -3,7 +3,9 @@
 # found in the LICENSE file.
 
 import os
-from chrome_ent_test.infra.core import environment, before_all, test
+from chrome_ent_test.infra.core import before_all
+from chrome_ent_test.infra.core import environment
+from chrome_ent_test.infra.core import test
 from infra import ChromeEnterpriseTestCase
 
 
diff --git a/chrome/test/enterprise/e2e/policy/popups_allowed/popups_allowed.py b/chrome/test/enterprise/e2e/policy/popups_allowed/popups_allowed.py
index 1c67631..59decac 100644
--- a/chrome/test/enterprise/e2e/policy/popups_allowed/popups_allowed.py
+++ b/chrome/test/enterprise/e2e/policy/popups_allowed/popups_allowed.py
@@ -6,7 +6,9 @@
 import os
 from absl import flags
 
-from chrome_ent_test.infra.core import environment, before_all, test
+from chrome_ent_test.infra.core import before_all
+from chrome_ent_test.infra.core import environment
+from chrome_ent_test.infra.core import test
 from infra import ChromeEnterpriseTestCase
 
 FLAGS = flags.FLAGS
diff --git a/chrome/test/enterprise/e2e/policy/restore_on_startup/restore_on_startup.py b/chrome/test/enterprise/e2e/policy/restore_on_startup/restore_on_startup.py
index e9d6632d..74ba15f 100644
--- a/chrome/test/enterprise/e2e/policy/restore_on_startup/restore_on_startup.py
+++ b/chrome/test/enterprise/e2e/policy/restore_on_startup/restore_on_startup.py
@@ -8,7 +8,9 @@
 
 from absl import flags
 
-from chrome_ent_test.infra.core import environment, before_all, test
+from chrome_ent_test.infra.core import before_all
+from chrome_ent_test.infra.core import environment
+from chrome_ent_test.infra.core import test
 from infra import ChromeEnterpriseTestCase
 
 FLAGS = flags.FLAGS
diff --git a/chrome/test/enterprise/e2e/policy/safe_browsing/safe_browsing.py b/chrome/test/enterprise/e2e/policy/safe_browsing/safe_browsing.py
index fd6688c..ed59b6fd 100644
--- a/chrome/test/enterprise/e2e/policy/safe_browsing/safe_browsing.py
+++ b/chrome/test/enterprise/e2e/policy/safe_browsing/safe_browsing.py
@@ -3,7 +3,10 @@
 # found in the LICENSE file.
 
 import os
-from chrome_ent_test.infra.core import before_all, category, environment, test
+from chrome_ent_test.infra.core import before_all
+from chrome_ent_test.infra.core import category
+from chrome_ent_test.infra.core import environment
+from chrome_ent_test.infra.core import test
 from infra import ChromeEnterpriseTestCase
 
 
diff --git a/chrome/test/enterprise/e2e/policy/url_allowlist/url_allowlist.py b/chrome/test/enterprise/e2e/policy/url_allowlist/url_allowlist.py
index ed6111e..dfc2f0b0 100644
--- a/chrome/test/enterprise/e2e/policy/url_allowlist/url_allowlist.py
+++ b/chrome/test/enterprise/e2e/policy/url_allowlist/url_allowlist.py
@@ -4,7 +4,9 @@
 
 import logging
 import os
-from chrome_ent_test.infra.core import environment, before_all, test
+from chrome_ent_test.infra.core import before_all
+from chrome_ent_test.infra.core import environment
+from chrome_ent_test.infra.core import test
 from infra import ChromeEnterpriseTestCase
 
 
diff --git a/chrome/test/enterprise/e2e/policy/url_blocklist/url_blocklist.py b/chrome/test/enterprise/e2e/policy/url_blocklist/url_blocklist.py
index 418e5b5..2d56f9b2 100644
--- a/chrome/test/enterprise/e2e/policy/url_blocklist/url_blocklist.py
+++ b/chrome/test/enterprise/e2e/policy/url_blocklist/url_blocklist.py
@@ -4,7 +4,9 @@
 
 import logging
 import os
-from chrome_ent_test.infra.core import environment, before_all, test
+from chrome_ent_test.infra.core import before_all
+from chrome_ent_test.infra.core import environment
+from chrome_ent_test.infra.core import test
 from infra import ChromeEnterpriseTestCase
 
 
diff --git a/chrome/test/enterprise/e2e/policy/user_data_dir/user_data_dir.py b/chrome/test/enterprise/e2e/policy/user_data_dir/user_data_dir.py
index be93345..1438ab4 100644
--- a/chrome/test/enterprise/e2e/policy/user_data_dir/user_data_dir.py
+++ b/chrome/test/enterprise/e2e/policy/user_data_dir/user_data_dir.py
@@ -5,7 +5,9 @@
 import logging
 import os
 
-from chrome_ent_test.infra.core import environment, before_all, test
+from chrome_ent_test.infra.core import before_all
+from chrome_ent_test.infra.core import environment
+from chrome_ent_test.infra.core import test
 from infra import ChromeEnterpriseTestCase
 
 
diff --git a/chrome/test/enterprise/e2e/policy/webprotect_bulk_text_entry/__init__.py b/chrome/test/enterprise/e2e/policy/webprotect_bulk_text_entry/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/chrome/test/enterprise/e2e/policy/webprotect_bulk_text_entry/__init__.py
+++ /dev/null
diff --git a/chrome/test/enterprise/e2e/policy/webprotect_bulk_text_entry/webprotect_bulk_text_entry.py b/chrome/test/enterprise/e2e/policy/webprotect_bulk_text_entry/webprotect_bulk_text_entry.py
deleted file mode 100644
index 9bfa77d..0000000
--- a/chrome/test/enterprise/e2e/policy/webprotect_bulk_text_entry/webprotect_bulk_text_entry.py
+++ /dev/null
@@ -1,66 +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.
-
-import os
-from chrome_ent_test.infra.core import category, environment, before_all, test
-from infra import ChromeEnterpriseTestCase
-
-
-@category("chrome_only")
-@environment(file="../webprotect_test.asset.textpb")
-class WebProtectBulkTextEntryTest(ChromeEnterpriseTestCase):
-  """Test the WebProtect client behaviour of web content upload.
-
-     The purpose of these tests is to catch chrome
-     client UI regression when the user pastes text into the web.
-
-  """
-  blocked_text = "This data has sensitive or dangerous content. " \
-               "Remove this content and try again."
-  timeout = 300
-
-  @before_all
-  def setup(self):
-    self.InstallChrome('webprotect-1')
-    self.EnableUITest('webprotect-1')
-    self.InstallChrome('webprotect-2')
-    self.EnableUITest('webprotect-2')
-
-  @test
-  def test_paste_from_clipboard(self):
-    # Enroll to a OU where SSN paste is blocked
-    self.SetPolicy('win2016-dc', r'CloudManagementEnrollmentToken',
-                   '3dd4fde1-914f-4cad-83c0-c1470954066d', 'String')
-    instance_name = 'webprotect-1'
-    self.RunCommand(instance_name, 'gpupdate /force')
-    text = "NormalText"
-
-    local_dir = os.path.dirname(os.path.abspath(__file__))
-    args = ['--text', text]
-    output = self.RunUITest(
-        instance_name,
-        os.path.join(local_dir, 'webprotect_bulk_text_entry_webdriver.py'),
-        self.timeout, args)
-
-    self.assertNotIn(self.blocked_text, output)
-    self.assertIn("Paste is done", output)
-
-  @test
-  def test_paste_SSN_from_clipboard(self):
-    # Enroll to a OU where SSN paste is blocked
-    self.SetPolicy('win2016-dc', r'CloudManagementEnrollmentToken',
-                   '3dd4fde1-914f-4cad-83c0-c1470954066d', 'String')
-    instance_name = 'webprotect-2'
-    self.RunCommand(instance_name, 'gpupdate /force')
-    text = "314-20-9301"
-
-    local_dir = os.path.dirname(os.path.abspath(__file__))
-    args = ['--text', text]
-    output = self.RunUITest(
-        instance_name,
-        os.path.join(local_dir, 'webprotect_bulk_text_entry_webdriver.py'),
-        self.timeout, args)
-
-    self.assertIn(self.blocked_text, output)
-    self.assertIn("Paste is blocked", output)
diff --git a/chrome/test/enterprise/e2e/policy/webprotect_bulk_text_entry/webprotect_bulk_text_entry_webdriver.py b/chrome/test/enterprise/e2e/policy/webprotect_bulk_text_entry/webprotect_bulk_text_entry_webdriver.py
deleted file mode 100644
index c932c4eb..0000000
--- a/chrome/test/enterprise/e2e/policy/webprotect_bulk_text_entry/webprotect_bulk_text_entry_webdriver.py
+++ /dev/null
@@ -1,65 +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.
-
-import os
-import time
-
-from absl import app, flags
-from selenium import webdriver
-from selenium.webdriver.common.keys import Keys
-from pywinauto.application import Application
-import pyperclip
-
-import test_util
-
-FLAGS = flags.FLAGS
-
-flags.DEFINE_string('text', None, 'The text to paste in form.')
-flags.mark_flag_as_required('text')
-
-paste_url = "https://storage.googleapis.com/testfiles-webprotect/bugbash/index.html"
-
-
-def main(argv):
-  os.system('start chrome --remote-debugging-port=9222')
-  options = webdriver.ChromeOptions()
-  options.add_argument("--force-renderer-accessibility")
-  options.add_experimental_option("debuggerAddress", "localhost:9222")
-  driver = test_util.create_chrome_webdriver(chrome_options=options)
-
-  app = Application(backend="uia")
-  app.connect(title_re='.*Chrome|.*Chromium')
-  # Wait for browser enrolling
-  time.sleep(15)
-
-  # Click reload policy to pull cloud policies from the server side
-  policy_url = "chrome://policy"
-  driver.get(policy_url)
-  driver.find_element_by_id('reload-policies').click
-
-  # paste content from clipboard to the form
-  driver.get(paste_url)
-  form = driver.find_element_by_name('sensitive_data_scan')
-  pyperclip.copy(FLAGS.text)
-  form.send_keys(Keys.CONTROL, "v")
-  time.sleep(20)
-
-  try:
-    # Print the UI elements which contain text
-    for desc in app.top_window().descendants():
-      print(desc.window_text())
-    # Check if the paste is completed in form
-    if form.get_attribute('value') == FLAGS.text:
-      print("Paste is done")
-    else:
-      print("Paste is blocked")
-  except:
-    print("Error occurs")
-  finally:
-    driver.quit()
-    os.system('taskkill /f /im chrome.exe')
-
-
-if __name__ == '__main__':
-  app.run(main)
diff --git a/chrome/test/enterprise/e2e/policy/webprotect_file_download/webprotect_file_download.py b/chrome/test/enterprise/e2e/policy/webprotect_file_download/webprotect_file_download.py
index 3693ec7..9a3cb35 100644
--- a/chrome/test/enterprise/e2e/policy/webprotect_file_download/webprotect_file_download.py
+++ b/chrome/test/enterprise/e2e/policy/webprotect_file_download/webprotect_file_download.py
@@ -3,7 +3,10 @@
 # found in the LICENSE file.
 
 import os
-from chrome_ent_test.infra.core import category, environment, before_all, test
+from chrome_ent_test.infra.core import before_all
+from chrome_ent_test.infra.core import category
+from chrome_ent_test.infra.core import environment
+from chrome_ent_test.infra.core import test
 from infra import ChromeEnterpriseTestCase
 
 
diff --git a/chrome/test/enterprise/e2e/policy/youtube_restrict/youtube_restrict.py b/chrome/test/enterprise/e2e/policy/youtube_restrict/youtube_restrict.py
index 0766797..826bf7a6 100644
--- a/chrome/test/enterprise/e2e/policy/youtube_restrict/youtube_restrict.py
+++ b/chrome/test/enterprise/e2e/policy/youtube_restrict/youtube_restrict.py
@@ -4,7 +4,9 @@
 
 import logging
 import os
-from chrome_ent_test.infra.core import environment, before_all, test
+from chrome_ent_test.infra.core import before_all
+from chrome_ent_test.infra.core import environment
+from chrome_ent_test.infra.core import test
 from infra import ChromeEnterpriseTestCase
 
 
diff --git a/chromeos/dbus/missive/fake_missive_client.h b/chromeos/dbus/missive/fake_missive_client.h
index e501467..898f7c2 100644
--- a/chromeos/dbus/missive/fake_missive_client.h
+++ b/chromeos/dbus/missive/fake_missive_client.h
@@ -13,8 +13,9 @@
 namespace chromeos {
 
 // Fake implementation of MissiveClient. This is currently a no-op fake.
-class FakeMissiveClient : public MissiveClient,
-                          public MissiveClient::TestInterface {
+class COMPONENT_EXPORT(MISSIVE) FakeMissiveClient
+    : public MissiveClient,
+      public MissiveClient::TestInterface {
  public:
   FakeMissiveClient();
   ~FakeMissiveClient() override;
diff --git a/chromeos/network/auto_connect_handler.cc b/chromeos/network/auto_connect_handler.cc
index 2932460..78f13fe 100644
--- a/chromeos/network/auto_connect_handler.cc
+++ b/chromeos/network/auto_connect_handler.cc
@@ -113,8 +113,6 @@
     client_cert_resolver_->RemoveObserver(this);
   if (network_connection_handler_)
     network_connection_handler_->RemoveObserver(this);
-  if (network_state_handler_)
-    network_state_handler_->RemoveObserver(this, FROM_HERE);
   if (managed_configuration_handler_)
     managed_configuration_handler_->RemoveObserver(this);
 }
@@ -136,8 +134,9 @@
     network_connection_handler_->AddObserver(this);
 
   network_state_handler_ = network_state_handler;
-  if (network_state_handler_)
-    network_state_handler_->AddObserver(this, FROM_HERE);
+  if (network_state_handler_) {
+    network_state_handler_observer_.Observe(network_state_handler_);
+  }
 
   managed_configuration_handler_ = managed_network_configuration_handler;
   if (managed_configuration_handler_)
diff --git a/chromeos/network/auto_connect_handler.h b/chromeos/network/auto_connect_handler.h
index 4bfeb9cd..aa1a628d 100644
--- a/chromeos/network/auto_connect_handler.h
+++ b/chromeos/network/auto_connect_handler.h
@@ -10,11 +10,13 @@
 #include "base/component_export.h"
 #include "base/memory/weak_ptr.h"
 #include "base/observer_list.h"
+#include "base/scoped_observation.h"
 #include "chromeos/login/login_state/login_state.h"
 #include "chromeos/network/client_cert_resolver.h"
 #include "chromeos/network/network_connection_observer.h"
 #include "chromeos/network/network_handler.h"
 #include "chromeos/network/network_policy_observer.h"
+#include "chromeos/network/network_state_handler.h"
 #include "chromeos/network/network_state_handler_observer.h"
 
 namespace chromeos {
@@ -136,6 +138,9 @@
   ClientCertResolver* client_cert_resolver_;
   NetworkConnectionHandler* network_connection_handler_;
   NetworkStateHandler* network_state_handler_;
+  base::ScopedObservation<chromeos::NetworkStateHandler,
+                          chromeos::NetworkStateHandlerObserver>
+      network_state_handler_observer_{this};
   ManagedNetworkConfigurationHandler* managed_configuration_handler_;
 
   // Whether a request to connect to the best network is pending. If true, once
diff --git a/chromeos/network/cellular_connection_handler.cc b/chromeos/network/cellular_connection_handler.cc
index 65eb838..94cb1f5 100644
--- a/chromeos/network/cellular_connection_handler.cc
+++ b/chromeos/network/cellular_connection_handler.cc
@@ -17,7 +17,6 @@
 #include "chromeos/network/network_connection_handler.h"
 #include "chromeos/network/network_event_log.h"
 #include "chromeos/network/network_state.h"
-#include "chromeos/network/network_state_handler.h"
 #include "chromeos/network/network_type_pattern.h"
 
 namespace chromeos {
@@ -127,10 +126,7 @@
 
 CellularConnectionHandler::CellularConnectionHandler() = default;
 
-CellularConnectionHandler::~CellularConnectionHandler() {
-  if (network_state_handler_)
-    network_state_handler_->RemoveObserver(this, FROM_HERE);
-}
+CellularConnectionHandler::~CellularConnectionHandler() = default;
 
 void CellularConnectionHandler::Init(
     NetworkStateHandler* network_state_handler,
@@ -140,7 +136,7 @@
   cellular_inhibitor_ = cellular_inhibitor;
   cellular_esim_profile_handler_ = cellular_esim_profile_handler;
 
-  network_state_handler_->AddObserver(this, FROM_HERE);
+  network_state_handler_observer_.Observe(network_state_handler_);
 }
 
 void CellularConnectionHandler::PrepareExistingCellularNetworkForConnection(
diff --git a/chromeos/network/cellular_connection_handler.h b/chromeos/network/cellular_connection_handler.h
index 77f66f8..e03a9b85 100644
--- a/chromeos/network/cellular_connection_handler.h
+++ b/chromeos/network/cellular_connection_handler.h
@@ -10,9 +10,11 @@
 #include "base/containers/queue.h"
 #include "base/gtest_prod_util.h"
 #include "base/memory/weak_ptr.h"
+#include "base/scoped_observation.h"
 #include "base/timer/timer.h"
 #include "chromeos/dbus/hermes/hermes_response_status.h"
 #include "chromeos/network/cellular_inhibitor.h"
+#include "chromeos/network/network_state_handler.h"
 #include "chromeos/network/network_state_handler_observer.h"
 #include "dbus/object_path.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
@@ -21,7 +23,6 @@
 
 class CellularESimProfileHandler;
 class CellularInhibitor;
-class NetworkStateHandler;
 class NetworkState;
 
 // Prepares cellular networks for connection. Before we can connect to a
@@ -196,6 +197,9 @@
   base::OneShotTimer timer_;
 
   NetworkStateHandler* network_state_handler_ = nullptr;
+  base::ScopedObservation<chromeos::NetworkStateHandler,
+                          chromeos::NetworkStateHandlerObserver>
+      network_state_handler_observer_{this};
   CellularInhibitor* cellular_inhibitor_ = nullptr;
   CellularESimProfileHandler* cellular_esim_profile_handler_ = nullptr;
 
diff --git a/chromeos/network/cellular_esim_profile_handler_impl.cc b/chromeos/network/cellular_esim_profile_handler_impl.cc
index 39008df..e9e339f4 100644
--- a/chromeos/network/cellular_esim_profile_handler_impl.cc
+++ b/chromeos/network/cellular_esim_profile_handler_impl.cc
@@ -51,9 +51,7 @@
 
 CellularESimProfileHandlerImpl::CellularESimProfileHandlerImpl() = default;
 
-CellularESimProfileHandlerImpl::~CellularESimProfileHandlerImpl() {
-  network_state_handler()->RemoveObserver(this, FROM_HERE);
-}
+CellularESimProfileHandlerImpl::~CellularESimProfileHandlerImpl() = default;
 
 void CellularESimProfileHandlerImpl::DeviceListChanged() {
   if (!device_prefs_)
@@ -63,7 +61,7 @@
 }
 
 void CellularESimProfileHandlerImpl::InitInternal() {
-  network_state_handler()->AddObserver(this, FROM_HERE);
+  network_state_handler_observer_.Observe(network_state_handler());
 }
 
 std::vector<CellularESimProfile>
diff --git a/chromeos/network/cellular_esim_profile_handler_impl.h b/chromeos/network/cellular_esim_profile_handler_impl.h
index 7d0c694..0fcaecb8 100644
--- a/chromeos/network/cellular_esim_profile_handler_impl.h
+++ b/chromeos/network/cellular_esim_profile_handler_impl.h
@@ -8,7 +8,9 @@
 #include "base/component_export.h"
 #include "base/containers/flat_set.h"
 #include "base/memory/weak_ptr.h"
+#include "base/scoped_observation.h"
 #include "chromeos/network/cellular_esim_profile_handler.h"
+#include "chromeos/network/network_state_handler.h"
 #include "chromeos/network/network_state_handler_observer.h"
 
 class PrefService;
@@ -95,6 +97,10 @@
   // Initialized to null and set once SetDevicePrefs() is called.
   PrefService* device_prefs_ = nullptr;
 
+  base::ScopedObservation<chromeos::NetworkStateHandler,
+                          chromeos::NetworkStateHandlerObserver>
+      network_state_handler_observer_{this};
+
   base::flat_set<std::string> paths_pending_auto_refresh_;
 
   base::WeakPtrFactory<CellularESimProfileHandlerImpl> weak_ptr_factory_{this};
diff --git a/chromeos/network/cellular_esim_uninstall_handler.cc b/chromeos/network/cellular_esim_uninstall_handler.cc
index f0784e8..b3c0f4e 100644
--- a/chromeos/network/cellular_esim_uninstall_handler.cc
+++ b/chromeos/network/cellular_esim_uninstall_handler.cc
@@ -60,7 +60,7 @@
   network_connection_handler_ = network_connection_handler;
   network_state_handler_ = network_state_handler;
 
-  network_state_handler->AddObserver(this, FROM_HERE);
+  network_state_handler_observer_.Observe(network_state_handler_);
 }
 
 void CellularESimUninstallHandler::UninstallESim(
@@ -96,10 +96,8 @@
 }
 
 void CellularESimUninstallHandler::OnShuttingDown() {
-  if (network_state_handler_) {
-    network_state_handler_->RemoveObserver(this, FROM_HERE);
-    network_state_handler_ = nullptr;
-  }
+  network_state_handler_observer_.Reset();
+  network_state_handler_ = nullptr;
 }
 
 void CellularESimUninstallHandler::ProcessPendingUninstallRequests() {
diff --git a/chromeos/network/cellular_esim_uninstall_handler.h b/chromeos/network/cellular_esim_uninstall_handler.h
index 3bd4c6a..de76a074 100644
--- a/chromeos/network/cellular_esim_uninstall_handler.h
+++ b/chromeos/network/cellular_esim_uninstall_handler.h
@@ -12,6 +12,7 @@
 #include "base/containers/flat_set.h"
 #include "base/containers/queue.h"
 #include "base/gtest_prod_util.h"
+#include "base/scoped_observation.h"
 #include "base/time/time.h"
 #include "chromeos/dbus/hermes/hermes_response_status.h"
 #include "chromeos/network/cellular_esim_profile_handler.h"
@@ -200,6 +201,9 @@
   NetworkConfigurationHandler* network_configuration_handler_ = nullptr;
   NetworkConnectionHandler* network_connection_handler_ = nullptr;
   NetworkStateHandler* network_state_handler_ = nullptr;
+  base::ScopedObservation<chromeos::NetworkStateHandler,
+                          chromeos::NetworkStateHandlerObserver>
+      network_state_handler_observer_{this};
 
   base::WeakPtrFactory<CellularESimUninstallHandler> weak_ptr_factory_{this};
 };
diff --git a/chromeos/network/cellular_inhibitor.cc b/chromeos/network/cellular_inhibitor.cc
index 6ef118d..b6f1375 100644
--- a/chromeos/network/cellular_inhibitor.cc
+++ b/chromeos/network/cellular_inhibitor.cc
@@ -60,17 +60,14 @@
 
 CellularInhibitor::CellularInhibitor() = default;
 
-CellularInhibitor::~CellularInhibitor() {
-  if (network_state_handler_)
-    network_state_handler_->RemoveObserver(this, FROM_HERE);
-}
+CellularInhibitor::~CellularInhibitor() = default;
 
 void CellularInhibitor::Init(NetworkStateHandler* network_state_handler,
                              NetworkDeviceHandler* network_device_handler) {
   network_state_handler_ = network_state_handler;
   network_device_handler_ = network_device_handler;
 
-  network_state_handler_->AddObserver(this, FROM_HERE);
+  network_state_handler_observer_.Observe(network_state_handler_);
 }
 
 void CellularInhibitor::InhibitCellularScanning(InhibitReason reason,
diff --git a/chromeos/network/cellular_inhibitor.h b/chromeos/network/cellular_inhibitor.h
index 5bf28bce..f4eaaac2 100644
--- a/chromeos/network/cellular_inhibitor.h
+++ b/chromeos/network/cellular_inhibitor.h
@@ -11,9 +11,11 @@
 #include "base/memory/weak_ptr.h"
 #include "base/observer_list.h"
 #include "base/observer_list_types.h"
+#include "base/scoped_observation.h"
 #include "base/time/time.h"
 #include "base/timer/timer.h"
 #include "chromeos/network/network_handler_callbacks.h"
+#include "chromeos/network/network_state_handler.h"
 #include "chromeos/network/network_state_handler_observer.h"
 
 namespace chromeos {
@@ -177,6 +179,10 @@
       absl::optional<InhibitOperationResult> result);
 
   NetworkStateHandler* network_state_handler_ = nullptr;
+  base::ScopedObservation<chromeos::NetworkStateHandler,
+                          chromeos::NetworkStateHandlerObserver>
+      network_state_handler_observer_{this};
+
   NetworkDeviceHandler* network_device_handler_ = nullptr;
 
   State state_ = State::kIdle;
diff --git a/chromeos/network/cellular_metrics_logger.cc b/chromeos/network/cellular_metrics_logger.cc
index 8239ca37..8708bb56 100644
--- a/chromeos/network/cellular_metrics_logger.cc
+++ b/chromeos/network/cellular_metrics_logger.cc
@@ -136,7 +136,6 @@
   }
 }
 
-
 // static
 void CellularMetricsLogger::LogCellularUserInitiatedConnectionSuccessHistogram(
     CellularMetricsLogger::ConnectResult start_connect_result,
@@ -365,7 +364,7 @@
     CellularESimProfileHandler* cellular_esim_profile_handler) {
   network_state_handler_ = network_state_handler;
   cellular_esim_profile_handler_ = cellular_esim_profile_handler;
-  network_state_handler_->AddObserver(this, FROM_HERE);
+  network_state_handler_observer_.Observe(network_state_handler_);
 
   if (network_connection_handler) {
     network_connection_handler_ = network_connection_handler;
@@ -906,7 +905,7 @@
 }
 
 void CellularMetricsLogger::OnShuttingDown() {
-  network_state_handler_->RemoveObserver(this, FROM_HERE);
+  network_state_handler_observer_.Reset();
   network_state_handler_ = nullptr;
   esim_feature_usage_metrics_.reset();
 }
diff --git a/chromeos/network/cellular_metrics_logger.h b/chromeos/network/cellular_metrics_logger.h
index b96c33a..94079df 100644
--- a/chromeos/network/cellular_metrics_logger.h
+++ b/chromeos/network/cellular_metrics_logger.h
@@ -8,11 +8,13 @@
 #include "base/component_export.h"
 #include "base/containers/flat_map.h"
 #include "base/gtest_prod_util.h"
+#include "base/scoped_observation.h"
 #include "base/time/time.h"
 #include "base/timer/elapsed_timer.h"
 #include "base/timer/timer.h"
 #include "chromeos/login/login_state/login_state.h"
 #include "chromeos/network/network_connection_observer.h"
+#include "chromeos/network/network_state_handler.h"
 #include "chromeos/network/network_state_handler_observer.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 
@@ -23,7 +25,6 @@
 class ESimFeatureUsageMetrics;
 class NetworkConnectionHandler;
 class NetworkState;
-class NetworkStateHandler;
 
 // Cellular network SIM types.
 enum class SimType {
@@ -363,6 +364,9 @@
   bool is_cellular_available_ = false;
 
   NetworkStateHandler* network_state_handler_ = nullptr;
+  base::ScopedObservation<chromeos::NetworkStateHandler,
+                          chromeos::NetworkStateHandlerObserver>
+      network_state_handler_observer_{this};
 
   NetworkConnectionHandler* network_connection_handler_ = nullptr;
 
diff --git a/chromeos/network/cellular_policy_handler.cc b/chromeos/network/cellular_policy_handler.cc
index 67040fb..fe63965 100644
--- a/chromeos/network/cellular_policy_handler.cc
+++ b/chromeos/network/cellular_policy_handler.cc
@@ -76,7 +76,7 @@
   hermes_observation_.Observe(HermesManagerClient::Get());
   cellular_esim_profile_handler_observation_.Observe(
       cellular_esim_profile_handler);
-  network_state_handler_->AddObserver(this, FROM_HERE);
+  network_state_handler_observer_.Observe(network_state_handler_);
 }
 
 void CellularPolicyHandler::InstallESim(const std::string& smdp_address,
@@ -101,9 +101,7 @@
   if (!network_state_handler_) {
     return;
   }
-  if (network_state_handler_->HasObserver(this)) {
-    network_state_handler_->RemoveObserver(this, FROM_HERE);
-  }
+  network_state_handler_observer_.Reset();
   network_state_handler_ = nullptr;
 }
 
diff --git a/chromeos/network/cellular_policy_handler.h b/chromeos/network/cellular_policy_handler.h
index 96f2d287..a18211a 100644
--- a/chromeos/network/cellular_policy_handler.h
+++ b/chromeos/network/cellular_policy_handler.h
@@ -13,6 +13,7 @@
 #include "base/timer/timer.h"
 #include "base/values.h"
 #include "chromeos/network/cellular_esim_profile_handler.h"
+#include "chromeos/network/network_state_handler.h"
 #include "chromeos/network/network_state_handler_observer.h"
 #include "net/base/backoff_entry.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
@@ -120,6 +121,9 @@
   CellularESimInstaller* cellular_esim_installer_ = nullptr;
   NetworkProfileHandler* network_profile_handler_ = nullptr;
   NetworkStateHandler* network_state_handler_ = nullptr;
+  base::ScopedObservation<chromeos::NetworkStateHandler,
+                          chromeos::NetworkStateHandlerObserver>
+      network_state_handler_observer_{this};
   ManagedCellularPrefHandler* managed_cellular_pref_handler_ = nullptr;
   ManagedNetworkConfigurationHandler* managed_network_configuration_handler_ =
       nullptr;
diff --git a/chromeos/network/client_cert_resolver.cc b/chromeos/network/client_cert_resolver.cc
index 1e2445f4..50577a8 100644
--- a/chromeos/network/client_cert_resolver.cc
+++ b/chromeos/network/client_cert_resolver.cc
@@ -460,11 +460,8 @@
               const std::string& dbus_error_name,
               const std::string& dbus_error_message) {
   network_handler::ShillErrorCallbackFunction(
-      "ClientCertResolver.SetProperties failed",
-      service_path,
-      network_handler::ErrorCallback(),
-      dbus_error_name,
-      dbus_error_message);
+      "ClientCertResolver.SetProperties failed", service_path,
+      network_handler::ErrorCallback(), dbus_error_name, dbus_error_message);
 }
 
 bool ClientCertificatesLoaded() {
@@ -488,8 +485,6 @@
 
 ClientCertResolver::~ClientCertResolver() {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  if (network_state_handler_)
-    network_state_handler_->RemoveObserver(this, FROM_HERE);
   if (NetworkCertLoader::IsInitialized())
     NetworkCertLoader::Get()->RemoveObserver(this);
   if (managed_network_config_handler_)
@@ -502,7 +497,7 @@
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   DCHECK(network_state_handler);
   network_state_handler_ = network_state_handler;
-  network_state_handler_->AddObserver(this, FROM_HERE);
+  network_state_handler_observer_.Observe(network_state_handler_);
 
   DCHECK(managed_network_config_handler);
   managed_network_config_handler_ = managed_network_config_handler;
@@ -603,11 +598,8 @@
 
   NetworkStateHandler::NetworkStateList networks;
   network_state_handler_->GetNetworkListByType(
-      NetworkTypePattern::Default(),
-      true /* configured_only */,
-      false /* visible_only */,
-      0 /* no limit */,
-      &networks);
+      NetworkTypePattern::Default(), true /* configured_only */,
+      false /* visible_only */, 0 /* no limit */, &networks);
 
   NetworkStateHandler::NetworkStateList networks_to_check;
   for (const NetworkState* network : networks) {
@@ -648,11 +640,8 @@
   // Compare all networks with all certificates.
   NetworkStateHandler::NetworkStateList networks;
   network_state_handler_->GetNetworkListByType(
-      NetworkTypePattern::Default(),
-      true /* configured_only */,
-      false /* visible_only */,
-      0 /* no limit */,
-      &networks);
+      NetworkTypePattern::Default(), true /* configured_only */,
+      false /* visible_only */, 0 /* no limit */, &networks);
   ResolveNetworks(networks);
 }
 
@@ -766,11 +755,9 @@
 void ClientCertResolver::ResolvePendingNetworks() {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   NetworkStateHandler::NetworkStateList networks;
-  network_state_handler_->GetNetworkListByType(NetworkTypePattern::Default(),
-                                               true /* configured_only */,
-                                               false /* visible_only */,
-                                               0 /* no limit */,
-                                               &networks);
+  network_state_handler_->GetNetworkListByType(
+      NetworkTypePattern::Default(), true /* configured_only */,
+      false /* visible_only */, 0 /* no limit */, &networks);
 
   NetworkStateHandler::NetworkStateList networks_to_resolve;
   for (const NetworkState* network : networks) {
diff --git a/chromeos/network/client_cert_resolver.h b/chromeos/network/client_cert_resolver.h
index c733c4e..c8606d59 100644
--- a/chromeos/network/client_cert_resolver.h
+++ b/chromeos/network/client_cert_resolver.h
@@ -14,6 +14,7 @@
 #include "base/containers/flat_map.h"
 #include "base/memory/weak_ptr.h"
 #include "base/observer_list.h"
+#include "base/scoped_observation.h"
 #include "base/sequence_checker.h"
 #include "base/time/time.h"
 #include "chromeos/network/client_cert_util.h"
@@ -25,7 +26,7 @@
 namespace base {
 class Clock;
 class Value;
-}
+}  // namespace base
 
 namespace chromeos {
 
@@ -162,6 +163,9 @@
 
   // Unowned associated (global or test) instance.
   NetworkStateHandler* network_state_handler_;
+  base::ScopedObservation<chromeos::NetworkStateHandler,
+                          chromeos::NetworkStateHandlerObserver>
+      network_state_handler_observer_{this};
 
   // Unowned associated (global or test) instance.
   ManagedNetworkConfigurationHandler* managed_network_config_handler_;
diff --git a/chromeos/network/network_cert_migrator.cc b/chromeos/network/network_cert_migrator.cc
index e18f82d..31d6a6e 100644
--- a/chromeos/network/network_cert_migrator.cc
+++ b/chromeos/network/network_cert_migrator.cc
@@ -57,8 +57,7 @@
       // Defer migration of user networks to when the user certificate database
       // has finished loading.
       if (!CorrespondingCertificateDatabaseLoaded(network)) {
-        VLOG(2) << "Skipping network cert migration of network "
-                << service_path
+        VLOG(2) << "Skipping network cert migration of network " << service_path
                 << " until the corresponding certificate database is loaded.";
         continue;
       }
@@ -112,7 +111,8 @@
         FindCertificateWithPkcs11Id(pkcs11_id, &real_slot_id);
     if (!cert) {
       LOG(WARNING) << "No matching cert found, removing the certificate "
-                      "configuration from network " << service_path;
+                      "configuration from network "
+                   << service_path;
       chromeos::client_cert::SetEmptyShillProperties(config_type,
                                                      result.GetDict());
       return result;
@@ -158,11 +158,8 @@
                        const std::string& error_name,
                        const std::string& error_message) {
     network_handler::ShillErrorCallbackFunction(
-        "MigrationTask.SetProperties failed",
-        service_path,
-        network_handler::ErrorCallback(),
-        error_name,
-        error_message);
+        "MigrationTask.SetProperties failed", service_path,
+        network_handler::ErrorCallback(), error_name, error_message);
   }
 
  private:
@@ -182,7 +179,6 @@
 NetworkCertMigrator::NetworkCertMigrator() : network_state_handler_(nullptr) {}
 
 NetworkCertMigrator::~NetworkCertMigrator() {
-  network_state_handler_->RemoveObserver(this, FROM_HERE);
   if (NetworkCertLoader::IsInitialized())
     NetworkCertLoader::Get()->RemoveObserver(this);
 }
@@ -190,7 +186,7 @@
 void NetworkCertMigrator::Init(NetworkStateHandler* network_state_handler) {
   DCHECK(network_state_handler);
   network_state_handler_ = network_state_handler;
-  network_state_handler_->AddObserver(this, FROM_HERE);
+  network_state_handler_observer_.Observe(network_state_handler_);
 
   DCHECK(NetworkCertLoader::IsInitialized());
   NetworkCertLoader::Get()->AddObserver(this);
diff --git a/chromeos/network/network_cert_migrator.h b/chromeos/network/network_cert_migrator.h
index 3f9f90f..1b4da782 100644
--- a/chromeos/network/network_cert_migrator.h
+++ b/chromeos/network/network_cert_migrator.h
@@ -7,7 +7,9 @@
 
 #include "base/component_export.h"
 #include "base/memory/weak_ptr.h"
+#include "base/scoped_observation.h"
 #include "chromeos/network/network_cert_loader.h"
+#include "chromeos/network/network_state_handler.h"
 #include "chromeos/network/network_state_handler_observer.h"
 
 namespace chromeos {
@@ -41,6 +43,9 @@
 
   // Unowned associated NetworkStateHandler* (global or test instance).
   NetworkStateHandler* network_state_handler_;
+  base::ScopedObservation<chromeos::NetworkStateHandler,
+                          chromeos::NetworkStateHandlerObserver>
+      network_state_handler_observer_{this};
 
   base::WeakPtrFactory<NetworkCertMigrator> weak_ptr_factory_{this};
 };
diff --git a/chromeos/network/network_configuration_handler.cc b/chromeos/network/network_configuration_handler.cc
index 783b4cc1..a6279fa 100644
--- a/chromeos/network/network_configuration_handler.cc
+++ b/chromeos/network/network_configuration_handler.cc
@@ -490,7 +490,7 @@
 }
 
 void NetworkConfigurationHandler::OnShuttingDown() {
-  network_state_handler_->RemoveObserver(this, FROM_HERE);
+  network_state_handler_observer_.Reset();
   network_state_handler_ = nullptr;
   for (auto& observer : observers_)
     observer.OnShuttingDown();
@@ -514,7 +514,7 @@
   network_device_handler_ = network_device_handler;
 
   // Observer is removed in OnShuttingDown() observer override.
-  network_state_handler_->AddObserver(this, FROM_HERE);
+  network_state_handler_observer_.Observe(network_state_handler_);
 }
 
 void NetworkConfigurationHandler::ConfigurationFailed(
diff --git a/chromeos/network/network_configuration_handler.h b/chromeos/network/network_configuration_handler.h
index aff1988..d079253 100644
--- a/chromeos/network/network_configuration_handler.h
+++ b/chromeos/network/network_configuration_handler.h
@@ -15,11 +15,13 @@
 #include "base/component_export.h"
 #include "base/memory/weak_ptr.h"
 #include "base/observer_list.h"
+#include "base/scoped_observation.h"
 #include "base/values.h"
 #include "chromeos/dbus/common/dbus_method_call_status.h"
 #include "chromeos/network/network_configuration_observer.h"
 #include "chromeos/network/network_handler.h"
 #include "chromeos/network/network_handler_callbacks.h"
+#include "chromeos/network/network_state_handler.h"
 #include "chromeos/network/network_state_handler_observer.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 
@@ -226,6 +228,9 @@
 
   // Unowned associated Network*Handlers (global or test instance).
   NetworkStateHandler* network_state_handler_;
+  base::ScopedObservation<chromeos::NetworkStateHandler,
+                          chromeos::NetworkStateHandlerObserver>
+      network_state_handler_observer_{this};
   NetworkDeviceHandler* network_device_handler_;
 
   // Map of in-progress deleter instances.
diff --git a/chromeos/network/network_configuration_handler_unittest.cc b/chromeos/network/network_configuration_handler_unittest.cc
index 4512626..de2b644 100644
--- a/chromeos/network/network_configuration_handler_unittest.cc
+++ b/chromeos/network/network_configuration_handler_unittest.cc
@@ -187,14 +187,13 @@
     base::RunLoop().RunUntilIdle();
     network_state_handler_observer_ =
         std::make_unique<TestNetworkStateHandlerObserver>();
-    network_state_handler_->AddObserver(network_state_handler_observer_.get(),
-                                        FROM_HERE);
+    network_state_handler_->AddObserver(network_state_handler_observer_.get());
   }
 
   ~NetworkConfigurationHandlerTest() override {
     network_state_handler_->Shutdown();
     network_state_handler_->RemoveObserver(
-        network_state_handler_observer_.get(), FROM_HERE);
+        network_state_handler_observer_.get());
     network_state_handler_observer_.reset();
     network_configuration_handler_.reset();
     network_state_handler_.reset();
diff --git a/chromeos/network/network_connection_handler_impl.cc b/chromeos/network/network_connection_handler_impl.cc
index 54986611..ae4a5bb 100644
--- a/chromeos/network/network_connection_handler_impl.cc
+++ b/chromeos/network/network_connection_handler_impl.cc
@@ -226,8 +226,6 @@
       certificates_loaded_(false) {}
 
 NetworkConnectionHandlerImpl::~NetworkConnectionHandlerImpl() {
-  if (network_state_handler_)
-    network_state_handler_->RemoveObserver(this, FROM_HERE);
   if (network_cert_loader_)
     network_cert_loader_->RemoveObserver(this);
 }
@@ -252,7 +250,7 @@
 
   if (network_state_handler) {
     network_state_handler_ = network_state_handler;
-    network_state_handler_->AddObserver(this, FROM_HERE);
+    network_state_handler_observer_.Observe(network_state_handler_);
   }
   configuration_handler_ = network_configuration_handler;
   managed_configuration_handler_ = managed_network_configuration_handler;
diff --git a/chromeos/network/network_connection_handler_impl.h b/chromeos/network/network_connection_handler_impl.h
index abefc0a..bda32f3 100644
--- a/chromeos/network/network_connection_handler_impl.h
+++ b/chromeos/network/network_connection_handler_impl.h
@@ -6,10 +6,12 @@
 #define CHROMEOS_NETWORK_NETWORK_CONNECTION_HANDLER_IMPL_H_
 
 #include "base/component_export.h"
+#include "base/scoped_observation.h"
 #include "base/timer/timer.h"
 #include "chromeos/dbus/common/dbus_method_call_status.h"
 #include "chromeos/network/network_cert_loader.h"
 #include "chromeos/network/network_connection_handler.h"
+#include "chromeos/network/network_state_handler.h"
 #include "chromeos/network/network_state_handler_observer.h"
 
 namespace chromeos {
@@ -177,6 +179,9 @@
   // Local references to the associated handler instances.
   NetworkCertLoader* network_cert_loader_ = nullptr;
   NetworkStateHandler* network_state_handler_ = nullptr;
+  base::ScopedObservation<chromeos::NetworkStateHandler,
+                          chromeos::NetworkStateHandlerObserver>
+      network_state_handler_observer_{this};
   NetworkConfigurationHandler* configuration_handler_ = nullptr;
   ManagedNetworkConfigurationHandler* managed_configuration_handler_ = nullptr;
   CellularConnectionHandler* cellular_connection_handler_ = nullptr;
diff --git a/chromeos/network/network_device_handler_impl.cc b/chromeos/network/network_device_handler_impl.cc
index 27898ec..0ab3a64 100644
--- a/chromeos/network/network_device_handler_impl.cc
+++ b/chromeos/network/network_device_handler_impl.cc
@@ -115,10 +115,7 @@
 
 NetworkDeviceHandlerImpl::NetworkDeviceHandlerImpl() = default;
 
-NetworkDeviceHandlerImpl::~NetworkDeviceHandlerImpl() {
-  if (network_state_handler_)
-    network_state_handler_->RemoveObserver(this, FROM_HERE);
-}
+NetworkDeviceHandlerImpl::~NetworkDeviceHandlerImpl() = default;
 
 void NetworkDeviceHandlerImpl::GetDeviceProperties(
     const std::string& device_path,
@@ -288,7 +285,7 @@
     NetworkStateHandler* network_state_handler) {
   DCHECK(network_state_handler);
   network_state_handler_ = network_state_handler;
-  network_state_handler_->AddObserver(this, FROM_HERE);
+  network_state_handler_observer_.Observe(network_state_handler_);
 }
 
 void NetworkDeviceHandlerImpl::ApplyCellularAllowRoamingToShill() {
diff --git a/chromeos/network/network_device_handler_impl.h b/chromeos/network/network_device_handler_impl.h
index f9b7e28..942221a 100644
--- a/chromeos/network/network_device_handler_impl.h
+++ b/chromeos/network/network_device_handler_impl.h
@@ -13,9 +13,11 @@
 #include "base/compiler_specific.h"
 #include "base/component_export.h"
 #include "base/memory/weak_ptr.h"
+#include "base/scoped_observation.h"
 #include "chromeos/network/network_device_handler.h"
 #include "chromeos/network/network_handler.h"
 #include "chromeos/network/network_handler_callbacks.h"
+#include "chromeos/network/network_state_handler.h"
 #include "chromeos/network/network_state_handler_observer.h"
 
 namespace chromeos {
@@ -172,6 +174,9 @@
   const DeviceState* GetWifiDeviceState();
 
   NetworkStateHandler* network_state_handler_ = nullptr;
+  base::ScopedObservation<chromeos::NetworkStateHandler,
+                          chromeos::NetworkStateHandlerObserver>
+      network_state_handler_observer_{this};
   bool allow_cellular_sim_lock_ = true;
   bool cellular_policy_allow_roaming_ = true;
   WifiFeatureSupport mac_addr_randomization_supported_ =
diff --git a/chromeos/network/network_metadata_store.cc b/chromeos/network/network_metadata_store.cc
index ac58e070..faaefcc 100644
--- a/chromeos/network/network_metadata_store.cc
+++ b/chromeos/network/network_metadata_store.cc
@@ -89,7 +89,7 @@
     network_configuration_handler_->AddObserver(this);
   }
   if (network_state_handler_) {
-    network_state_handler_->AddObserver(this, FROM_HERE);
+    network_state_handler_observer_.Observe(network_state_handler_);
   }
   if (LoginState::IsInitialized()) {
     LoginState::Get()->AddObserver(this);
@@ -103,9 +103,6 @@
   if (network_configuration_handler_) {
     network_configuration_handler_->RemoveObserver(this);
   }
-  if (network_state_handler_ && network_state_handler_->HasObserver(this)) {
-    network_state_handler_->RemoveObserver(this, FROM_HERE);
-  }
   if (LoginState::IsInitialized()) {
     LoginState::Get()->RemoveObserver(this);
   }
diff --git a/chromeos/network/network_metadata_store.h b/chromeos/network/network_metadata_store.h
index bf445bf6..1c361d0 100644
--- a/chromeos/network/network_metadata_store.h
+++ b/chromeos/network/network_metadata_store.h
@@ -9,11 +9,13 @@
 
 #include "base/component_export.h"
 #include "base/observer_list.h"
+#include "base/scoped_observation.h"
 #include "base/values.h"
 #include "chromeos/login/login_state/login_state.h"
 #include "chromeos/network/network_configuration_observer.h"
 #include "chromeos/network/network_connection_observer.h"
 #include "chromeos/network/network_metadata_observer.h"
+#include "chromeos/network/network_state_handler.h"
 #include "chromeos/network/network_state_handler_observer.h"
 
 class PrefService;
@@ -159,6 +161,9 @@
   NetworkConfigurationHandler* network_configuration_handler_;
   NetworkConnectionHandler* network_connection_handler_;
   NetworkStateHandler* network_state_handler_;
+  base::ScopedObservation<chromeos::NetworkStateHandler,
+                          chromeos::NetworkStateHandlerObserver>
+      network_state_handler_observer_{this};
   PrefService* profile_pref_service_;
   PrefService* device_pref_service_;
   bool is_enterprise_managed_;
diff --git a/chromeos/network/network_state_handler_unittest.cc b/chromeos/network/network_state_handler_unittest.cc
index b97f24c..42f868a 100644
--- a/chromeos/network/network_state_handler_unittest.cc
+++ b/chromeos/network/network_state_handler_unittest.cc
@@ -351,7 +351,7 @@
     network_state_handler_.reset(new NetworkStateHandler);
     test_observer_ =
         std::make_unique<TestObserver>(network_state_handler_.get());
-    network_state_handler_->AddObserver(test_observer_.get(), FROM_HERE);
+    network_state_handler_->AddObserver(test_observer_.get());
     network_state_handler_->InitShillPropertyHandler();
     network_state_handler_->set_stub_cellular_networks_provider(
         &fake_stub_cellular_networks_provider_);
@@ -360,7 +360,7 @@
   }
 
   void TearDown() override {
-    network_state_handler_->RemoveObserver(test_observer_.get(), FROM_HERE);
+    network_state_handler_->RemoveObserver(test_observer_.get());
     network_state_handler_->Shutdown();
     test_observer_.reset();
     network_state_handler_.reset();
diff --git a/components/app_restore/BUILD.gn b/components/app_restore/BUILD.gn
index 8d73031..117457c 100644
--- a/components/app_restore/BUILD.gn
+++ b/components/app_restore/BUILD.gn
@@ -37,6 +37,8 @@
     "lacros_save_handler.h",
     "restore_data.cc",
     "restore_data.h",
+    "tab_group_info.cc",
+    "tab_group_info.h",
     "window_info.cc",
     "window_info.h",
     "window_properties.cc",
@@ -55,6 +57,7 @@
     "//components/services/app_service/public/cpp:intents",
     "//components/services/app_service/public/mojom",
     "//components/sessions:session_id",
+    "//components/tab_groups",
     "//ui/aura",
     "//ui/views",
   ]
diff --git a/components/app_restore/DEPS b/components/app_restore/DEPS
index 48bc9b6..510d8d2 100644
--- a/components/app_restore/DEPS
+++ b/components/app_restore/DEPS
@@ -5,6 +5,7 @@
   "+components/account_id/account_id.h",
   "+components/services/app_service/public",
   "+components/sessions/core/session_id.h",
+  "+components/tab_groups",
   "+content/public/test",
   "+ui",
 ]
diff --git a/components/app_restore/app_launch_info.h b/components/app_restore/app_launch_info.h
index 7398aca..2848496 100644
--- a/components/app_restore/app_launch_info.h
+++ b/components/app_restore/app_launch_info.h
@@ -9,6 +9,7 @@
 
 #include "base/component_export.h"
 #include "base/files/file_path.h"
+#include "components/app_restore/tab_group_info.h"
 #include "components/services/app_service/public/mojom/types.mojom.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 #include "ui/base/window_open_disposition.h"
@@ -56,6 +57,7 @@
 
   ~AppLaunchInfo();
 
+  // TODO(1326250): Remove optional wrappers around vector fields.
   std::string app_id;
   absl::optional<int32_t> window_id;
   absl::optional<int32_t> event_flag;
@@ -70,6 +72,11 @@
   absl::optional<apps::mojom::IntentPtr> intent;
   absl::optional<bool> app_type_browser;
   absl::optional<std::string> app_name;
+  // For Browsers only, represents tab groups associated with this browser
+  // instance if there are any. This is only used in Desks Storage, tab groups
+  // in full restore are persistsed by sessions. This field is not converted to
+  // base::Value in base value conversions.
+  absl::optional<std::vector<TabGroupInfo>> tab_group_infos;
 };
 
 }  // namespace app_restore
diff --git a/components/app_restore/app_restore_data.cc b/components/app_restore/app_restore_data.cc
index c5762da..acaea5a 100644
--- a/components/app_restore/app_restore_data.cc
+++ b/components/app_restore/app_restore_data.cc
@@ -273,6 +273,7 @@
   intent = std::move(app_launch_info->intent);
   app_type_browser = std::move(app_launch_info->app_type_browser);
   app_name = std::move(app_launch_info->app_name);
+  tab_group_infos = std::move(app_launch_info->tab_group_infos);
 }
 
 AppRestoreData::~AppRestoreData() = default;
@@ -349,6 +350,9 @@
   if (status_bar_color.has_value())
     data->status_bar_color = status_bar_color.value();
 
+  if (tab_group_infos.has_value())
+    data->tab_group_infos = tab_group_infos.value();
+
   return data;
 }
 
@@ -529,6 +533,7 @@
     app_launch_info->intent = intent->Clone();
   app_launch_info->app_type_browser = app_type_browser;
   app_launch_info->app_name = app_name;
+  app_launch_info->tab_group_infos = tab_group_infos;
   return app_launch_info;
 }
 
diff --git a/components/app_restore/app_restore_data.h b/components/app_restore/app_restore_data.h
index 8be25bc..8fe0514 100644
--- a/components/app_restore/app_restore_data.h
+++ b/components/app_restore/app_restore_data.h
@@ -10,6 +10,7 @@
 
 #include "base/component_export.h"
 #include "chromeos/ui/base/window_state_type.h"
+#include "components/app_restore/tab_group_info.h"
 #include "components/services/app_service/public/mojom/types.mojom.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 #include "ui/base/ui_base_types.h"
@@ -81,6 +82,7 @@
   apps::mojom::WindowInfoPtr GetAppWindowInfo() const;
 
   // App launch parameters.
+  // TODO(crbug.com/1326250): Remove optional wrappers around vector fields.
   absl::optional<int32_t> event_flag;
   absl::optional<int32_t> container;
   absl::optional<int32_t> disposition;
@@ -105,6 +107,11 @@
   // `snap_percentage` of 60 when the display is in portrait means the height is
   // 60 percent of the work area height.
   absl::optional<uint32_t> snap_percentage;
+  // For Browsers only, represents tab groups associtated with this browser
+  // instance if there are any. This is only used in Desks Storage, tab groups
+  // in full restore are persistsed by sessions.  This field is not converted to
+  // base::value in base value conversions.
+  absl::optional<std::vector<TabGroupInfo>> tab_group_infos;
 
   // Extra ARC window's information.
   absl::optional<gfx::Size> minimum_size;
diff --git a/components/app_restore/restore_data_unittest.cc b/components/app_restore/restore_data_unittest.cc
index 87ea0d1..77fa3f0 100644
--- a/components/app_restore/restore_data_unittest.cc
+++ b/components/app_restore/restore_data_unittest.cc
@@ -13,8 +13,12 @@
 #include "components/app_constants/constants.h"
 #include "components/app_restore/app_launch_info.h"
 #include "components/app_restore/app_restore_data.h"
+#include "components/app_restore/tab_group_info.h"
 #include "components/app_restore/window_info.h"
 #include "components/services/app_service/public/mojom/types.mojom.h"
+#include "components/tab_groups/tab_group_color.h"
+#include "components/tab_groups/tab_group_visual_data.h"
+#include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "ui/aura/client/aura_constants.h"
 #include "ui/base/window_open_disposition.h"
@@ -95,6 +99,32 @@
 constexpr gfx::Rect kBoundsInRoot1(11, 21, 111, 121);
 constexpr gfx::Rect kBoundsInRoot2(31, 41, 131, 141);
 
+constexpr char16_t kTestTabGroupTitleOne[] = u"sample_tab_group_1";
+constexpr char16_t kTestTabGroupTitleTwo[] = u"sample_tab_group_2";
+constexpr char16_t kTestTabGroupTitleThree[] = u"sample_tab_group_3";
+const tab_groups::TabGroupColorId kTestTabGroupColorOne =
+    tab_groups::TabGroupColorId::kGrey;
+const tab_groups::TabGroupColorId kTestTabGroupColorTwo =
+    tab_groups::TabGroupColorId::kBlue;
+const tab_groups::TabGroupColorId kTestTabGroupColorThree =
+    tab_groups::TabGroupColorId::kGreen;
+const gfx::Range kTestTabGroupTabRange(1, 2);
+
+TabGroupInfo MakeTestTabGroup(const char16_t* title,
+                              tab_groups::TabGroupColorId color) {
+  return TabGroupInfo(kTestTabGroupTabRange,
+                      tab_groups::TabGroupVisualData(title, color));
+}
+
+void PopulateTestTabgroups(std::vector<TabGroupInfo>& out_tab_groups) {
+  out_tab_groups.push_back(
+      MakeTestTabGroup(kTestTabGroupTitleOne, kTestTabGroupColorOne));
+  out_tab_groups.push_back(
+      MakeTestTabGroup(kTestTabGroupTitleTwo, kTestTabGroupColorTwo));
+  out_tab_groups.push_back(
+      MakeTestTabGroup(kTestTabGroupTitleThree, kTestTabGroupColorThree));
+}
+
 }  // namespace
 
 // Unit tests for restore data.
@@ -133,6 +163,8 @@
             std::vector<base::FilePath>{base::FilePath(kFilePath2)},
             CreateIntent(kIntentActionView, kMimeType, kShareText2));
     app_launch_info2->app_type_browser = kAppTypeBrower2;
+    app_launch_info2->tab_group_infos.emplace();
+    PopulateTestTabgroups(app_launch_info2->tab_group_infos.value());
 
     std::unique_ptr<AppLaunchInfo> app_launch_info3 =
         std::make_unique<AppLaunchInfo>(
@@ -210,7 +242,9 @@
                             absl::optional<std::u16string> title,
                             absl::optional<gfx::Rect> bounds_in_root,
                             uint32_t primary_color,
-                            uint32_t status_bar_color) {
+                            uint32_t status_bar_color,
+                            std::vector<TabGroupInfo> expected_tab_group_infos,
+                            bool test_tab_group_infos = true) {
     EXPECT_TRUE(data->container.has_value());
     EXPECT_EQ(static_cast<int>(container), data->container.value());
 
@@ -308,9 +342,28 @@
     } else {
       EXPECT_FALSE(data->status_bar_color.has_value());
     }
+
+    // Only test tab group infos in tests that don't concern serialization
+    // or deserialization as the logic for serializing tab group infos
+    // exists in the desks_storage component.  This is because tab group
+    // infos are only utilized by save and recall and desk template features.
+    if (expected_tab_group_infos.size() > 0 && test_tab_group_infos) {
+      // If we're passing a non-empty expceted vector then we expect the
+      // object under test to have tab group infos.
+      EXPECT_TRUE(data->tab_group_infos.has_value());
+
+      // Parameter vector and data vector should always have the same size
+      // as they should be instantiated from the same function.
+      EXPECT_EQ(expected_tab_group_infos.size(),
+                data->tab_group_infos.value().size());
+
+      EXPECT_THAT(expected_tab_group_infos, testing::UnorderedElementsAreArray(
+                                                data->tab_group_infos.value()));
+    }
   }
 
-  void VerifyRestoreData(const RestoreData& restore_data) {
+  void VerifyRestoreData(const RestoreData& restore_data,
+                         bool test_tab_group_infos = true) {
     EXPECT_EQ(2u, app_id_to_launch_list(restore_data).size());
 
     // Verify for |kAppId1|.
@@ -332,9 +385,11 @@
         kAppTypeBrower1, kActivationIndex1, kDeskId1, kCurrentBounds1,
         kWindowStateType1, kPreMinimizedWindowStateType1, /*snap_percentage=*/0,
         kMaxSize1, kMinSize1, std::u16string(kTitle1), kBoundsInRoot1,
-        kPrimaryColor1, kStatusBarColor1);
+        kPrimaryColor1, kStatusBarColor1, /*tab_group_infos=*/{});
 
     const auto app_restore_data_it2 = launch_list_it1->second.find(kWindowId2);
+    std::vector<TabGroupInfo> expected_tab_group_infos;
+    PopulateTestTabgroups(expected_tab_group_infos);
     EXPECT_TRUE(app_restore_data_it2 != launch_list_it1->second.end());
     VerifyAppRestoreData(
         app_restore_data_it2->second,
@@ -345,7 +400,8 @@
         kAppTypeBrower2, kActivationIndex2, kDeskId2, kCurrentBounds2,
         kWindowStateType2, kPreMinimizedWindowStateType2, /*snap_percentage=*/0,
         absl::nullopt, kMinSize2, std::u16string(kTitle2), kBoundsInRoot2,
-        kPrimaryColor2, kStatusBarColor2);
+        kPrimaryColor2, kStatusBarColor2, std::move(expected_tab_group_infos),
+        test_tab_group_infos);
 
     // Verify for |kAppId2|.
     const auto launch_list_it2 =
@@ -362,7 +418,8 @@
         CreateIntent(kIntentActionView, kMimeType, kShareText1),
         kAppTypeBrower3, kActivationIndex3, kDeskId3, kCurrentBounds3,
         kWindowStateType3, kPreMinimizedWindowStateType3, kSnapPercentage,
-        absl::nullopt, absl::nullopt, absl::nullopt, absl::nullopt, 0, 0);
+        absl::nullopt, absl::nullopt, absl::nullopt, absl::nullopt, 0, 0,
+        /*tab_group_infos=*/{});
   }
 
   RestoreData& restore_data() { return restore_data_; }
@@ -417,16 +474,16 @@
   // Verify the restore data for |kWindowId2| is migrated to |kWindowId4|.
   const auto app_restore_data_it4 = launch_list_it1->second.find(kWindowId4);
   EXPECT_TRUE(app_restore_data_it4 != launch_list_it1->second.end());
-  VerifyAppRestoreData(app_restore_data_it4->second,
-                       apps::mojom::LaunchContainer::kLaunchContainerTab,
-                       WindowOpenDisposition::NEW_FOREGROUND_TAB, kDisplayId1,
-                       std::vector<base::FilePath>{base::FilePath(kFilePath2)},
-                       CreateIntent(kIntentActionView, kMimeType, kShareText2),
-                       kAppTypeBrower2, kActivationIndex2, kDeskId2,
-                       kCurrentBounds2, kWindowStateType2,
-                       kPreMinimizedWindowStateType2, /*snap_percentage=*/0,
-                       absl::nullopt, kMinSize2, std::u16string(kTitle2),
-                       kBoundsInRoot2, kPrimaryColor2, kStatusBarColor2);
+  VerifyAppRestoreData(
+      app_restore_data_it4->second,
+      apps::mojom::LaunchContainer::kLaunchContainerTab,
+      WindowOpenDisposition::NEW_FOREGROUND_TAB, kDisplayId1,
+      std::vector<base::FilePath>{base::FilePath(kFilePath2)},
+      CreateIntent(kIntentActionView, kMimeType, kShareText2), kAppTypeBrower2,
+      kActivationIndex2, kDeskId2, kCurrentBounds2, kWindowStateType2,
+      kPreMinimizedWindowStateType2, /*snap_percentage=*/0, absl::nullopt,
+      kMinSize2, std::u16string(kTitle2), kBoundsInRoot2, kPrimaryColor2,
+      kStatusBarColor2, /*tab_group_infos=*/{});
 
   // Verify the restore data for |kAppId2| still exists.
   const auto launch_list_it2 =
@@ -544,7 +601,9 @@
       std::make_unique<base::Value>(restore_data().ConvertToValue());
   std::unique_ptr<RestoreData> restore_data =
       std::make_unique<RestoreData>(std::move(value));
-  VerifyRestoreData(*restore_data);
+  // Full restore is not responsible for serializing or deseraizling
+  // TabGroupInfos.
+  VerifyRestoreData(*restore_data, /*test_tab_group_infos=*/false);
 }
 
 TEST_F(RestoreDataTest, ConvertNullData) {
diff --git a/components/app_restore/tab_group_info.cc b/components/app_restore/tab_group_info.cc
new file mode 100644
index 0000000..594e0e8
--- /dev/null
+++ b/components/app_restore/tab_group_info.cc
@@ -0,0 +1,62 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/app_restore/tab_group_info.h"
+
+#include "base/strings/utf_string_conversions.h"
+#include "components/tab_groups/tab_group_color.h"
+
+namespace app_restore {
+
+std::string TabGroupColorToString(tab_groups::TabGroupColorId color) {
+  switch (color) {
+    case tab_groups::TabGroupColorId::kGrey:
+      return kTabGroupColorGrey;
+    case tab_groups::TabGroupColorId::kBlue:
+      return kTabGroupColorBlue;
+    case tab_groups::TabGroupColorId::kRed:
+      return kTabGroupColorRed;
+    case tab_groups::TabGroupColorId::kYellow:
+      return kTabGroupColorYellow;
+    case tab_groups::TabGroupColorId::kGreen:
+      return kTabGroupColorGreen;
+    case tab_groups::TabGroupColorId::kPink:
+      return kTabGroupColorYellow;
+    case tab_groups::TabGroupColorId::kPurple:
+      return kTabGroupColorPurple;
+    case tab_groups::TabGroupColorId::kCyan:
+      return kTabGroupColorCyan;
+    case tab_groups::TabGroupColorId::kOrange:
+      return kTabGroupColorOrange;
+  }
+}
+
+TabGroupInfo::TabGroupInfo(const gfx::Range& tab_range,
+                           const tab_groups::TabGroupVisualData& visual_data)
+    : tab_range(tab_range), visual_data(visual_data) {}
+
+TabGroupInfo::TabGroupInfo(TabGroupInfo&& other)
+    : tab_range(other.tab_range), visual_data(other.visual_data) {}
+
+TabGroupInfo::TabGroupInfo(const TabGroupInfo& other) = default;
+TabGroupInfo& TabGroupInfo::operator=(const TabGroupInfo& other) = default;
+TabGroupInfo::~TabGroupInfo() = default;
+
+bool TabGroupInfo::operator==(const TabGroupInfo& other) const {
+  return tab_range == other.tab_range && visual_data == other.visual_data;
+}
+
+std::string TabGroupInfo::ToString() const {
+  std::string result =
+      "{\n\tname: " + base::UTF16ToUTF8(visual_data.title()) + "\n";
+  result += "\tcolor: " + TabGroupColorToString(visual_data.color()) + "\n";
+  std::string is_collapsed_string =
+      visual_data.is_collapsed() ? "TRUE" : "FALSE";
+  result += "\tis_collapsed: " + is_collapsed_string + "\n";
+  result += "\trange_start: " + tab_range.ToString() + "\n}";
+
+  return result;
+}
+
+}  // namespace app_restore
\ No newline at end of file
diff --git a/components/app_restore/tab_group_info.h b/components/app_restore/tab_group_info.h
new file mode 100644
index 0000000..7181071
--- /dev/null
+++ b/components/app_restore/tab_group_info.h
@@ -0,0 +1,65 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_APP_RESTORE_TAB_GROUP_INFO_H_
+#define COMPONENTS_APP_RESTORE_TAB_GROUP_INFO_H_
+
+#include "base/component_export.h"
+#include "components/tab_groups/tab_group_visual_data.h"
+#include "ui/gfx/range/range.h"
+
+namespace app_restore {
+
+// String kConstants used by TabGroupColorToString.
+constexpr char kTabGroupColorUnknown[] = "UNKNONW";
+constexpr char kTabGroupColorGrey[] = "GREY";
+constexpr char kTabGroupColorBlue[] = "BLUE";
+constexpr char kTabGroupColorRed[] = "RED";
+constexpr char kTabGroupColorYellow[] = "YELLOW";
+constexpr char kTabGroupColorGreen[] = "GREEN";
+constexpr char kTabGroupColorPink[] = "PINK";
+constexpr char kTabGroupColorPurple[] = "PURPLE";
+constexpr char kTabGroupColorCyan[] = "CYAN";
+constexpr char kTabGroupColorOrange[] = "ORANGE";
+
+// Used in ToString as well as in Conversion Logic for
+// components/desks_storage/core/desk_template_conversion.cc
+std::string COMPONENT_EXPORT(APP_RESTORE)
+    TabGroupColorToString(tab_groups::TabGroupColorId color);
+
+// Tab group info is a structure representing a tab group that
+// is associated with a specific browser window.  This struct lives
+// in a list of instances of its kind located under the tab_group_infos
+// field of an AppRestoreData struct.  This structure is used by saved desks
+// to store data relating to tab groups and is not directly used by full
+// restore.
+struct COMPONENT_EXPORT(APP_RESTORE) TabGroupInfo {
+  TabGroupInfo(const gfx::Range& tab_range,
+               const tab_groups::TabGroupVisualData& visual_data);
+
+  TabGroupInfo(const TabGroupInfo&);
+  TabGroupInfo& operator=(const TabGroupInfo& other);
+
+  // Move constructor used for vector allocation.
+  TabGroupInfo(TabGroupInfo&& other);
+
+  ~TabGroupInfo();
+
+  // Checks whether or not two TabGroupInfos are semantically equivalent.
+  // Used in testing.
+  bool operator==(const TabGroupInfo& other) const;
+
+  // Produces a string representation of this tab group used in debugging.
+  std::string ToString() const;
+
+  // Range of tabs this group is associated with.
+  gfx::Range tab_range;
+
+  // Human readable data associated with this tab group.
+  tab_groups::TabGroupVisualData visual_data;
+};
+
+}  // namespace app_restore
+
+#endif  // COMPONENTS_APP_RESTORE_TAB_GROUP_INFO_H_
\ No newline at end of file
diff --git a/components/autofill_assistant/browser/actions/external_action.cc b/components/autofill_assistant/browser/actions/external_action.cc
index ee6d6fa..89ccc1b8 100644
--- a/components/autofill_assistant/browser/actions/external_action.cc
+++ b/components/autofill_assistant/browser/actions/external_action.cc
@@ -3,6 +3,8 @@
 // found in the LICENSE file.
 
 #include "components/autofill_assistant/browser/actions/external_action.h"
+
+#include "base/logging.h"
 #include "components/autofill_assistant/browser/actions/action_delegate.h"
 #include "components/autofill_assistant/browser/client_status.h"
 
diff --git a/components/autofill_assistant/browser/autofill_assistant_impl.h b/components/autofill_assistant/browser/autofill_assistant_impl.h
index f1d1bb9..1d12585b 100644
--- a/components/autofill_assistant/browser/autofill_assistant_impl.h
+++ b/components/autofill_assistant/browser/autofill_assistant_impl.h
@@ -15,6 +15,7 @@
 
 namespace content {
 class BrowserContext;
+class WebContents;
 }  // namespace content
 
 namespace autofill_assistant {
diff --git a/components/autofill_assistant/browser/public/autofill_assistant.h b/components/autofill_assistant/browser/public/autofill_assistant.h
index 5370263..2fd5cb10 100644
--- a/components/autofill_assistant/browser/public/autofill_assistant.h
+++ b/components/autofill_assistant/browser/public/autofill_assistant.h
@@ -13,6 +13,10 @@
 #include "components/autofill_assistant/browser/public/external_action_delegate.h"
 #include "components/autofill_assistant/browser/public/external_script_controller.h"
 
+namespace content {
+class WebContents;
+}
+
 namespace autofill_assistant {
 
 // Abstract interface for exported services.
diff --git a/components/autofill_assistant/browser/public/external_script_controller.h b/components/autofill_assistant/browser/public/external_script_controller.h
index 48d6500..44dad58 100644
--- a/components/autofill_assistant/browser/public/external_script_controller.h
+++ b/components/autofill_assistant/browser/public/external_script_controller.h
@@ -5,11 +5,10 @@
 #ifndef COMPONENTS_AUTOFILL_ASSISTANT_BROWSER_PUBLIC_EXTERNAL_SCRIPT_CONTROLLER_H_
 #define COMPONENTS_AUTOFILL_ASSISTANT_BROWSER_PUBLIC_EXTERNAL_SCRIPT_CONTROLLER_H_
 
-#include "base/memory/weak_ptr.h"
-#include "components/autofill_assistant/browser/public/external_action.pb.h"
-#include "components/autofill_assistant/browser/public/runtime_observer.h"
-#include "components/autofill_assistant/browser/public/ui_state.h"
-#include "content/public/browser/web_contents.h"
+#include <string>
+
+#include "base/callback_forward.h"
+#include "base/containers/flat_map.h"
 
 namespace autofill_assistant {
 
diff --git a/components/certificate_transparency/data/log_list.json b/components/certificate_transparency/data/log_list.json
index 4c0f2a7..9372055b 100644
--- a/components/certificate_transparency/data/log_list.json
+++ b/components/certificate_transparency/data/log_list.json
@@ -1,6 +1,6 @@
 {
-  "version": "9.31",
-  "log_list_timestamp": "2022-06-02T12:54:07Z",
+  "version": "10.1",
+  "log_list_timestamp": "2022-06-03T12:54:44Z",
   "operators": [
     {
       "name": "Google",
@@ -284,8 +284,8 @@
           "url": "https://yeti2022-2.ct.digicert.com/log/",
           "mmd": 86400,
           "state": {
-            "qualified": {
-              "timestamp": "2022-03-11T00:00:00Z"
+            "usable": {
+              "timestamp": "2022-05-20T00:00:00Z"
             }
           },
           "temporal_interval": {
diff --git a/components/cronet/host_cache_persistence_manager.cc b/components/cronet/host_cache_persistence_manager.cc
index 7c1777af..a0ed6bd 100644
--- a/components/cronet/host_cache_persistence_manager.cc
+++ b/components/cronet/host_cache_persistence_manager.cc
@@ -56,8 +56,9 @@
     return;
 
   net_log_.BeginEvent(net::NetLogEventType::HOST_CACHE_PREF_READ);
-  const base::Value* pref_value = pref_service_->GetList(pref_name_);
-  bool success = cache_->RestoreFromListValue(*pref_value);
+  const base::Value::List& pref_value =
+      pref_service_->GetList(pref_name_)->GetList();
+  bool success = cache_->RestoreFromListValue(pref_value);
   net_log_.AddEntryWithBoolParams(net::NetLogEventType::HOST_CACHE_PREF_READ,
                                   net::NetLogEventPhase::END, "success",
                                   success);
@@ -79,11 +80,10 @@
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
 
   net_log_.AddEvent(net::NetLogEventType::HOST_CACHE_PREF_WRITE);
-  base::Value value(base::Value::Type::LIST);
-  cache_->GetList(&value, false,
-                  net::HostCache::SerializationType::kRestorable);
+  base::Value::List list;
+  cache_->GetList(list, false, net::HostCache::SerializationType::kRestorable);
   writing_pref_ = true;
-  pref_service_->Set(pref_name_, value);
+  pref_service_->SetList(pref_name_, std::move(list));
   writing_pref_ = false;
 }
 
diff --git a/components/cronet/host_cache_persistence_manager_unittest.cc b/components/cronet/host_cache_persistence_manager_unittest.cc
index ad929ffa..89164296 100644
--- a/components/cronet/host_cache_persistence_manager_unittest.cc
+++ b/components/cronet/host_cache_persistence_manager_unittest.cc
@@ -51,11 +51,9 @@
   // correctness.
   void CheckPref(size_t expected_size) {
     const base::Value* value = pref_service_->GetUserPref(kPrefName);
-    base::Value list(base::Value::Type::LIST);
-    if (value)
-      list = base::Value(value->GetListDeprecated());
     net::HostCache temp_cache(10);
-    temp_cache.RestoreFromListValue(base::Value::AsListValue(list));
+    if (value)
+      temp_cache.RestoreFromListValue(value->GetList());
     ASSERT_EQ(expected_size, temp_cache.size());
   }
 
@@ -80,10 +78,10 @@
     temp_cache.Set(key2, entry, base::TimeTicks::Now(), base::Seconds(1));
     temp_cache.Set(key3, entry, base::TimeTicks::Now(), base::Seconds(1));
 
-    base::Value value(base::Value::Type::LIST);
-    temp_cache.GetList(&value, false /* include_stale */,
+    base::Value::List list;
+    temp_cache.GetList(list, false /* include_stale */,
                        net::HostCache::SerializationType::kRestorable);
-    pref_service_->Set(kPrefName, value);
+    pref_service_->SetList(kPrefName, std::move(list));
   }
 
   static const char kPrefName[];
diff --git a/components/desks_storage/DEPS b/components/desks_storage/DEPS
index 937bd54..e27267f6 100644
--- a/components/desks_storage/DEPS
+++ b/components/desks_storage/DEPS
@@ -8,6 +8,7 @@
   "+components/keyed_service/core",
   "+components/services/app_service/public/cpp",
   "+components/sync",
+  "+components/tab_groups",
   "+components/version_info",
   "+third_party/re2",
   "+ui/base/ui_base_types.h",
diff --git a/components/desks_storage/core/desk_sync_bridge.cc b/components/desks_storage/core/desk_sync_bridge.cc
index a8d7454..48db03e 100644
--- a/components/desks_storage/core/desk_sync_bridge.cc
+++ b/components/desks_storage/core/desk_sync_bridge.cc
@@ -65,6 +65,9 @@
 using ProgressiveWebApp = sync_pb::WorkspaceDeskSpecifics_ProgressiveWebApp;
 using ChromeApp = sync_pb::WorkspaceDeskSpecifics_ChromeApp;
 using WorkspaceDeskSpecifics_App = sync_pb::WorkspaceDeskSpecifics_App;
+using SyncTabGroup = sync_pb::WorkspaceDeskSpecifics_BrowserAppWindow_TabGroup;
+using SyncTabGroupColor = sync_pb::WorkspaceDeskSpecifics_TabGroupColor;
+using TabGroupColor = tab_groups::TabGroupColorId;
 
 namespace {
 
@@ -136,6 +139,71 @@
   }
 }
 
+// Since tab groups must have completely valid fields therefore this function
+// exists to validate that sync tab groups are entirely valid.
+bool ValidSyncTabGroup(const SyncTabGroup& sync_tab_group) {
+  return sync_tab_group.has_first_index() && sync_tab_group.has_last_index() &&
+         sync_tab_group.has_title() && sync_tab_group.has_color();
+}
+
+// Converts a sync tab group color to its tab_groups::TabGroupColorId
+// equivalent.
+TabGroupColor TabGroupColorIdFromSyncTabColor(
+    const SyncTabGroupColor& sync_color) {
+  switch (sync_color) {
+    // Default to grey if unknown.
+    case SyncTabGroupColor::WorkspaceDeskSpecifics_TabGroupColor_UNKNOWN_COLOR:
+    case SyncTabGroupColor::WorkspaceDeskSpecifics_TabGroupColor_GREY:
+      return TabGroupColor::kGrey;
+    case SyncTabGroupColor::WorkspaceDeskSpecifics_TabGroupColor_BLUE:
+      return TabGroupColor::kBlue;
+    case SyncTabGroupColor::WorkspaceDeskSpecifics_TabGroupColor_RED:
+      return TabGroupColor::kRed;
+    case SyncTabGroupColor::WorkspaceDeskSpecifics_TabGroupColor_YELLOW:
+      return TabGroupColor::kYellow;
+    case SyncTabGroupColor::WorkspaceDeskSpecifics_TabGroupColor_GREEN:
+      return TabGroupColor::kGreen;
+    case SyncTabGroupColor::WorkspaceDeskSpecifics_TabGroupColor_PINK:
+      return TabGroupColor::kPink;
+    case SyncTabGroupColor::WorkspaceDeskSpecifics_TabGroupColor_PURPLE:
+      return TabGroupColor::kPurple;
+    case SyncTabGroupColor::WorkspaceDeskSpecifics_TabGroupColor_CYAN:
+      return TabGroupColor::kCyan;
+    case SyncTabGroupColor::WorkspaceDeskSpecifics_TabGroupColor_ORANGE:
+      return TabGroupColor::kOrange;
+  };
+}
+
+// Instantiates a TabGroup from its sync equivalent.
+app_restore::TabGroupInfo FillTabGroupInfoFromProto(
+    const SyncTabGroup& sync_tab_group) {
+  // This function should never be called with a partially instantiated
+  // tab group.
+  DCHECK(ValidSyncTabGroup(sync_tab_group));
+
+  return app_restore::TabGroupInfo(
+      {static_cast<uint32_t>(sync_tab_group.first_index()),
+       static_cast<uint32_t>(sync_tab_group.last_index())},
+      tab_groups::TabGroupVisualData(
+          base::UTF8ToUTF16(sync_tab_group.title()),
+          TabGroupColorIdFromSyncTabColor(sync_tab_group.color()),
+          sync_tab_group.is_collapsed()));
+}
+
+// Fill `out_group_infos` using information found in the proto's
+// tab group structure.
+void FillTabGroupInfosFromProto(
+    const BrowserAppWindow& browser_app_window,
+    std::vector<app_restore::TabGroupInfo>* out_group_infos) {
+  for (const auto& group : browser_app_window.tab_groups()) {
+    if (!ValidSyncTabGroup(group)) {
+      continue;
+    }
+
+    out_group_infos->push_back(FillTabGroupInfoFromProto(group));
+  }
+}
+
 // Get App ID from App proto.
 std::string GetAppId(const sync_pb::WorkspaceDeskSpecifics_App& app) {
   switch (app.app().app_case()) {
@@ -220,6 +288,12 @@
       FillUrlList(app.app().browser_app_window(),
                   &app_launch_info->urls.value());
 
+      if (app.app().browser_app_window().tab_groups_size() > 0) {
+        app_launch_info->tab_group_infos.emplace();
+        FillTabGroupInfosFromProto(app.app().browser_app_window(),
+                                   &app_launch_info->tab_group_infos.value());
+      }
+
       if (app.app().browser_app_window().has_show_as_app())
         app_launch_info->app_type_browser =
             app.app().browser_app_window().show_as_app();
@@ -322,6 +396,57 @@
   }
 }
 
+// Converts a sync tab group color to its tab_groups::TabGroupColorId
+// equivalent.
+SyncTabGroupColor SyncTabColorFromTabGroupColorId(
+    const TabGroupColor& sync_color) {
+  switch (sync_color) {
+    case TabGroupColor::kGrey:
+      return SyncTabGroupColor::WorkspaceDeskSpecifics_TabGroupColor_GREY;
+    case TabGroupColor::kBlue:
+      return SyncTabGroupColor::WorkspaceDeskSpecifics_TabGroupColor_BLUE;
+    case TabGroupColor::kRed:
+      return SyncTabGroupColor::WorkspaceDeskSpecifics_TabGroupColor_RED;
+    case TabGroupColor::kYellow:
+      return SyncTabGroupColor::WorkspaceDeskSpecifics_TabGroupColor_YELLOW;
+    case TabGroupColor::kGreen:
+      return SyncTabGroupColor::WorkspaceDeskSpecifics_TabGroupColor_GREEN;
+    case TabGroupColor::kPink:
+      return SyncTabGroupColor::WorkspaceDeskSpecifics_TabGroupColor_PINK;
+    case TabGroupColor::kPurple:
+      return SyncTabGroupColor::WorkspaceDeskSpecifics_TabGroupColor_PURPLE;
+    case TabGroupColor::kCyan:
+      return SyncTabGroupColor::WorkspaceDeskSpecifics_TabGroupColor_CYAN;
+    case TabGroupColor::kOrange:
+      return SyncTabGroupColor::WorkspaceDeskSpecifics_TabGroupColor_ORANGE;
+  };
+}
+
+void FillSyncTabGroupInfo(const app_restore::TabGroupInfo& tab_group_info,
+                          SyncTabGroup* out_sync_tab_group) {
+  out_sync_tab_group->set_first_index(tab_group_info.tab_range.start());
+  out_sync_tab_group->set_last_index(tab_group_info.tab_range.end());
+  out_sync_tab_group->set_title(
+      base::UTF16ToUTF8(tab_group_info.visual_data.title()));
+  // Save some storage space by leaving is_collapsed to default value if the
+  // tab group isn't collapsed.
+  if (tab_group_info.visual_data.is_collapsed()) {
+    out_sync_tab_group->set_is_collapsed(
+        tab_group_info.visual_data.is_collapsed());
+  }
+  out_sync_tab_group->set_color(
+      SyncTabColorFromTabGroupColorId(tab_group_info.visual_data.color()));
+}
+
+void FillBrowserAppTabGroupInfos(
+    const std::vector<app_restore::TabGroupInfo>& tab_group_infos,
+    BrowserAppWindow* out_browser_app_window) {
+  for (const auto& tab_group : tab_group_infos) {
+    SyncTabGroup* sync_tab_group = out_browser_app_window->add_tab_groups();
+    FillSyncTabGroupInfo(tab_group, sync_tab_group);
+  }
+}
+
 // Fill `out_browser_app_window` with the given GURLs as BrowserAppTabs.
 void FillBrowserAppTabs(const std::vector<GURL>& gurls,
                         BrowserAppWindow* out_browser_app_window) {
@@ -352,6 +477,11 @@
     out_browser_app_window->set_show_as_app(
         app_restore_data->app_type_browser.value());
   }
+
+  if (app_restore_data->tab_group_infos.has_value()) {
+    FillBrowserAppTabGroupInfos(app_restore_data->tab_group_infos.value(),
+                                out_browser_app_window);
+  }
 }
 
 // Fill `out_window_bounds` with information from `bounds`.
diff --git a/components/desks_storage/core/desk_sync_bridge_unittest.cc b/components/desks_storage/core/desk_sync_bridge_unittest.cc
index a59629d..b19c765 100644
--- a/components/desks_storage/core/desk_sync_bridge_unittest.cc
+++ b/components/desks_storage/core/desk_sync_bridge_unittest.cc
@@ -53,6 +53,8 @@
 using WindowBound = sync_pb::WorkspaceDeskSpecifics_WindowBound;
 using WindowState = sync_pb::WorkspaceDeskSpecifics_WindowState;
 using WorkspaceDeskSpecifics_App = sync_pb::WorkspaceDeskSpecifics_App;
+using SyncTabGroup = sync_pb::WorkspaceDeskSpecifics_BrowserAppWindow_TabGroup;
+using SyncTabGroupColor = sync_pb::WorkspaceDeskSpecifics_TabGroupColor;
 
 namespace {
 
@@ -89,6 +91,8 @@
 constexpr char kTestAppNameFormat[] = "_some_prefix_%s";
 constexpr int kDefaultTemplateIndex = 1;
 constexpr int kBrowserWindowId = 1555;
+constexpr char kTestTabGroupName[] = "test_tab_group";
+constexpr char kTestTabGroupNameFormat[] = "test_tab_group_%zu";
 
 // Example app index as set in `ExampleWorkspaceDeskSpecifics`.
 constexpr int kExampleDeskBrowserAppIndex = 0;
@@ -117,7 +121,10 @@
     "state\":\"NORMAL\",\"z_index\":1,\"app_type\":\"BROWSER\",\"tabs\":[{"
     "\"url\":\"https://example.com\",\"title\":\"Example\"},{\"url\":\"https://"
     "example.com/"
-    "2\",\"title\":\"Example2\"}],\"active_tab_index\":1,\"window_id\":0,"
+    "2\",\"title\":\"Example2\"}],\"tab_groups\":[{\"range_"
+    "start\":1,\"range_end\":2,\"title\":\"sample_tab_"
+    "group\",\"color\":\"GREY\",\"is_collapsed\":false}],\"active_tab_index\":"
+    "1,\"window_id\":0,"
     "\"display_id\":\"100\",\"pre_minimized_window_state\":\"NORMAL\"}]}},"
     "{\"version\":1,\"uuid\":\"" +
     base::StringPrintf(kUuidFormat, 9) +
@@ -133,11 +140,9 @@
     "2\",\"title\":\"Example2\"}],\"active_tab_index\":1,\"window_id\":0,"
     "\"display_id\":\"100\",\"pre_minimized_window_state\":\"NORMAL\"}]}}]";
 
-void FillExampleBrowserAppWindow(WorkspaceDeskSpecifics_App* app,
-                                 int number_of_tabs = 2) {
-  BrowserAppWindow* app_window =
-      app->mutable_app()->mutable_browser_app_window();
-
+void FillDefaultBrowserAppWindow(WorkspaceDeskSpecifics_App* app,
+                                 BrowserAppWindow* app_window,
+                                 int number_of_tabs) {
   for (int i = 0; i < number_of_tabs; ++i) {
     BrowserAppTab* tab = app_window->add_tabs();
     tab->set_url(base::StringPrintf(kTestUrlFormat, i));
@@ -158,6 +163,41 @@
   app->set_snap_percentage(75);
 }
 
+void FillExampleBrowserAppWindow(WorkspaceDeskSpecifics_App* app,
+                                 int number_of_tabs = 2) {
+  BrowserAppWindow* app_window =
+      app->mutable_app()->mutable_browser_app_window();
+
+  FillDefaultBrowserAppWindow(app, app_window, number_of_tabs);
+
+  SyncTabGroup* tab_group = app_window->add_tab_groups();
+  tab_group->set_first_index(1);
+  tab_group->set_last_index(2);
+  tab_group->set_title(kTestTabGroupName);
+  tab_group->set_color(
+      SyncTabGroupColor::WorkspaceDeskSpecifics_TabGroupColor_GREEN);
+}
+
+void FillExampleBrowserAppWindow(WorkspaceDeskSpecifics_App* app,
+                                 int tab_group_first_index,
+                                 int tab_group_last_index,
+                                 const std::string& tab_group_title,
+                                 bool tab_group_is_collapsed,
+                                 SyncTabGroupColor tab_group_color) {
+  BrowserAppWindow* app_window =
+      app->mutable_app()->mutable_browser_app_window();
+
+  FillDefaultBrowserAppWindow(app, app_window, tab_group_last_index);
+
+  SyncTabGroup* tab_group = app_window->add_tab_groups();
+  tab_group->set_first_index(tab_group_first_index);
+  tab_group->set_last_index(tab_group_last_index);
+  tab_group->set_title(tab_group_title);
+  if (tab_group_is_collapsed)
+    tab_group->set_is_collapsed(tab_group_is_collapsed);
+  tab_group->set_color(tab_group_color);
+}
+
 void FillExampleProgressiveWebAppWindow(WorkspaceDeskSpecifics_App* app) {
   ProgressiveWebApp* app_window =
       app->mutable_app()->mutable_progress_web_app();
@@ -261,6 +301,37 @@
   return specifics;
 }
 
+WorkspaceDeskSpecifics ExampleWorkspaceDeskSpecifics(
+    const std::string& uuid,
+    const std::string& template_name,
+    int tab_group_first_index,
+    int tab_group_last_index,
+    const std::string& tab_group_title,
+    bool tab_group_is_collapsed,
+    SyncTabGroupColor tab_group_color) {
+  base::Time created_time = base::Time::Now();
+
+  WorkspaceDeskSpecifics specifics;
+  specifics.set_uuid(uuid);
+  specifics.set_name(template_name);
+  specifics.set_created_time_windows_epoch_micros(
+      created_time.ToDeltaSinceWindowsEpoch().InMicroseconds());
+  specifics.set_updated_time_windows_epoch_micros(
+      (created_time + base::Minutes(5))
+          .ToDeltaSinceWindowsEpoch()
+          .InMicroseconds());
+  specifics.set_desk_type(
+      SyncDeskType::WorkspaceDeskSpecifics_DeskType_SAVE_AND_RECALL);
+  Desk* desk = specifics.mutable_desk();
+  FillExampleBrowserAppWindow(desk->add_apps(), tab_group_first_index,
+                              tab_group_last_index, tab_group_title,
+                              tab_group_is_collapsed, tab_group_color);
+  FillExampleArcAppWindow(desk->add_apps());
+  FillExampleChromeAppWindow(desk->add_apps());
+  FillExampleProgressiveWebAppWindow(desk->add_apps());
+  return specifics;
+}
+
 WorkspaceDeskSpecifics CreateWorkspaceDeskSpecifics(
     int templateIndex,
     base::Time created_time = base::Time::Now()) {
@@ -706,6 +777,55 @@
   }
 }
 
+TEST_F(DeskSyncBridgeTest, TabGroupInfoConversionShouldBeLossless) {
+  CreateBridge();
+
+  std::vector<SyncTabGroupColor> values = {
+      SyncTabGroupColor::WorkspaceDeskSpecifics_TabGroupColor_GREY,
+      SyncTabGroupColor::WorkspaceDeskSpecifics_TabGroupColor_BLUE,
+      SyncTabGroupColor::WorkspaceDeskSpecifics_TabGroupColor_RED,
+      SyncTabGroupColor::WorkspaceDeskSpecifics_TabGroupColor_YELLOW,
+      SyncTabGroupColor::WorkspaceDeskSpecifics_TabGroupColor_GREEN,
+      SyncTabGroupColor::WorkspaceDeskSpecifics_TabGroupColor_PINK,
+      SyncTabGroupColor::WorkspaceDeskSpecifics_TabGroupColor_PURPLE,
+      SyncTabGroupColor::WorkspaceDeskSpecifics_TabGroupColor_CYAN,
+      SyncTabGroupColor::WorkspaceDeskSpecifics_TabGroupColor_ORANGE};
+
+  // Iterate over colors, changing the values contained within the
+  // tab group for each iteration.
+  size_t curr_start = 0;
+  for (const auto& test_color_value : values) {
+    // We test with the color GREEN by default.
+    if (test_color_value ==
+        SyncTabGroupColor::WorkspaceDeskSpecifics_TabGroupColor_GREEN) {
+      continue;
+    }
+
+    WorkspaceDeskSpecifics desk_proto = ExampleWorkspaceDeskSpecifics(
+        kTestUuid1.AsLowercaseString(), "template 1",
+        /*Set a different range for each iteration.*/
+        curr_start, curr_start + 1,
+        /*Change name for each iteration.*/
+        base::StringPrintf(kTestTabGroupNameFormat, curr_start),
+        /*Modulate between true and false for is_collapsed value on each
+           iteration.*/
+        static_cast<bool>(curr_start % 2),
+        /*Set the color to the current iteration*/
+        test_color_value);
+
+    std::unique_ptr<DeskTemplate> desk_template =
+        DeskSyncBridge::FromSyncProto(desk_proto);
+
+    WorkspaceDeskSpecifics converted_desk_proto =
+        bridge()->ToSyncProto(desk_template.get());
+
+    EXPECT_THAT(converted_desk_proto, EqualsSpecifics(desk_proto));
+
+    // Shift range up by one.
+    ++curr_start;
+  }
+}
+
 // Tests that URLs are saved properly when converting a DeskTemplate
 // to its protobuf form.
 TEST_F(DeskSyncBridgeTest, EnsureBrowserWindowsSavedProperly) {
diff --git a/components/desks_storage/core/desk_template_conversion.cc b/components/desks_storage/core/desk_template_conversion.cc
index a76eb58..264e901 100644
--- a/components/desks_storage/core/desk_template_conversion.cc
+++ b/components/desks_storage/core/desk_template_conversion.cc
@@ -7,16 +7,20 @@
 #include "base/guid.h"
 #include "base/json/json_reader.h"
 #include "base/json/values_util.h"
+#include "base/notreached.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/time/time.h"
 #include "components/app_constants/constants.h"
 #include "components/app_restore/app_launch_info.h"
 #include "components/app_restore/restore_data.h"
+#include "components/app_restore/tab_group_info.h"
 #include "components/app_restore/window_info.h"
 #include "components/services/app_service/public/cpp/app_registry_cache.h"
 #include "components/services/app_service/public/cpp/app_types.h"
 #include "components/sync/protocol/proto_enum_conversions.h"
+#include "components/tab_groups/tab_group_color.h"
+#include "components/tab_groups/tab_group_visual_data.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 #include "ui/gfx/geometry/rect.h"
 
@@ -25,6 +29,7 @@
 using SyncWindowOpenDisposition =
     sync_pb::WorkspaceDeskSpecifics_WindowOpenDisposition;
 using SyncLaunchContainer = sync_pb::WorkspaceDeskSpecifics_LaunchContainer;
+using GroupColor = tab_groups::TabGroupColorId;
 
 // JSON value keys.
 constexpr char kActiveTabIndex[] = "active_tab_index";
@@ -55,15 +60,21 @@
 constexpr char kMinimumSize[] = "minimum_size";
 constexpr char kName[] = "name";
 constexpr char kPreMinimizedWindowState[] = "pre_minimized_window_state";
+constexpr char kTabRangeFirstIndex[] = "first_index";
+constexpr char kTabRangeLastIndex[] = "last_index";
 constexpr char kSizeHeight[] = "height";
 constexpr char kSizeWidth[] = "width";
 constexpr char kSnapPercentage[] = "snap_percent";
 constexpr char kTabs[] = "tabs";
+constexpr char kTabGroups[] = "tab_groups";
 constexpr char kTabUrl[] = "url";
 constexpr char kTitle[] = "title";
 constexpr char kUpdatedTime[] = "updated_time_usec";
 constexpr char kUuid[] = "uuid";
 constexpr char kVersion[] = "version";
+constexpr char kTabGroupTitleKey[] = "title";
+constexpr char kTabGroupColorKey[] = "color";
+constexpr char kTabGroupIsCollapsed[] = "is_collapsed";
 constexpr char kWindowId[] = "window_id";
 constexpr char kWindowBound[] = "window_bound";
 constexpr char kWindowBoundHeight[] = "height";
@@ -119,6 +130,12 @@
                                                   kWindowStatePrimarySnapped,
                                                   kWindowStateSecondarySnapped,
                                                   kZIndex};
+const std::set<std::string> kValidTabGroupColors = {
+    app_restore::kTabGroupColorUnknown, app_restore::kTabGroupColorGrey,
+    app_restore::kTabGroupColorBlue,    app_restore::kTabGroupColorRed,
+    app_restore::kTabGroupColorYellow,  app_restore::kTabGroupColorGreen,
+    app_restore::kTabGroupColorPink,    app_restore::kTabGroupColorPurple,
+    app_restore::kTabGroupColorCyan,    app_restore::kTabGroupColorOrange};
 
 // Version number.
 constexpr int kVersionNum = 1;
@@ -189,6 +206,111 @@
   return std::string();
 }
 
+// Convert a TabGroupInfo object to a base::Value dictionary.
+base::Value ConvertTabGroupInfoToValue(
+    const app_restore::TabGroupInfo& group_info) {
+  base::Value tab_group_dict(base::Value::Type::DICTIONARY);
+
+  tab_group_dict.SetIntKey(kTabRangeFirstIndex, group_info.tab_range.start());
+  tab_group_dict.SetIntKey(kTabRangeLastIndex, group_info.tab_range.end());
+  tab_group_dict.SetStringKey(
+      kTabGroupTitleKey, base::UTF16ToUTF8(group_info.visual_data.title()));
+  tab_group_dict.SetStringKey(
+      kTabGroupColorKey,
+      app_restore::TabGroupColorToString(group_info.visual_data.color()));
+  tab_group_dict.SetBoolKey(kTabGroupIsCollapsed,
+                            group_info.visual_data.is_collapsed());
+
+  return tab_group_dict;
+}
+
+bool IsValidGroupColor(const std::string& group_color) {
+  return base::Contains(kValidTabGroupColors, group_color);
+}
+
+GroupColor ConvertGroupColorStringToGroupColor(const std::string& group_color) {
+  if (group_color == app_restore::kTabGroupColorGrey) {
+    return GroupColor::kGrey;
+  } else if (group_color == app_restore::kTabGroupColorBlue) {
+    return GroupColor::kBlue;
+  } else if (group_color == app_restore::kTabGroupColorRed) {
+    return GroupColor::kRed;
+  } else if (group_color == app_restore::kTabGroupColorYellow) {
+    return GroupColor::kYellow;
+  } else if (group_color == app_restore::kTabGroupColorGreen) {
+    return GroupColor::kGreen;
+  } else if (group_color == app_restore::kTabGroupColorPink) {
+    return GroupColor::kPink;
+  } else if (group_color == app_restore::kTabGroupColorPurple) {
+    return GroupColor::kPurple;
+  } else if (group_color == app_restore::kTabGroupColorCyan) {
+    return GroupColor::kCyan;
+  } else if (group_color == app_restore::kTabGroupColorOrange) {
+    return GroupColor::kOrange;
+    // There is no UNKNOWN equivalent in GroupColor, simply default
+    // to grey.
+  } else if (group_color == app_restore::kTabGroupColorUnknown) {
+    return GroupColor::kGrey;
+  } else {
+    NOTREACHED();
+    return GroupColor::kGrey;
+  }
+}
+
+// Constructs a GroupVisualData from value `group_visual_data` IFF all fields
+// are present and valid in the value parameter.  Returns true on success, false
+// on failure.
+bool MakeTabGroupVisualDataFromValue(
+    const base::Value& tab_group,
+    tab_groups::TabGroupVisualData* out_visual_data) {
+  std::string tab_group_title;
+  std::string group_color_string;
+  bool is_collapsed;
+  if (GetString(tab_group, kTabGroupTitleKey, &tab_group_title) &&
+      GetBool(tab_group, kTabGroupIsCollapsed, &is_collapsed) &&
+      GetString(tab_group, kTabGroupColorKey, &group_color_string) &&
+      IsValidGroupColor(group_color_string)) {
+    *out_visual_data = tab_groups::TabGroupVisualData(
+        base::UTF8ToUTF16(tab_group_title),
+        ConvertGroupColorStringToGroupColor(group_color_string), is_collapsed);
+    return true;
+  }
+
+  return false;
+}
+
+// Constructs a gfx::Range from value `group_range` IFF all fields are
+// present and valid in the value parameter.  Returns true on success, false on
+// failure.
+bool MakeTabGroupRangeFromValue(const base::Value& tab_group,
+                                gfx::Range* out_range) {
+  int32_t range_start;
+  int32_t range_end;
+  if (GetInt(tab_group, kTabRangeFirstIndex, &range_start) &&
+      GetInt(tab_group, kTabRangeLastIndex, &range_end)) {
+    *out_range = gfx::Range(range_start, range_end);
+    return true;
+  }
+
+  return false;
+}
+
+// Constructs a TabGroupInfo from `tab_group` IFF all fields are present
+// and valid in the value parameter. Returns true on success, false on failure.
+absl::optional<app_restore::TabGroupInfo> MakeTabGroupInfoFromDict(
+    const base::Value& tab_group) {
+  absl::optional<app_restore::TabGroupInfo> tab_group_info = absl::nullopt;
+
+  tab_groups::TabGroupVisualData visual_data;
+  gfx::Range range;
+  if (MakeTabGroupRangeFromValue(tab_group, &range) &&
+      MakeTabGroupVisualDataFromValue(tab_group, &visual_data)) {
+    tab_group_info.emplace(range, visual_data);
+  }
+
+  return tab_group_info;
+}
+
 // Returns true if launch container string value is valid.
 bool IsValidLaunchContainer(const std::string& launch_container) {
   return base::Contains(kValidLaunchContainers, launch_container);
@@ -327,6 +449,21 @@
         }
       }
     }
+
+    // Fill the tab groups
+    app_launch_info->tab_group_infos.emplace();
+    const base::Value* tab_groups =
+        app.FindKeyOfType(kTabGroups, base::Value::Type::LIST);
+    if (tab_groups) {
+      for (auto& tab : tab_groups->GetList()) {
+        absl::optional<app_restore::TabGroupInfo> tab_group =
+            MakeTabGroupInfoFromDict(tab);
+        if (tab_group.has_value()) {
+          app_launch_info->tab_group_infos->push_back(
+              std::move(tab_group.value()));
+        }
+      }
+    }
   }
   // For Chrome apps and PWAs, the |app_id| is sufficient for identification.
 
@@ -692,6 +829,16 @@
   if (app->urls.has_value())
     app_data.SetKey(kTabs, ConvertURLsToBrowserAppTabValues(app->urls.value()));
 
+  if (app->tab_group_infos.has_value()) {
+    base::Value tab_groups_value(base::Value::Type::LIST);
+
+    for (const auto& tab_group : app->tab_group_infos.value()) {
+      tab_groups_value.Append(ConvertTabGroupInfoToValue(tab_group));
+    }
+
+    app_data.SetKey(kTabGroups, std::move(tab_groups_value));
+  }
+
   if (app->active_tab_index.has_value()) {
     app_data.SetKey(kActiveTabIndex,
                     base::Value(app->active_tab_index.value()));
@@ -786,6 +933,31 @@
 
 namespace desk_template_conversion {
 
+// Converts the TabGroupColorId passed into its string equivalent
+// as defined in the k constants above.
+std::string ConvertTabGroupColorIdToString(GroupColor color) {
+  switch (color) {
+    case GroupColor::kGrey:
+      return app_restore::kTabGroupColorGrey;
+    case GroupColor::kBlue:
+      return app_restore::kTabGroupColorBlue;
+    case GroupColor::kRed:
+      return app_restore::kTabGroupColorRed;
+    case GroupColor::kYellow:
+      return app_restore::kTabGroupColorYellow;
+    case GroupColor::kGreen:
+      return app_restore::kTabGroupColorGreen;
+    case GroupColor::kPink:
+      return app_restore::kTabGroupColorPink;
+    case GroupColor::kPurple:
+      return app_restore::kTabGroupColorPurple;
+    case GroupColor::kCyan:
+      return app_restore::kTabGroupColorCyan;
+    case GroupColor::kOrange:
+      return app_restore::kTabGroupColorOrange;
+  }
+}
+
 // Converts a time field from sync protobufs to a time object.
 base::Time ProtoTimeToTime(int64_t proto_time) {
   return base::Time::FromDeltaSinceWindowsEpoch(base::Microseconds(proto_time));
diff --git a/components/desks_storage/core/desk_template_conversion.h b/components/desks_storage/core/desk_template_conversion.h
index 741b493..ff9ad4f 100644
--- a/components/desks_storage/core/desk_template_conversion.h
+++ b/components/desks_storage/core/desk_template_conversion.h
@@ -32,6 +32,10 @@
     sync_pb::WorkspaceDeskSpecifics_WindowOpenDisposition;
 using SyncLaunchContainer = sync_pb::WorkspaceDeskSpecifics_LaunchContainer;
 
+// Converts the TabGroupColorId passed into its string equivalent
+// as defined in the k constants above.
+std::string ConvertTabGroupColorIdToString(tab_groups::TabGroupColorId color);
+
 // Converts a time field from sync protobufs to a time object.
 base::Time ProtoTimeToTime(int64_t proto_time);
 
diff --git a/components/desks_storage/core/desk_template_conversion_unittests.cc b/components/desks_storage/core/desk_template_conversion_unittests.cc
index 4a085f0..2274f64 100644
--- a/components/desks_storage/core/desk_template_conversion_unittests.cc
+++ b/components/desks_storage/core/desk_template_conversion_unittests.cc
@@ -46,7 +46,10 @@
     "state\":\"NORMAL\",\"z_index\":1,\"app_type\":\"BROWSER\",\"tabs\":[{"
     "\"url\":\"https://example.com/\"},{\"url\":\"https://"
     "example.com/"
-    "2\"}],\"active_tab_index\":1,\"window_id\":0,"
+    "2\"}],\"tab_groups\":[{\"first_"
+    "index\":1,\"last_index\":2,\"title\":\"sample_tab_"
+    "group\",\"color\":\"GREY\",\"is_collapsed\":false}],\"active_tab_index\":"
+    "1,\"window_id\":0,"
     "\"display_id\":\"100\",\"event_flag\":0,\"pre_minimized_window_state\":"
     "\"NORMAL\"}]}}";
 const std::string kValidTemplateChromeAndProgressive =
@@ -76,9 +79,19 @@
     "state\":\"NORMAL\",\"z_index\":1,\"app_type\":\"BROWSER\",\"tabs\":[{"
     "\"url\":\"https://example.com/\"},{\"url\":\"https://"
     "example.com/"
-    "2\"}],\"active_tab_index\":1,\"window_id\":0,"
+    "2\"}],\"tab_groups\":[{\"first_"
+    "index\":1,\"last_index\":2,\"title\":\"sample_tab_"
+    "group\",\"color\":\"GREY\",\"is_collapsed\":false}],\"active_tab_index\":"
+    "1,\"window_id\":0,"
     "\"display_id\":\"100\",\"event_flag\":0,\"pre_minimized_window_state\":"
     "\"NORMAL\"}]}}";
+const constexpr char16_t kSampleTabGroupTitle[] = u"sample_tab_group";
+
+app_restore::TabGroupInfo MakeSampleTabGroup() {
+  return app_restore::TabGroupInfo(
+      {1, 2}, tab_groups::TabGroupVisualData(
+                  kSampleTabGroupTitle, tab_groups::TabGroupColorId::kGrey));
+}
 
 }  // namespace
 
@@ -148,6 +161,8 @@
   EXPECT_TRUE(ali->urls.has_value());
   EXPECT_EQ(ali->urls.value()[0].spec(), "https://example.com/");
   EXPECT_EQ(ali->urls.value()[1].spec(), "https://example.com/2");
+  EXPECT_TRUE(ali->tab_group_infos.has_value());
+  EXPECT_EQ(ali->tab_group_infos.value()[0], MakeSampleTabGroup());
   EXPECT_TRUE(wi->window_state_type.has_value());
   EXPECT_EQ(wi->window_state_type.value(), chromeos::WindowStateType::kNormal);
   EXPECT_TRUE(wi->pre_minimized_show_state_type.has_value());
diff --git a/components/embedder_support/user_agent_utils_unittest.cc b/components/embedder_support/user_agent_utils_unittest.cc
index af497d7..de7fd76c 100644
--- a/components/embedder_support/user_agent_utils_unittest.cc
+++ b/components/embedder_support/user_agent_utils_unittest.cc
@@ -1016,11 +1016,11 @@
                            UserAgentReductionEnterprisePolicyState::kDefault);
   EXPECT_TRUE(re2::RE2::FullMatch(product, kChromeProductVersionRegex,
                                   &major_version, &minor_version,
-                                  &build_version, &patch_version));
+                                  &build_version));
   EXPECT_EQ(major_version, version_info::GetMajorVersionNumber());
   EXPECT_EQ(minor_version, "0");
   EXPECT_NE(build_version, "0");
-  EXPECT_EQ(patch_version, "0");
+  // Patch version cannot be tested as it would be set in a release branch.
 
   // (1b) Policies: UserAgentReduction and MajorVersionInMinor force enabled.
   product = GetProductAndVersion(
@@ -1040,11 +1040,11 @@
       UserAgentReductionEnterprisePolicyState::kForceDisabled);
   EXPECT_TRUE(re2::RE2::FullMatch(product, kChromeProductVersionRegex,
                                   &major_version, &minor_version,
-                                  &build_version, &patch_version));
+                                  &build_version));
   EXPECT_EQ(major_version, version_info::GetMajorVersionNumber());
   EXPECT_EQ(minor_version, "0");
   EXPECT_NE(build_version, "0");
-  EXPECT_EQ(patch_version, "0");
+  // Patch version cannot be tested as it would be set in a release branch.
 
   // (2) Features: UserAgentReduction enabled with version and
   // MajorVersionInMinor disabled.
@@ -1085,11 +1085,11 @@
       UserAgentReductionEnterprisePolicyState::kForceDisabled);
   EXPECT_TRUE(re2::RE2::FullMatch(product, kChromeProductVersionRegex,
                                   &major_version, &minor_version,
-                                  &build_version, &patch_version));
+                                  &build_version));
   EXPECT_EQ(major_version, version_info::GetMajorVersionNumber());
   EXPECT_EQ(minor_version, "0");
   EXPECT_NE(build_version, "5555");
-  EXPECT_EQ(patch_version, "0");
+  // Patch version cannot be tested as it would be set in a release branch.
 
   // (3) Features: UserAgentReduction disabled and MajorVersionInMinor enabled.
   scoped_feature_list.Reset();
@@ -1104,11 +1104,11 @@
                            UserAgentReductionEnterprisePolicyState::kDefault);
   EXPECT_TRUE(re2::RE2::FullMatch(product, kChromeProductVersionRegex,
                                   &major_version, &minor_version,
-                                  &build_version, &patch_version));
+                                  &build_version));
   EXPECT_EQ(major_version, "99");
   EXPECT_EQ(minor_version, version_info::GetMajorVersionNumber());
   EXPECT_NE(build_version, "0");
-  EXPECT_EQ(patch_version, "0");
+  // Patch version cannot be tested as it would be set in a release branch.
 
   // (3b) Policies: UserAgentReduction and MajorVersionInMinor force enabled.
   product = GetProductAndVersion(
@@ -1128,11 +1128,11 @@
       UserAgentReductionEnterprisePolicyState::kForceDisabled);
   EXPECT_TRUE(re2::RE2::FullMatch(product, kChromeProductVersionRegex,
                                   &major_version, &minor_version,
-                                  &build_version, &patch_version));
+                                  &build_version));
   EXPECT_EQ(major_version, version_info::GetMajorVersionNumber());
   EXPECT_EQ(minor_version, "0");
   EXPECT_NE(build_version, "0");
-  EXPECT_EQ(patch_version, "0");
+  // Patch version cannot be tested as it would be set in a release branch.
 
   // (4) Features: UserAgentReduction enabled and MajorVersionInMinor disabled.
   scoped_feature_list.Reset();
@@ -1171,11 +1171,11 @@
       UserAgentReductionEnterprisePolicyState::kForceDisabled);
   EXPECT_TRUE(re2::RE2::FullMatch(product, kChromeProductVersionRegex,
                                   &major_version, &minor_version,
-                                  &build_version, &patch_version));
+                                  &build_version));
   EXPECT_EQ(major_version, version_info::GetMajorVersionNumber());
   EXPECT_EQ(minor_version, "0");
   EXPECT_NE(build_version, "0");
-  EXPECT_EQ(patch_version, "0");
+  // Patch version cannot be tested as it would be set in a release branch.
 
   // (5) Features: UserAgentReduction and MajorVersionInMinor enabled.
   scoped_feature_list.Reset();
@@ -1215,11 +1215,11 @@
       UserAgentReductionEnterprisePolicyState::kForceDisabled);
   EXPECT_TRUE(re2::RE2::FullMatch(product, kChromeProductVersionRegex,
                                   &major_version, &minor_version,
-                                  &build_version, &patch_version));
+                                  &build_version));
   EXPECT_EQ(major_version, version_info::GetMajorVersionNumber());
   EXPECT_EQ(minor_version, "0");
   EXPECT_NE(build_version, "0");
-  EXPECT_EQ(patch_version, "0");
+  // Patch version cannot be tested as it would be set in a release branch.
 }
 
 TEST_F(UserAgentUtilsTest, GetUserAgent) {
diff --git a/components/exo/test/exo_test_suite_aura.cc b/components/exo/test/exo_test_suite_aura.cc
index a3aa9d9..e00ed4c 100644
--- a/components/exo/test/exo_test_suite_aura.cc
+++ b/components/exo/test/exo_test_suite_aura.cc
@@ -16,11 +16,11 @@
 
 void ExoTestSuiteAura::Initialize() {
   base::TestSuite::Initialize();
-  gl::GLSurfaceTestSupport::InitializeOneOff();
+  display_ = gl::GLSurfaceTestSupport::InitializeOneOff();
 }
 
 void ExoTestSuiteAura::Shutdown() {
-  gl::GLSurfaceTestSupport::ShutdownGL();
+  gl::GLSurfaceTestSupport::ShutdownGL(display_);
   base::TestSuite::Shutdown();
 }
 
diff --git a/components/exo/test/exo_test_suite_aura.h b/components/exo/test/exo_test_suite_aura.h
index 8217346..37cbbd7 100644
--- a/components/exo/test/exo_test_suite_aura.h
+++ b/components/exo/test/exo_test_suite_aura.h
@@ -6,6 +6,7 @@
 #define COMPONENTS_EXO_TEST_EXO_TEST_SUITE_AURA_H_
 
 #include "base/test/test_suite.h"
+#include "ui/gl/gl_display.h"
 
 namespace exo {
 namespace test {
@@ -27,6 +28,9 @@
   // base::TestSuite:
   void Initialize() override;
   void Shutdown() override;
+
+ private:
+  gl::GLDisplay* display_ = nullptr;
 };
 
 }  // namespace test
diff --git a/components/favicon_base/favicon_url_parser.cc b/components/favicon_base/favicon_url_parser.cc
index a8c23a8a8..2df39ba 100644
--- a/components/favicon_base/favicon_url_parser.cc
+++ b/components/favicon_base/favicon_url_parser.cc
@@ -97,18 +97,20 @@
     const base::StringPiece key = it.GetKey();
     // Note: each of these keys can be used in chrome://favicon2 path. See file
     // "favicon_url_parser.h" for a description of what each one does.
-    if (key == "allow_google_server_fallback") {
+    if (key == "allow_google_server_fallback" ||
+        key == "allowGoogleServerFallback") {
       const std::string val = it.GetUnescapedValue();
       if (!(val == "0" || val == "1"))
         return false;
       parsed->allow_favicon_server_fallback = val == "1";
-    } else if (key == "show_fallback_monogram") {
+    } else if (key == "show_fallback_monogram" ||
+               key == "showFallbackMonogram") {
       parsed->show_fallback_monogram = true;
-    } else if (key == "icon_url") {
+    } else if (key == "icon_url" || key == "iconUrl") {
       parsed->icon_url = it.GetUnescapedValue();
-    } else if (key == "page_url") {
+    } else if (key == "page_url" || key == "pageUrl") {
       parsed->page_url = it.GetUnescapedValue();
-    } else if (key == "scale_factor" &&
+    } else if ((key == "scale_factor" || key == "scaleFactor") &&
                !webui::ParseScaleFactor(it.GetUnescapedValue(),
                                         &parsed->device_scale_factor)) {
       return false;
diff --git a/components/flags_ui/feature_entry.h b/components/flags_ui/feature_entry.h
index c7d9a177..f61a438b 100644
--- a/components/flags_ui/feature_entry.h
+++ b/components/flags_ui/feature_entry.h
@@ -74,9 +74,6 @@
     // passed from the server in a trial config). When set to Enabled, the
     // feature is overriden to be enabled and empty set of parameters is used
     // boiling down to the default behavior in the code.
-    // TODO(crbug.com/805766): The resulting chrome://flags entries will not
-    // work on Chrome OS devices (but will work in the CrOS-emulated build on
-    // Linux).
     FEATURE_WITH_PARAMS_VALUE,
 
     // Corresponds to a command line switch where the value is treated as a list
diff --git a/components/flags_ui/flags_state.cc b/components/flags_ui/flags_state.cc
index 3c96c37..c3666aaf 100644
--- a/components/flags_ui/flags_state.cc
+++ b/components/flags_ui/flags_state.cc
@@ -27,6 +27,7 @@
 #include "components/flags_ui/flags_ui_switches.h"
 #include "components/variations/field_trial_config/field_trial_util.h"
 #include "components/variations/variations_associated_data.h"
+#include "components/variations/variations_switches.h"
 #include "ui/base/l10n/l10n_util.h"
 #include "url/gurl.h"
 #include "url/origin.h"
@@ -259,6 +260,10 @@
   // If |switch_name| is not empty, the value of the switch to set.
   std::string switch_value;
 
+  // If |variation_id| is not empty, variation id value to set.
+  // In the format of VariationsIdsProvider::ForceVariationIds().
+  std::string variation_id;
+
   SwitchEntry() : feature_state(false) {}
 };
 
@@ -296,7 +301,8 @@
 void FlagsState::GetSwitchesAndFeaturesFromFlags(
     FlagsStorage* flags_storage,
     std::set<std::string>* switches,
-    std::set<std::string>* features) const {
+    std::set<std::string>* features,
+    std::set<std::string>* variation_ids) const {
   std::set<std::string> enabled_entries;
   std::map<std::string, SwitchEntry> name_to_switch_map;
   GenerateFlagsToSwitchesMapping(flags_storage,
@@ -316,6 +322,9 @@
         features->insert(entry.feature_name + ":enabled");
       else
         features->insert(entry.feature_name + ":disabled");
+      if (!entry.variation_id.empty()) {
+        variation_ids->insert(entry.variation_id);
+      }
     }
   }
 }
@@ -651,12 +660,14 @@
     const std::string& key,
     const std::string& feature_name,
     bool feature_state,
+    const std::string& variation_id,
     std::map<std::string, SwitchEntry>* name_to_switch_map) const {
   DCHECK(!base::Contains(*name_to_switch_map, key));
 
   SwitchEntry* entry = &(*name_to_switch_map)[key];
   entry->feature_name = feature_name;
   entry->feature_state = feature_state;
+  entry->variation_id = variation_id;
 }
 
 void FlagsState::AddSwitchesToCommandLine(
@@ -672,6 +683,8 @@
     flags_switches_[switches::kFlagSwitchesBegin] = std::string();
   }
 
+  std::vector<std::string> variation_ids;
+
   for (const std::string& entry_name : enabled_entries) {
     const auto& entry_it = name_to_switch_map.find(entry_name);
     if (entry_it == name_to_switch_map.end()) {
@@ -682,6 +695,9 @@
     const SwitchEntry& entry = entry_it->second;
     if (!entry.feature_name.empty()) {
       feature_switches[entry.feature_name] = entry.feature_state;
+      if (!entry.variation_id.empty()) {
+        variation_ids.push_back(entry.variation_id);
+      }
     } else if (!entry.switch_name.empty()) {
       command_line->AppendSwitchASCII(entry.switch_name, entry.switch_value);
       flags_switches_[entry.switch_name] = entry.switch_value;
@@ -696,6 +712,10 @@
     MergeFeatureCommandLineSwitch(feature_switches, disable_features_flag_name,
                                   false, command_line);
   }
+  if (!variation_ids.empty()) {
+    command_line->AppendSwitchASCII(variations::switches::kForceVariationIds,
+                                    base::JoinString(variation_ids, ","));
+  }
 
   if (sentinels == kAddSentinels) {
     command_line->AppendSwitch(switches::kFlagSwitchesEnd);
@@ -834,13 +854,14 @@
           FeatureEntry::FeatureState state = entry.StateForOption(j);
           if (state == FeatureEntry::FeatureState::DEFAULT) {
             AddFeatureMapping(entry.NameForOption(j), std::string(), false,
-                              name_to_switch_map);
+                              std::string(), name_to_switch_map);
           } else {
             const FeatureEntry::FeatureVariation* variation =
                 entry.VariationForOption(j);
             std::string feature_name(entry.feature.feature->name);
             std::vector<std::string> params_value;
 
+            std::string variation_id;
             if (variation) {
               feature_name.append(":");
               for (int i = 0; i < variation->num_params; ++i) {
@@ -851,11 +872,14 @@
                 params_value.push_back(
                     param_name.append("/").append(param_value));
               }
+              if (variation->variation_id) {
+                variation_id = variation->variation_id;
+              }
             }
             AddFeatureMapping(
                 entry.NameForOption(j),
                 feature_name.append(base::JoinString(params_value, "/")),
-                state == FeatureEntry::FeatureState::ENABLED,
+                state == FeatureEntry::FeatureState::ENABLED, variation_id,
                 name_to_switch_map);
           }
         }
diff --git a/components/flags_ui/flags_state.h b/components/flags_ui/flags_state.h
index b7b6b3b2..cbf4358 100644
--- a/components/flags_ui/flags_state.h
+++ b/components/flags_ui/flags_state.h
@@ -115,9 +115,13 @@
   // switches corresponding to enabled entries and |features| with the set of
   // strings corresponding to enabled/disabled base::Feature states. Feature
   // names are suffixed with ":enabled" or ":disabled" depending on their state.
-  void GetSwitchesAndFeaturesFromFlags(FlagsStorage* flags_storage,
-                                       std::set<std::string>* switches,
-                                       std::set<std::string>* features) const;
+  // Also fills |variation_ids| with variation IDs to force based on
+  // flags_storage, in the format of VariationsIdsProvider::ForceVariationIds().
+  void GetSwitchesAndFeaturesFromFlags(
+      FlagsStorage* flags_storage,
+      std::set<std::string>* switches,
+      std::set<std::string>* features,
+      std::set<std::string>* variation_ids) const;
 
   bool IsRestartNeededToCommitChanges();
   void SetFeatureEntryEnabled(FlagsStorage* flags_storage,
@@ -190,11 +194,13 @@
       std::map<std::string, SwitchEntry>* name_to_switch_map) const;
 
   // Adds mapping to |name_to_switch_map| to toggle base::Feature |feature_name|
-  // to state |feature_state|.
+  // to state |feature_state|, along with the given |variation_id|, in the
+  // format of VariationsIdsProvider::ForceVariationIds().
   void AddFeatureMapping(
       const std::string& key,
       const std::string& feature_name,
       bool feature_state,
+      const std::string& variation_id,
       std::map<std::string, SwitchEntry>* name_to_switch_map) const;
 
   // Updates the switches in |command_line| by applying the modifications
diff --git a/components/flags_ui/flags_state_unittest.cc b/components/flags_ui/flags_state_unittest.cc
index 0dd72c1..ae1ec7c 100644
--- a/components/flags_ui/flags_state_unittest.cc
+++ b/components/flags_ui/flags_state_unittest.cc
@@ -30,6 +30,7 @@
 #include "components/prefs/testing_pref_service.h"
 #include "components/strings/grit/components_strings.h"
 #include "components/variations/variations_associated_data.h"
+#include "components/variations/variations_switches.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace flags_ui {
@@ -97,7 +98,7 @@
 const FeatureEntry::FeatureVariation kTestVariations3[] = {
     {"dummy description 1", kTestVariationOther1, 1, nullptr},
     {"dummy description 2", kTestVariationOther2, 1, nullptr},
-    {"dummy description 3", kTestVariationOther3, 2, nullptr}};
+    {"dummy description 3", kTestVariationOther3, 2, "t123456"}};
 
 const char kTestVariation3Cmdline[] =
     "FeatureName3:param1/value/param%3A%2F3/value";
@@ -343,6 +344,11 @@
   EXPECT_TRUE(command_line3.HasSwitch(kEnableFeatures));
   EXPECT_EQ(command_line3.GetSwitchValueASCII(kEnableFeatures),
             kTestVariation3Cmdline);
+  EXPECT_TRUE(
+      command_line3.HasSwitch(variations::switches::kForceVariationIds));
+  EXPECT_EQ(command_line3.GetSwitchValueASCII(
+                variations::switches::kForceVariationIds),
+            "t123456");
 }
 
 TEST_F(FlagsStateTest, RegisterAllFeatureVariationParameters) {
diff --git a/components/history/core/browser/history_types.cc b/components/history/core/browser/history_types.cc
index a9d1ef02..25fe3c3 100644
--- a/components/history/core/browser/history_types.cc
+++ b/components/history/core/browser/history_types.cc
@@ -12,6 +12,12 @@
 
 namespace history {
 
+namespace {
+
+static constexpr float kScoreEpsilon = 1e-8;
+
+}  // namespace
+
 // VisitRow --------------------------------------------------------------------
 
 VisitRow::VisitRow() = default;
@@ -425,6 +431,11 @@
 ClusterKeywordData::ClusterKeywordData(
     const std::vector<std::string>& entity_collections)
     : entity_collections(entity_collections) {}
+ClusterKeywordData::ClusterKeywordData(
+    ClusterKeywordData::ClusterKeywordType type,
+    float score,
+    const std::vector<std::string>& entity_collections)
+    : type(type), score(score), entity_collections(entity_collections) {}
 ClusterKeywordData::ClusterKeywordData(const ClusterKeywordData&) = default;
 ClusterKeywordData::ClusterKeywordData(ClusterKeywordData&&) = default;
 ClusterKeywordData& ClusterKeywordData::operator=(const ClusterKeywordData&) =
@@ -433,6 +444,18 @@
     default;
 ClusterKeywordData::~ClusterKeywordData() = default;
 
+bool ClusterKeywordData::operator==(const ClusterKeywordData& data) const {
+  return type == data.type && std::fabs(score - data.score) < kScoreEpsilon &&
+         entity_collections == data.entity_collections;
+}
+
+void ClusterKeywordData::MaybeUpdateKeywordType(
+    ClusterKeywordData::ClusterKeywordType other_type) {
+  if (type < other_type) {
+    type = other_type;
+  }
+}
+
 Cluster::Cluster() = default;
 Cluster::Cluster(int64_t cluster_id,
                  const std::vector<ClusterVisit>& visits,
diff --git a/components/history/core/browser/history_types.h b/components/history/core/browser/history_types.h
index 174c403..5f258897 100644
--- a/components/history/core/browser/history_types.h
+++ b/components/history/core/browser/history_types.h
@@ -884,14 +884,37 @@
 
 // Additional data for a cluster keyword.
 struct ClusterKeywordData {
+  // Types are ordered according to preferences.
+  enum ClusterKeywordType {
+    kUnknown = 0,
+    kEntityCategory = 1,
+    kEntityAlias = 2,
+    kEntity = 3,
+    kSearchTerms = 4
+  };
+
   ClusterKeywordData();
   explicit ClusterKeywordData(
       const std::vector<std::string>& entity_collections);
+  ClusterKeywordData(ClusterKeywordType type,
+                     float score,
+                     const std::vector<std::string>& entity_collections);
   ClusterKeywordData(const ClusterKeywordData&);
   ClusterKeywordData(ClusterKeywordData&&);
   ClusterKeywordData& operator=(const ClusterKeywordData&);
   ClusterKeywordData& operator=(ClusterKeywordData&&);
   ~ClusterKeywordData();
+  bool operator==(const ClusterKeywordData& data) const;
+
+  // Updates cluster keyword type if a new type is preferred over the existing
+  // type.
+  void MaybeUpdateKeywordType(ClusterKeywordType other_type);
+
+  ClusterKeywordType type;
+
+  // A floating point score describing how important this
+  // keyword is to the containing cluster.
+  float score;
 
   // Entity collections associated with the keyword this is attached to.
   std::vector<std::string> entity_collections;
diff --git a/components/history_clusters/core/config.cc b/components/history_clusters/core/config.cc
index 6319072..b7895a7 100644
--- a/components/history_clusters/core/config.cc
+++ b/components/history_clusters/core/config.cc
@@ -116,6 +116,14 @@
       history_clusters::features::kOnDeviceClusteringKeywordFiltering,
       "keyword_filter_on_visit_hosts", keyword_filter_on_visit_hosts);
 
+  category_keyword_score_weight = GetFieldTrialParamByFeatureAsDouble(
+      features::kOnDeviceClusteringKeywordFiltering,
+      "category_keyword_score_weight", category_keyword_score_weight);
+
+  max_num_keywords_per_cluster = GetFieldTrialParamByFeatureAsInt(
+      features::kOnDeviceClusteringKeywordFiltering,
+      "max_num_keywords_per_cluster", max_num_keywords_per_cluster);
+
   non_user_visible_debug =
       base::FeatureList::IsEnabled(internal::kNonUserVisibleDebug);
 
diff --git a/components/history_clusters/core/config.h b/components/history_clusters/core/config.h
index b360353..e88be63 100644
--- a/components/history_clusters/core/config.h
+++ b/components/history_clusters/core/config.h
@@ -121,6 +121,12 @@
   // the visit's host.
   bool keyword_filter_on_visit_hosts = true;
 
+  // The weight for category keyword scores per cluster.
+  float category_keyword_score_weight = 1.0;
+
+  // Maximum number of keywords to keep per cluster.
+  size_t max_num_keywords_per_cluster = 20;
+
   // Enables debug info in non-user-visible surfaces, like Chrome Inspector.
   // Does nothing if `kJourneys` is disabled.
   bool non_user_visible_debug = false;
diff --git a/components/history_clusters/core/keyword_cluster_finalizer.cc b/components/history_clusters/core/keyword_cluster_finalizer.cc
index 62a4f3a..8d8b5be 100644
--- a/components/history_clusters/core/keyword_cluster_finalizer.cc
+++ b/components/history_clusters/core/keyword_cluster_finalizer.cc
@@ -4,6 +4,8 @@
 
 #include "components/history_clusters/core/keyword_cluster_finalizer.h"
 
+#include <queue>
+
 #include "base/containers/contains.h"
 #include "base/containers/flat_set.h"
 #include "base/strings/string_split.h"
@@ -19,6 +21,9 @@
 
 namespace {
 
+static constexpr float kSearchTermsScore = 100.0;
+static constexpr float kScoreEpsilon = 1e-8;
+
 bool IsKeywordSimilarToVisitHost(
     const std::vector<std::u16string>& lowercase_host_parts,
     const std::u16string& keyword) {
@@ -33,6 +38,74 @@
   return base::Contains(lowercase_host_parts, stripped_lowercase_keyword);
 }
 
+// Computes an keyword score per cluster visit by mulitplying its visit-wise
+// weight and cluster visit score, and applying a weight factor.
+float ComputeKeywordScorePerClusterVisit(int visit_keyword_weight,
+                                         float cluster_visit_score,
+                                         float weight = 1.0) {
+  return static_cast<float>(visit_keyword_weight) * cluster_visit_score *
+         weight;
+}
+
+void KeepTopKeywords(
+    base::flat_map<std::u16string, history::ClusterKeywordData>&
+        keyword_to_data_map,
+    size_t max_num_keywords_per_cluster) {
+  if (keyword_to_data_map.size() <= max_num_keywords_per_cluster) {
+    return;
+  }
+
+  // Compare keywords first by their scores and then by types.
+  auto cmp_keywords =
+      [](const std::pair<std::u16string, const history::ClusterKeywordData*>&
+             left,
+         const std::pair<std::u16string, const history::ClusterKeywordData*>&
+             right) {
+        return std::fabs(left.second->score - right.second->type) >
+                       kScoreEpsilon
+                   ? left.second->score > right.second->score
+                   : left.second->type > right.second->type;
+      };
+
+  // Minimum priority queue of top keywords.
+  std::priority_queue<
+      std::pair<std::u16string, const history::ClusterKeywordData*>,
+      std::vector<
+          std::pair<std::u16string, const history::ClusterKeywordData*>>,
+      decltype(cmp_keywords)>
+      pq(cmp_keywords);
+  for (const auto& keyword_data_p : keyword_to_data_map) {
+    bool should_insert = false;
+    if (pq.size() < max_num_keywords_per_cluster) {
+      should_insert = true;
+    } else {
+      if (pq.top().second->score < keyword_data_p.second.score) {
+        pq.pop();
+        should_insert = true;
+      }
+    }
+    if (should_insert) {
+      pq.push(std::make_pair(keyword_data_p.first, &keyword_data_p.second));
+    }
+  }
+
+  base::flat_set<std::u16string> keywords_set;
+  while (!pq.empty()) {
+    keywords_set.insert(pq.top().first);
+    pq.pop();
+  }
+
+  auto it = keyword_to_data_map.begin();
+  for (; it != keyword_to_data_map.end();) {
+    if (!keywords_set.contains(it->first)) {
+      it = keyword_to_data_map.erase(it);
+    } else {
+      it++;
+    }
+  }
+  DCHECK_EQ(keyword_to_data_map.size(), max_num_keywords_per_cluster);
+}
+
 }  // namespace
 
 KeywordClusterFinalizer::KeywordClusterFinalizer(
@@ -61,12 +134,27 @@
       const std::u16string keyword_u16str = base::UTF8ToUTF16(entity.id);
       entity_keywords.insert(keyword_u16str);
 
-      auto it = entity_metadata_map_.find(entity.id);
+      // Add an entity to keyword data.
+      const float entity_score =
+          ComputeKeywordScorePerClusterVisit(entity.weight, visit.score);
+      auto keyword_it = keyword_to_data_map.find(keyword_u16str);
+      if (keyword_it != keyword_to_data_map.end()) {
+        // Accumulate entity scores from multiple visits.
+        keyword_it->second.score += entity_score;
+        keyword_it->second.MaybeUpdateKeywordType(
+            history::ClusterKeywordData::kEntity);
+      } else {
+        keyword_to_data_map[keyword_u16str] = history::ClusterKeywordData(
+            history::ClusterKeywordData::kEntity, entity_score,
+            /*entity_collections=*/{});
+      }
+
       // Add entity collections to keyword data.
-      keyword_to_data_map[keyword_u16str] =
-          it != entity_metadata_map_.end()
-              ? history::ClusterKeywordData(it->second.collections)
-              : history::ClusterKeywordData();
+      const auto it = entity_metadata_map_.find(entity.id);
+      if (it != entity_metadata_map_.end()) {
+        keyword_to_data_map[keyword_u16str].entity_collections =
+            it->second.collections;
+      }
 
       if (GetConfig().keyword_filter_on_entity_aliases) {
         if (it != entity_metadata_map_.end()) {
@@ -76,9 +164,18 @@
             const auto alias =
                 base::UTF8ToUTF16(it->second.human_readable_aliases[i]);
             entity_keywords.insert(alias);
-            // Use the same collections of an entity for its aliases as well.
-            keyword_to_data_map[alias] =
-                history::ClusterKeywordData(it->second.collections);
+            // Use the same score and collections of an entity for its aliases
+            // as well.
+            auto alias_it = keyword_to_data_map.find(alias);
+            if (alias_it == keyword_to_data_map.end()) {
+              keyword_to_data_map[alias] = history::ClusterKeywordData(
+                  history::ClusterKeywordData::kEntityAlias, entity_score,
+                  it->second.collections);
+            } else {
+              alias_it->second.score += entity_score;
+              alias_it->second.MaybeUpdateKeywordType(
+                  history::ClusterKeywordData::kEntityAlias);
+            }
           }
         }
       }
@@ -113,17 +210,44 @@
                                         category_u16string)) {
           continue;
         }
-        keyword_to_data_map[category_u16string] = history::ClusterKeywordData();
+        // Add a discounted keyword score for categories.
+        const float category_score = ComputeKeywordScorePerClusterVisit(
+            category.weight, visit.score,
+            GetConfig().category_keyword_score_weight);
+        auto category_it = keyword_to_data_map.find(category_u16string);
+        if (category_it == keyword_to_data_map.end()) {
+          keyword_to_data_map[category_u16string] = history::ClusterKeywordData(
+              history::ClusterKeywordData::kEntityCategory, category_score,
+              /*entity_collections=*/{});
+        } else {
+          // Accumulate category scores if there are multiple.
+          category_it->second.score += category_score;
+          category_it->second.MaybeUpdateKeywordType(
+              history::ClusterKeywordData::kEntityCategory);
+        }
       }
     }
 
     if (GetConfig().keyword_filter_on_search_terms &&
         !visit.annotated_visit.content_annotations.search_terms.empty()) {
-      keyword_to_data_map[visit.annotated_visit.content_annotations
-                              .search_terms] = history::ClusterKeywordData();
+      const auto& search_terms =
+          visit.annotated_visit.content_annotations.search_terms;
+      auto search_it = keyword_to_data_map.find(search_terms);
+      if (search_it == keyword_to_data_map.end()) {
+        keyword_to_data_map[search_terms] = history::ClusterKeywordData(
+            history::ClusterKeywordData::kSearchTerms,
+            /*score=*/kSearchTermsScore, /*entity_collections=*/{});
+      } else {
+        search_it->second.score += kSearchTermsScore;
+        search_it->second.MaybeUpdateKeywordType(
+            history::ClusterKeywordData::kSearchTerms);
+      }
     }
   }
 
+  KeepTopKeywords(keyword_to_data_map,
+                  GetConfig().max_num_keywords_per_cluster);
+
   cluster.keyword_to_data_map = std::move(keyword_to_data_map);
 }
 
diff --git a/components/history_clusters/core/keyword_cluster_finalizer_unittest.cc b/components/history_clusters/core/keyword_cluster_finalizer_unittest.cc
index f5dba63..0eb9959c 100644
--- a/components/history_clusters/core/keyword_cluster_finalizer_unittest.cc
+++ b/components/history_clusters/core/keyword_cluster_finalizer_unittest.cc
@@ -34,7 +34,8 @@
     config_.keyword_filter_on_categories = false;
     config_.keyword_filter_on_entity_aliases = false;
     config_.keyword_filter_on_search_terms = false;
-    config_.keyword_filter_on_visit_hosts = false;
+    config_.keyword_filter_on_visit_hosts =
+        false;  // Drop keywords match host names.
     SetConfigForTesting(config_);
   }
 
@@ -74,7 +75,9 @@
   visit3.duplicate_visits.push_back(visit);
   visit3.engagement_score = 1.0;
   visit3.annotated_visit.content_annotations.model_annotations.entities = {
-      {"github", 1}, {"otherentity", 1}, {"baz", 1}};
+      {"github", 1},
+      {"otherentity", 1},
+      {"baz", 1} /*should be filtered due to host*/};
   visit3.annotated_visit.content_annotations.model_annotations.categories = {
       {"category", 1}};
   visit3.annotated_visit.content_annotations.search_terms = u"search";
@@ -83,12 +86,17 @@
   cluster.visits = {visit2, visit3};
   FinalizeCluster(cluster);
 
+  EXPECT_THAT(cluster.GetKeywords(),
+              UnorderedElementsAre(u"github", u"otherentity"));
   ASSERT_TRUE(cluster.keyword_to_data_map.contains(u"github"));
-  EXPECT_EQ(cluster.keyword_to_data_map[u"github"].entity_collections,
-            std::vector<std::string>{"/collection/computer"});
+  EXPECT_EQ(cluster.keyword_to_data_map.at(u"github"),
+            history::ClusterKeywordData(
+                history::ClusterKeywordData::kEntity, 1,
+                std::vector<std::string>{"/collection/computer"}));
   ASSERT_TRUE(cluster.keyword_to_data_map.contains(u"otherentity"));
-  EXPECT_TRUE(
-      cluster.keyword_to_data_map[u"otherentity"].entity_collections.empty());
+  EXPECT_EQ(
+      cluster.keyword_to_data_map.at(u"otherentity"),
+      history::ClusterKeywordData(history::ClusterKeywordData::kEntity, 1, {}));
 }
 
 class KeywordClusterFinalizerIncludeAllTest
@@ -103,6 +111,8 @@
     config_.max_entity_aliases_in_keywords = 1;
     config_.keyword_filter_on_search_terms = true;
     config_.keyword_filter_on_visit_hosts = true;
+    config_.category_keyword_score_weight = 0.1;
+    config_.max_num_keywords_per_cluster = 7;
     SetConfigForTesting(config_);
   }
 
@@ -135,35 +145,50 @@
   visit3.duplicate_visits.push_back(visit);
   visit3.engagement_score = 1.0;
   visit3.annotated_visit.content_annotations.model_annotations.entities = {
-      {"github", 1}, {"otherentity", 1}, {"baz", 1}};
+      {"github", 1}, {"otherentity", 1}, {"baz", 1}, {"search", 1}};
   visit3.annotated_visit.content_annotations.model_annotations.categories = {
+      {"category2", 0},  // `category2` is dropped due to keywords capping.
       {"category", 1}};
-  visit3.annotated_visit.content_annotations.search_terms = u"search";
+  visit3.annotated_visit.content_annotations.search_terms =
+      u"search";  // Keyword type should be `kSearchTerms`.
 
   history::Cluster cluster;
   cluster.visits = {visit2, visit3};
   FinalizeCluster(cluster);
 
+  EXPECT_THAT(
+      cluster.GetKeywords(),
+      UnorderedElementsAre(u"github", u"git hub", u"otherentity", u"baz",
+                           u"category", u"onlyinnoisyvisit", u"search"));
   ASSERT_TRUE(cluster.keyword_to_data_map.contains(u"github"));
-  EXPECT_EQ(cluster.keyword_to_data_map[u"github"].entity_collections,
-            std::vector<std::string>{"/collection/computer"});
+  EXPECT_EQ(cluster.keyword_to_data_map.at(u"github"),
+            history::ClusterKeywordData(history::ClusterKeywordData::kEntity, 2,
+                                        {"/collection/computer"}));
   ASSERT_TRUE(cluster.keyword_to_data_map.contains(u"git hub"));
-  EXPECT_EQ(cluster.keyword_to_data_map[u"git hub"].entity_collections,
-            std::vector<std::string>{"/collection/computer"});
+  EXPECT_EQ(
+      cluster.keyword_to_data_map.at(u"git hub"),
+      history::ClusterKeywordData(history::ClusterKeywordData::kEntityAlias, 2,
+                                  {"/collection/computer"}));
   ASSERT_TRUE(cluster.keyword_to_data_map.contains(u"category"));
-  EXPECT_TRUE(
-      cluster.keyword_to_data_map[u"category"].entity_collections.empty());
+  EXPECT_EQ(cluster.keyword_to_data_map.at(u"category"),
+            history::ClusterKeywordData(
+                history::ClusterKeywordData::kEntityCategory, 0.2, {}));
   ASSERT_TRUE(cluster.keyword_to_data_map.contains(u"onlyinnoisyvisit"));
-  EXPECT_TRUE(cluster.keyword_to_data_map[u"onlyinnoisyvisit"]
-                  .entity_collections.empty());
+  EXPECT_EQ(
+      cluster.keyword_to_data_map.at(u"onlyinnoisyvisit"),
+      history::ClusterKeywordData(history::ClusterKeywordData::kEntity, 1, {}));
   ASSERT_TRUE(cluster.keyword_to_data_map.contains(u"otherentity"));
-  EXPECT_TRUE(
-      cluster.keyword_to_data_map[u"otherentity"].entity_collections.empty());
+  EXPECT_EQ(
+      cluster.keyword_to_data_map.at(u"otherentity"),
+      history::ClusterKeywordData(history::ClusterKeywordData::kEntity, 1, {}));
   ASSERT_TRUE(cluster.keyword_to_data_map.contains(u"search"));
-  EXPECT_TRUE(
-      cluster.keyword_to_data_map[u"search"].entity_collections.empty());
+  EXPECT_EQ(cluster.keyword_to_data_map.at(u"search"),
+            history::ClusterKeywordData(
+                history::ClusterKeywordData::kSearchTerms, 101, {}));
   ASSERT_TRUE(cluster.keyword_to_data_map.contains(u"baz"));
-  EXPECT_TRUE(cluster.keyword_to_data_map[u"baz"].entity_collections.empty());
+  EXPECT_EQ(
+      cluster.keyword_to_data_map.at(u"baz"),
+      history::ClusterKeywordData(history::ClusterKeywordData::kEntity, 1, {}));
 }
 
 }  // namespace
diff --git a/components/image_fetcher/android/java/src/org/chromium/components/image_fetcher/ImageFetcherBridge.java b/components/image_fetcher/android/java/src/org/chromium/components/image_fetcher/ImageFetcherBridge.java
index c0a0bf2e..f82926f 100644
--- a/components/image_fetcher/android/java/src/org/chromium/components/image_fetcher/ImageFetcherBridge.java
+++ b/components/image_fetcher/android/java/src/org/chromium/components/image_fetcher/ImageFetcherBridge.java
@@ -90,7 +90,8 @@
     public void fetchImage(@ImageFetcherConfig int config, final ImageFetcher.Params params,
             Callback<Bitmap> callback) {
         ImageFetcherBridgeJni.get().fetchImage(mSimpleFactoryKeyHandle, config, params.url,
-                params.clientName, params.expirationIntervalMinutes, (bitmap) -> {
+                params.clientName, params.width, params.height, params.expirationIntervalMinutes,
+                (bitmap) -> {
                     callback.onResult(
                             ImageFetcher.resizeImage(bitmap, params.width, params.height));
                 });
@@ -136,8 +137,8 @@
                 @ImageFetcherConfig int config, String url, String clientName,
                 int expirationIntervalMinutes, Callback<byte[]> callback);
         void fetchImage(SimpleFactoryKeyHandle simpleFactoryKeyHandle,
-                @ImageFetcherConfig int config, String url, String clientName,
-                int expirationIntervalMinutes, Callback<Bitmap> callback);
+                @ImageFetcherConfig int config, String url, String clientName, int frameWidth,
+                int frameHeight, int expirationIntervalMinutes, Callback<Bitmap> callback);
         void reportEvent(String clientName, int eventId);
         void reportCacheHitTime(String clientName, long startTimeMillis);
         void reportTotalFetchTimeFromNative(String clientName, long startTimeMillis);
diff --git a/components/image_fetcher/android/junit/src/org/chromium/components/image_fetcher/ImageFetcherBridgeTest.java b/components/image_fetcher/android/junit/src/org/chromium/components/image_fetcher/ImageFetcherBridgeTest.java
index a817ecf..aef2a7f 100644
--- a/components/image_fetcher/android/junit/src/org/chromium/components/image_fetcher/ImageFetcherBridgeTest.java
+++ b/components/image_fetcher/android/junit/src/org/chromium/components/image_fetcher/ImageFetcherBridgeTest.java
@@ -72,8 +72,8 @@
             return null;
         })
                 .when(mNatives)
-                .fetchImage(eq(mSimpleFactoryKeyHandle), anyInt(), anyString(), anyString(), eq(0),
-                        callbackCaptor.capture());
+                .fetchImage(eq(mSimpleFactoryKeyHandle), anyInt(), anyString(), anyString(),
+                        eq(WIDTH_PX), eq(HEIGHT_PX), eq(0), callbackCaptor.capture());
 
         mBridge.fetchImage(
                 -1, ImageFetcher.Params.create("", "", WIDTH_PX, HEIGHT_PX), mBitmapCallback);
@@ -90,7 +90,8 @@
         })
                 .when(mNatives)
                 .fetchImage(eq(mSimpleFactoryKeyHandle), anyInt(), anyString(), anyString(),
-                        eq(EXPIRATION_INTERVAL_MINS), callbackCaptor.capture());
+                        eq(WIDTH_PX), eq(HEIGHT_PX), eq(EXPIRATION_INTERVAL_MINS),
+                        callbackCaptor.capture());
 
         mBridge.fetchImage(-1,
                 ImageFetcher.Params.createWithExpirationInterval(
@@ -102,6 +103,9 @@
 
     @Test
     public void testFetchImage_imageResized() {
+        int desiredWidth = 100;
+        int desiredHeight = 100;
+
         ArgumentCaptor<Callback<Bitmap>> callbackCaptor = ArgumentCaptor.forClass(Callback.class);
         final Bitmap bitmap = Bitmap.createBitmap(10, 10, Bitmap.Config.ARGB_8888);
         doAnswer((InvocationOnMock invocation) -> {
@@ -109,10 +113,11 @@
             return null;
         })
                 .when(mNatives)
-                .fetchImage(eq(mSimpleFactoryKeyHandle), anyInt(), anyString(), anyString(), eq(0),
-                        callbackCaptor.capture());
+                .fetchImage(eq(mSimpleFactoryKeyHandle), anyInt(), anyString(), anyString(),
+                        eq(desiredWidth), eq(desiredHeight), eq(0), callbackCaptor.capture());
 
-        mBridge.fetchImage(-1, ImageFetcher.Params.create("", "", 100, 100), mBitmapCallback);
+        mBridge.fetchImage(-1, ImageFetcher.Params.create("", "", desiredWidth, desiredHeight),
+                mBitmapCallback);
         ArgumentCaptor<Bitmap> bitmapCaptor = ArgumentCaptor.forClass(Bitmap.class);
         verify(mBitmapCallback).onResult(bitmapCaptor.capture());
 
@@ -187,4 +192,4 @@
         Assert.assertNotEquals(
                 mBridge, ImageFetcherBridge.getForSimpleFactoryKeyHandle(mSimpleFactoryKeyHandle));
     }
-}
\ No newline at end of file
+}
diff --git a/components/image_fetcher/image_fetcher_bridge.cc b/components/image_fetcher/image_fetcher_bridge.cc
index 1570a61..b84f200b 100644
--- a/components/image_fetcher/image_fetcher_bridge.cc
+++ b/components/image_fetcher/image_fetcher_bridge.cc
@@ -129,6 +129,8 @@
     const jint j_image_fetcher_config,
     const JavaParamRef<jstring>& j_url,
     const JavaParamRef<jstring>& j_client_name,
+    const jint j_frame_width,
+    const jint j_frame_height,
     const jint j_expiration_interval_mins,
     const JavaParamRef<jobject>& j_callback) {
   ScopedJavaGlobalRef<jobject> callback(j_callback);
@@ -139,6 +141,7 @@
       base::android::ConvertJavaStringToUTF8(j_client_name);
 
   ImageFetcherParams params(kTrafficAnnotation, client_name);
+  params.set_frame_size(gfx::Size(j_frame_width, j_frame_height));
   if (j_expiration_interval_mins > 0) {
     params.set_hold_for_expiration_interval(
         base::Minutes(j_expiration_interval_mins));
@@ -221,11 +224,13 @@
     const jint j_image_fetcher_config,
     const JavaParamRef<jstring>& j_url,
     const JavaParamRef<jstring>& j_client_name,
+    const jint j_frame_width,
+    const jint j_frame_height,
     const jint j_expiration_interval_mins,
     const JavaParamRef<jobject>& j_callback) {
-  ImageFetcherBridge::FetchImage(j_env, j_simple_factory_key,
-                                 j_image_fetcher_config, j_url, j_client_name,
-                                 j_expiration_interval_mins, j_callback);
+  ImageFetcherBridge::FetchImage(
+      j_env, j_simple_factory_key, j_image_fetcher_config, j_url, j_client_name,
+      j_frame_width, j_frame_height, j_expiration_interval_mins, j_callback);
 }
 
 // static
diff --git a/components/image_fetcher/image_fetcher_bridge.h b/components/image_fetcher/image_fetcher_bridge.h
index 11657d4..786ccba 100644
--- a/components/image_fetcher/image_fetcher_bridge.h
+++ b/components/image_fetcher/image_fetcher_bridge.h
@@ -45,6 +45,8 @@
       const jint j_image_fetcher_config,
       const base::android::JavaParamRef<jstring>& j_url,
       const base::android::JavaParamRef<jstring>& j_client_name,
+      const jint j_frame_width,
+      const jint j_frame_height,
       const jint j_expiration_interval_mins,
       const base::android::JavaParamRef<jobject>& j_callback);
 
diff --git a/components/messages/android/messages_feature.cc b/components/messages/android/messages_feature.cc
index 2f0965f..3b85d3e 100644
--- a/components/messages/android/messages_feature.cc
+++ b/components/messages/android/messages_feature.cc
@@ -24,7 +24,7 @@
     "MessagesForAndroidNearOomReduction", base::FEATURE_ENABLED_BY_DEFAULT};
 
 const base::Feature kMessagesForAndroidNotificationBlocked{
-    "MessagesForAndroidNotificationBlocked", base::FEATURE_DISABLED_BY_DEFAULT};
+    "MessagesForAndroidNotificationBlocked", base::FEATURE_ENABLED_BY_DEFAULT};
 
 const base::Feature kMessagesForAndroidOfferNotification{
     "MessagesForAndroidOfferNotification", base::FEATURE_DISABLED_BY_DEFAULT};
diff --git a/components/policy/tools/template_writers/PRESUBMIT.py b/components/policy/tools/template_writers/PRESUBMIT.py
index e159b0d3..af30ccf 100755
--- a/components/policy/tools/template_writers/PRESUBMIT.py
+++ b/components/policy/tools/template_writers/PRESUBMIT.py
@@ -13,8 +13,10 @@
 
 
 def RunUnittests(input_api, output_api):
-  return input_api.canned_checks.RunPythonUnitTests(input_api, output_api,
-                                                    ['test_suite_all'])
+  return input_api.canned_checks.RunPythonUnitTests(input_api,
+                                                    output_api,
+                                                    ['test_suite_all'],
+                                                    python3=True)
 
 
 def CheckChangeOnUpload(input_api, output_api):
diff --git a/components/safe_browsing/BUILD.gn b/components/safe_browsing/BUILD.gn
index 71f40a9..f076662 100644
--- a/components/safe_browsing/BUILD.gn
+++ b/components/safe_browsing/BUILD.gn
@@ -9,6 +9,19 @@
   header = "buildflags.h"
 
   flags = []
+
+  # FULL_SAFE_BROWSING means "are all Safe Browsing features available?"
+  # This is true only for desktop OSes.
+  #
+  # SAFE_BROWSING_AVAILABLE means "are any Safe Browsing features available?"
+  # This is true only for desktop OSes or Android.
+  #
+  # SAFE_BROWSING_DB_LOCAL means "are SB databases available locally?"
+  # This is true only for desktop OSes.
+  #
+  # SAFE_BROWSING_DB_REMOTE means "are SB databases available via GMS Core?"
+  # This is true only for Android.
+  #
   if (safe_browsing_mode == 0) {
     flags += [ "FULL_SAFE_BROWSING=0" ]
     flags += [ "SAFE_BROWSING_AVAILABLE=0" ]
diff --git a/components/safe_browsing/core/common/features.cc b/components/safe_browsing/core/common/features.cc
index d0bb3e7..a6da37d 100644
--- a/components/safe_browsing/core/common/features.cc
+++ b/components/safe_browsing/core/common/features.cc
@@ -52,6 +52,11 @@
 const base::Feature kConnectorsScanningReportOnlyUI{
     "ConnectorsScanningReportOnlyUI", base::FEATURE_ENABLED_BY_DEFAULT};
 
+#if BUILDFLAG(IS_ANDROID)
+const base::Feature kCreateSafebrowsingOnStartup{
+    "CreateSafebrowsingOnStartup", base::FEATURE_DISABLED_BY_DEFAULT};
+#endif
+
 const base::Feature kDelayedWarnings{"SafeBrowsingDelayedWarnings",
                                      base::FEATURE_DISABLED_BY_DEFAULT};
 
diff --git a/components/safe_browsing/core/common/features.h b/components/safe_browsing/core/common/features.h
index 1e703e92..d62a47c5d 100644
--- a/components/safe_browsing/core/common/features.h
+++ b/components/safe_browsing/core/common/features.h
@@ -53,6 +53,13 @@
 // instead of just showing an "Open Now" button with the blocking UI.
 extern const base::Feature kConnectorsScanningReportOnlyUI;
 
+// Controls whether to connect to the Safe Browsing service early on startup.
+// The alternative is to connect as soon as the first Safe Browsing check is
+// made associated with a URK request. Android only. On this platform getting
+// the notification about the success of establishing the connection can be
+// delayed by several seconds.
+extern const base::Feature kCreateSafebrowsingOnStartup;
+
 // Controls whether the delayed warning experiment is enabled.
 extern const base::Feature kDelayedWarnings;
 // True if mouse clicks should undelay the warnings immediately when delayed
diff --git a/components/shared_highlighting/core/common/shared_highlighting_features.cc b/components/shared_highlighting/core/common/shared_highlighting_features.cc
index 0eaa4f4..9006f4c4 100644
--- a/components/shared_highlighting/core/common/shared_highlighting_features.cc
+++ b/components/shared_highlighting/core/common/shared_highlighting_features.cc
@@ -33,6 +33,9 @@
     "SharedHighlightingRefinedMaxContextWords",
     base::FEATURE_DISABLED_BY_DEFAULT};
 
+const base::Feature kSharedHighlightingManager{
+    "SharedHighlightingManager", base::FEATURE_DISABLED_BY_DEFAULT};
+
 const char kSharedHighlightingRefinedMaxContextWordsName[] =
     "SharedHighlightingRefinedMaxContextWords";
 
diff --git a/components/shared_highlighting/core/common/shared_highlighting_features.h b/components/shared_highlighting/core/common/shared_highlighting_features.h
index 9ed78fee..5f2c42ee 100644
--- a/components/shared_highlighting/core/common/shared_highlighting_features.h
+++ b/components/shared_highlighting/core/common/shared_highlighting_features.h
@@ -21,6 +21,9 @@
 // Enables shared highlighting for AMP viewers pages.
 extern const base::Feature kSharedHighlightingAmp;
 
+// Enables the new SharedHighlightingManager refactoring.
+extern const base::Feature kSharedHighlightingManager;
+
 // Feature flag that enable Shared Highlighting V2 in iOS.
 extern const base::Feature kIOSSharedHighlightingV2;
 
diff --git a/components/sync/protocol/history_specifics.proto b/components/sync/protocol/history_specifics.proto
index 7ae0c06..05e2003d 100644
--- a/components/sync/protocol/history_specifics.proto
+++ b/components/sync/protocol/history_specifics.proto
@@ -16,6 +16,8 @@
 
 package sync_pb;
 
+import "components/sync/protocol/sync_enums.proto";
+
 // A history sync entity - this roughly represents one navigation, including its
 // full redirect chain (but not referrals). Fields correspond to similarly named
 // fields in history::VisitRow and history::URLRow.
@@ -36,14 +38,30 @@
     optional string title = 3;
     // True if the URL should NOT be used for auto-complete.
     optional bool hidden = 4;
+    // The redirect type (if any).
+    optional SyncEnums.PageTransitionRedirectType redirect_type = 5;
   }
   // The redirect chain. The first entry is the URL the user originally
   // navigated to; the last one is where they ended up. If there were no
   // redirects, this has only one entry.
   repeated RedirectEntry redirect_entries = 3;
 
+  message PageTransition {
+    // The core transition type.
+    optional SyncEnums.PageTransition core_transition = 1 [default = LINK];
+    // Qualifiers:
+    // A supervised user tried to access this URL but was blocked.
+    optional bool blocked = 2;
+    // User used the Forward or Back button to navigate among browsing history.
+    optional bool forward_back = 3;
+    // User used the address bar to trigger this navigation.
+    optional bool from_address_bar = 4;
+    // User is navigating to the home page.
+    optional bool home_page = 5;
+  }
   // The PageTransition for the navigation.
-  optional int32 page_transition = 4;
+  optional PageTransition page_transition = 4;
+
   // The ID of the visit, on the originator client, that was a referrer for
   // this one, or 0 if no referrer exists.
   optional int64 originator_referring_visit_id = 5;
diff --git a/components/sync/protocol/proto_enum_conversions.cc b/components/sync/protocol/proto_enum_conversions.cc
index 0a1d32d..1a13f963 100644
--- a/components/sync/protocol/proto_enum_conversions.cc
+++ b/components/sync/protocol/proto_enum_conversions.cc
@@ -749,6 +749,27 @@
   return "";
 }
 
+const char* ProtoEnumToString(
+    sync_pb::WorkspaceDeskSpecifics::TabGroupColor color) {
+  ASSERT_ENUM_BOUNDS(sync_pb::WorkspaceDeskSpecifics, TabGroupColor,
+                     UNKNOWN_COLOR, ORANGE);
+
+  switch (color) {
+    ENUM_CASE(sync_pb::WorkspaceDeskSpecifics, UNKNOWN_COLOR);
+    ENUM_CASE(sync_pb::WorkspaceDeskSpecifics, GREY);
+    ENUM_CASE(sync_pb::WorkspaceDeskSpecifics, BLUE);
+    ENUM_CASE(sync_pb::WorkspaceDeskSpecifics, RED);
+    ENUM_CASE(sync_pb::WorkspaceDeskSpecifics, YELLOW);
+    ENUM_CASE(sync_pb::WorkspaceDeskSpecifics, GREEN);
+    ENUM_CASE(sync_pb::WorkspaceDeskSpecifics, PINK);
+    ENUM_CASE(sync_pb::WorkspaceDeskSpecifics, PURPLE);
+    ENUM_CASE(sync_pb::WorkspaceDeskSpecifics, CYAN);
+    ENUM_CASE(sync_pb::WorkspaceDeskSpecifics, ORANGE);
+  }
+  NOTREACHED();
+  return "";
+}
+
 #undef ASSERT_ENUM_BOUNDS
 #undef ENUM_CASE
 
diff --git a/components/sync/protocol/proto_enum_conversions.h b/components/sync/protocol/proto_enum_conversions.h
index 5ceb173..4c61331 100644
--- a/components/sync/protocol/proto_enum_conversions.h
+++ b/components/sync/protocol/proto_enum_conversions.h
@@ -164,6 +164,9 @@
 
 const char* ProtoEnumToString(sync_pb::WorkspaceDeskSpecifics::DeskType type);
 
+const char* ProtoEnumToString(
+    sync_pb::WorkspaceDeskSpecifics::TabGroupColor color);
+
 }  // namespace syncer
 
 #endif  // COMPONENTS_SYNC_PROTOCOL_PROTO_ENUM_CONVERSIONS_H_
diff --git a/components/sync/protocol/proto_visitors.h b/components/sync/protocol/proto_visitors.h
index 4ea036f..2d8bddd9 100644
--- a/components/sync/protocol/proto_visitors.h
+++ b/components/sync/protocol/proto_visitors.h
@@ -691,11 +691,20 @@
   // etc.
 }
 
+VISIT_PROTO_FIELDS(const sync_pb::HistorySpecifics::PageTransition& proto) {
+  VISIT_ENUM(core_transition);
+  VISIT(blocked);
+  VISIT(forward_back);
+  VISIT(from_address_bar);
+  VISIT(home_page);
+}
+
 VISIT_PROTO_FIELDS(const sync_pb::HistorySpecifics::RedirectEntry& proto) {
   VISIT(originator_visit_id);
   VISIT(url);
   VISIT(title);
   VISIT(hidden);
+  VISIT_ENUM(redirect_type);
 }
 
 VISIT_PROTO_FIELDS(const sync_pb::HistorySpecifics& proto) {
@@ -1305,6 +1314,7 @@
   VISIT_REP(tabs);
   VISIT(active_tab_index);
   VISIT(show_as_app);
+  VISIT_REP(tab_groups);
 }
 
 VISIT_PROTO_FIELDS(
@@ -1347,6 +1357,15 @@
   VISIT(height);
 }
 
+VISIT_PROTO_FIELDS(
+    const sync_pb::WorkspaceDeskSpecifics::BrowserAppWindow::TabGroup& proto) {
+  VISIT(first_index);
+  VISIT(last_index);
+  VISIT(title);
+  VISIT_ENUM(color);
+  VISIT(is_collapsed);
+}
+
 }  // namespace syncer
 
 #undef VISIT_
diff --git a/components/sync/protocol/workspace_desk_specifics.proto b/components/sync/protocol/workspace_desk_specifics.proto
index c8e803a9..f7e7bea 100644
--- a/components/sync/protocol/workspace_desk_specifics.proto
+++ b/components/sync/protocol/workspace_desk_specifics.proto
@@ -105,6 +105,26 @@
       optional string title = 2;
     }
 
+    // Structure representing a tab group associated with this window.
+    message TabGroup {
+      // Zero-based index within the browser app tabs of the first tab in the
+      // group.
+      optional int32 first_index = 1;
+
+      // Zero-based Index wotjom the browser app tabs of the last tab in the
+      // group.
+      optional int32 last_index = 2;
+
+      // User readable title of the tab group.
+      optional string title = 3;
+
+      // Color associated with this tab group.
+      optional TabGroupColor color = 4;
+
+      // Indicates whether the tab group is collapsed.
+      optional bool is_collapsed = 5;
+    }
+
     // The tabs in this browser window.
     repeated BrowserAppTab tabs = 1;
 
@@ -113,6 +133,9 @@
 
     // Indicates whether to show as a dedicated app window.
     optional bool show_as_app = 3;
+
+    // Tab groups associated with this window.
+    repeated TabGroup tab_groups = 4;
   }
 
   // A Chrome App window.
@@ -228,4 +251,20 @@
     TEMPLATE = 1;
     SAVE_AND_RECALL = 2;
   }
+
+  // Enumerates the possible colors that a tab group can have.  This mirrors
+  // the enumeration that can be found in
+  // components/tab_groups/tab_group_color.h
+  enum TabGroupColor {
+    UNKNOWN_COLOR = 0;
+    GREY = 1;
+    BLUE = 2;
+    RED = 3;
+    YELLOW = 4;
+    GREEN = 5;
+    PINK = 6;
+    PURPLE = 7;
+    CYAN = 8;
+    ORANGE = 9;
+  }
 }
diff --git a/components/user_notes/browser/user_note_service.cc b/components/user_notes/browser/user_note_service.cc
index 099597e..c6a70fc 100644
--- a/components/user_notes/browser/user_note_service.cc
+++ b/components/user_notes/browser/user_note_service.cc
@@ -175,8 +175,7 @@
   DCHECK(creation_entry_it != creation_map_.end())
       << "Attempted to complete the creation of a note that doesn't exist";
   // TODO(gujen): Call
-  // UserNoteStorage::UpdateNote(entry.model, content, /*is_creation=*/true),
-  // which doesn't exist yet.
+  // UserNoteStorage::UpdateNote(entry.model, content, /*is_creation=*/true).
 
   // TODO(gujen): Make sure to transfer the model from the creation map to the
   // model map in the OnNotesChanged() event sent by the storage layer.
diff --git a/components/user_notes/interfaces/user_note_storage.h b/components/user_notes/interfaces/user_note_storage.h
index 3d265b8..e612a72 100644
--- a/components/user_notes/interfaces/user_note_storage.h
+++ b/components/user_notes/interfaces/user_note_storage.h
@@ -48,17 +48,10 @@
       base::OnceCallback<void(std::vector<std::unique_ptr<UserNote>>)>
           callback) = 0;
 
-  // Saves a brand-new note to disk.
-  virtual void CreateNote(base::UnguessableToken id,
+  // Saves a brand-new note or a modified note to disk.
+  virtual void UpdateNote(const UserNote* model,
                           std::string note_body_text,
-                          UserNoteTarget::TargetType target_type,
-                          std::string original_text,
-                          GURL target_page,
-                          std::string selector) = 0;
-
-  // Saves a modified note to disk.
-  virtual void UpdateNote(base::UnguessableToken id,
-                          std::string note_body_text) = 0;
+                          bool is_creation = false) = 0;
 
   // Deletes a note from disk.
   virtual void DeleteNote(const base::UnguessableToken& guid) = 0;
diff --git a/components/user_notes/storage/BUILD.gn b/components/user_notes/storage/BUILD.gn
index be21eb4..fc00946b 100644
--- a/components/user_notes/storage/BUILD.gn
+++ b/components/user_notes/storage/BUILD.gn
@@ -39,6 +39,8 @@
   deps = [
     ":internal",
     "//base",
+    "//components/user_notes/browser",
+    "//components/user_notes/model:unit_tests",
     "//sql",
     "//sql:test_support",
     "//testing/gtest",
diff --git a/components/user_notes/storage/user_note_database.cc b/components/user_notes/storage/user_note_database.cc
index 1467d67..85bfccc 100644
--- a/components/user_notes/storage/user_note_database.cc
+++ b/components/user_notes/storage/user_note_database.cc
@@ -7,6 +7,7 @@
 #include "base/files/file_util.h"
 #include "sql/error_delegate_util.h"
 #include "sql/meta_table.h"
+#include "sql/statement.h"
 #include "sql/transaction.h"
 
 namespace user_notes {
@@ -86,28 +87,160 @@
   return std::vector<std::unique_ptr<UserNote>>();
 }
 
-void UserNoteDatabase::CreateNote(base::UnguessableToken id,
-                                  std::string note_body_text,
-                                  UserNoteTarget::TargetType target_type,
-                                  std::string original_text,
-                                  GURL target_page,
-                                  std::string selector) {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-
-  // TODO(gayane): Implement.
-}
-
-void UserNoteDatabase::UpdateNote(base::UnguessableToken id,
+void UserNoteDatabase::CreateNote(const UserNote* model,
                                   std::string note_body_text) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
 
-  // TODO(gayane): Implement.
+  if (!EnsureDBInit())
+    return;
+
+  sql::Transaction transaction(&db_);
+  if (!transaction.Begin())
+    return;
+
+  sql::Statement create_note(db_.GetCachedStatement(SQL_FROM_HERE,
+                                                    "INSERT INTO notes("
+                                                    "id,"
+                                                    "creation_date,"
+                                                    "modification_date,"
+                                                    "url,"
+                                                    "origin,"
+                                                    "type)"
+                                                    "VALUES(?,?,?,?,?,?)"));
+
+  if (!create_note.is_valid())
+    return;
+
+  // TODO: possibly the time should be passed to this function, for example for
+  // sync to add notes with past creation date.
+  create_note.BindString(0, model->id().ToString());
+  create_note.BindTime(1, model->metadata().creation_date());
+  create_note.BindTime(2, model->metadata().modification_date());
+  create_note.BindString(3, model->target().target_page().spec());
+  create_note.BindString(
+      4, url::Origin::Create(model->target().target_page()).Serialize());
+  create_note.BindInt(5, model->target().type());
+
+  if (!create_note.Run())
+    return;
+
+  if (model->target().type() == UserNoteTarget::TargetType::kPageText) {
+    sql::Statement notes_text_target(db_.GetCachedStatement(
+        SQL_FROM_HERE,
+        "INSERT INTO notes_text_target(note_id, original_text, selector) "
+        "VALUES(?,?,?)"));
+    if (!notes_text_target.is_valid())
+      return;
+
+    notes_text_target.BindString(0, model->id().ToString());
+    notes_text_target.BindString(1, model->target().original_text());
+    notes_text_target.BindString(2, model->target().selector());
+
+    if (!notes_text_target.Run())
+      return;
+  }
+
+  sql::Statement notes_body(db_.GetCachedStatement(
+      SQL_FROM_HERE,
+      "INSERT INTO notes_body(note_id, type, plain_text) "
+      "VALUES(?,?,?)"));
+  if (!notes_body.is_valid())
+    return;
+
+  notes_body.BindString(0, model->id().ToString());
+  notes_body.BindInt(1, UserNoteBody::BodyType::PLAIN_TEXT);
+  notes_body.BindString(2, note_body_text);
+
+  if (!notes_body.Run())
+    return;
+
+  transaction.Commit();
+}
+
+void UserNoteDatabase::UpdateNote(const UserNote* model,
+                                  std::string note_body_text,
+                                  bool is_creation) {
+  if (is_creation) {
+    CreateNote(model, note_body_text);
+    return;
+  }
+
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+  if (!EnsureDBInit())
+    return;
+
+  sql::Transaction transaction(&db_);
+  if (!transaction.Begin())
+    return;
+
+  // Only the text of the note body can be modified.
+  // TODO(crbug.com/1313967): This will need to be updated if in the future we
+  // wish to support changing the target text.
+  sql::Statement update_notes_body(db_.GetCachedStatement(
+      SQL_FROM_HERE, "UPDATE notes_body SET plain_text = ? WHERE note_id = ?"));
+  if (!update_notes_body.is_valid())
+    return;
+
+  update_notes_body.BindString(0, note_body_text);
+  update_notes_body.BindString(1, model->id().ToString());
+
+  if (!update_notes_body.Run())
+    return;
+
+  sql::Statement update_modification_date(db_.GetCachedStatement(
+      SQL_FROM_HERE, "UPDATE notes SET modification_date = ? WHERE id = ?"));
+  if (!update_modification_date.is_valid())
+    return;
+
+  update_modification_date.BindTime(0, base::Time::Now());
+  update_modification_date.BindString(1, model->id().ToString());
+
+  if (!update_modification_date.Run())
+    return;
+
+  transaction.Commit();
 }
 
 void UserNoteDatabase::DeleteNote(const base::UnguessableToken& id) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
 
-  // TODO(gayane): Implement.
+  if (!EnsureDBInit())
+    return;
+
+  sql::Transaction transaction(&db_);
+  if (!transaction.Begin())
+    return;
+
+  sql::Statement delete_notes_body(db_.GetCachedStatement(
+      SQL_FROM_HERE, "DELETE FROM notes_body WHERE note_id = ?"));
+
+  if (!delete_notes_body.is_valid())
+    return;
+
+  delete_notes_body.BindString(0, id.ToString());
+  if (!delete_notes_body.Run())
+    return;
+
+  sql::Statement delete_notes_text_target(db_.GetCachedStatement(
+      SQL_FROM_HERE, "DELETE FROM notes_text_target WHERE note_id = ?"));
+  if (!delete_notes_text_target.is_valid())
+    return;
+
+  delete_notes_text_target.BindString(0, id.ToString());
+  if (!delete_notes_text_target.Run())
+    return;
+
+  sql::Statement delete_notes(
+      db_.GetCachedStatement(SQL_FROM_HERE, "DELETE FROM notes WHERE id = ?"));
+  if (!delete_notes.is_valid())
+    return;
+
+  delete_notes.BindString(0, id.ToString());
+  if (!delete_notes.Run())
+    return;
+
+  transaction.Commit();
 }
 
 void UserNoteDatabase::DeleteAllForUrl(const GURL& url) {
@@ -128,12 +261,18 @@
   // TODO(gayane): Implement.
 }
 
+bool UserNoteDatabase::EnsureDBInit() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  if (db_.is_open())
+    return true;
+  return Init();
+}
+
 void UserNoteDatabase::DatabaseErrorCallback(int error, sql::Statement* stmt) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
 
-  if (!sql::IsErrorCatastrophic(error)) {
+  if (!sql::IsErrorCatastrophic(error))
     return;
-  }
 
   // Ignore repeated callbacks.
   db_.reset_error_callback();
@@ -149,6 +288,7 @@
   sql::MetaTable meta_table;
   bool has_metatable = meta_table.DoesTableExist(&db_);
   bool has_schema = db_.DoesTableExist("notes");
+
   if (!has_metatable && has_schema) {
     // Existing DB with no meta table. Cannot determine DB version.
     db_.Raze();
diff --git a/components/user_notes/storage/user_note_database.h b/components/user_notes/storage/user_note_database.h
index 7605fe8..75535ca 100644
--- a/components/user_notes/storage/user_note_database.h
+++ b/components/user_notes/storage/user_note_database.h
@@ -37,14 +37,9 @@
   std::vector<std::unique_ptr<UserNote>> GetNotesById(
       std::vector<base::UnguessableToken> ids);
 
-  void CreateNote(base::UnguessableToken id,
+  void UpdateNote(const UserNote* model,
                   std::string note_body_text,
-                  UserNoteTarget::TargetType target_type,
-                  std::string original_text,
-                  GURL target_page,
-                  std::string selector);
-
-  void UpdateNote(base::UnguessableToken id, std::string note_body_text);
+                  bool is_creation);
 
   void DeleteNote(const base::UnguessableToken& id);
 
@@ -55,12 +50,22 @@
   void DeleteAllNotes();
 
  private:
+  FRIEND_TEST_ALL_PREFIXES(UserNoteDatabaseTest, UpdateNote);
+  FRIEND_TEST_ALL_PREFIXES(UserNoteDatabaseTest, CreateNote);
+  FRIEND_TEST_ALL_PREFIXES(UserNoteDatabaseTest, DeleteNote);
+
+  // Initialises internal database if needed.
+  bool EnsureDBInit();
+
   // Called by the database to report errors.
   void DatabaseErrorCallback(int error, sql::Statement* stmt);
 
   // Creates or migrates to the new schema if needed.
   bool InitSchema();
 
+  // Called by UpdateNote() with is_creation=true to create a new note.
+  void CreateNote(const UserNote* model, std::string note_body_text);
+
   bool CreateSchema();
 
   sql::Database db_ GUARDED_BY_CONTEXT(sequence_checker_);
diff --git a/components/user_notes/storage/user_note_database_unittest.cc b/components/user_notes/storage/user_note_database_unittest.cc
index 8b87c9a..e7bdde44 100644
--- a/components/user_notes/storage/user_note_database_unittest.cc
+++ b/components/user_notes/storage/user_note_database_unittest.cc
@@ -7,8 +7,10 @@
 #include "base/files/file_util.h"
 #include "base/files/scoped_temp_dir.h"
 #include "base/path_service.h"
+#include "components/user_notes/model/user_note_model_test_utils.h"
 #include "sql/database.h"
 #include "sql/meta_table.h"
+#include "sql/statement.h"
 #include "sql/test/test_helpers.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
@@ -47,7 +49,7 @@
     sql::Database db;
     EXPECT_TRUE(db.Open(db_file_path()));
 
-    // Should have 4 tables and 5 indexes
+    // Should have 4 tables and 6 indexes
     // tables - user_notes, user_notes_text_target, user_note_body, meta.
     // indexes - 1 implicit index for all 4 tables, url and origin index for
     // `notes` table.
@@ -108,4 +110,88 @@
   }
 }
 
+TEST_F(UserNoteDatabaseTest, CreateNote) {
+  UserNoteDatabase user_note_db(db_dir());
+  EXPECT_TRUE(user_note_db.Init());
+  DCHECK_CALLED_ON_VALID_SEQUENCE(user_note_db.sequence_checker_);
+
+  base::UnguessableToken note_id = base::UnguessableToken::Create();
+  UserNote* user_note =
+      new UserNote(note_id, GetTestUserNoteMetadata(), GetTestUserNoteBody(),
+                   GetTestUserNotePageTarget());
+
+  user_note_db.UpdateNote(user_note, "new test note", /*is_creation=*/true);
+
+  sql::Statement statement(user_note_db.db_.GetCachedStatement(
+      SQL_FROM_HERE, "SELECT plain_text FROM notes_body WHERE note_id = ?"));
+
+  EXPECT_TRUE(statement.is_valid());
+  statement.BindString(0, note_id.ToString());
+  EXPECT_TRUE(statement.Step());
+
+  EXPECT_EQ(1, statement.ColumnCount());
+  EXPECT_EQ("new test note", statement.ColumnString(0));
+  delete user_note;
+}
+
+TEST_F(UserNoteDatabaseTest, UpdateNote) {
+  UserNoteDatabase user_note_db(db_dir());
+  EXPECT_TRUE(user_note_db.Init());
+  DCHECK_CALLED_ON_VALID_SEQUENCE(user_note_db.sequence_checker_);
+
+  base::UnguessableToken note_id = base::UnguessableToken::Create();
+  UserNote* user_note =
+      new UserNote(note_id, GetTestUserNoteMetadata(), GetTestUserNoteBody(),
+                   GetTestUserNotePageTarget());
+
+  user_note_db.UpdateNote(user_note, "new test note", /*is_creation=*/true);
+  user_note_db.UpdateNote(user_note, "edit test note", false);
+
+  sql::Statement statement(user_note_db.db_.GetCachedStatement(
+      SQL_FROM_HERE, "SELECT plain_text FROM notes_body WHERE note_id = ?"));
+
+  EXPECT_TRUE(statement.is_valid());
+  statement.BindString(0, note_id.ToString());
+  EXPECT_TRUE(statement.Step());
+
+  EXPECT_EQ(1, statement.ColumnCount());
+  EXPECT_EQ("edit test note", statement.ColumnString(0));
+  delete user_note;
+}
+
+TEST_F(UserNoteDatabaseTest, DeleteNote) {
+  UserNoteDatabase user_note_db(db_dir());
+  EXPECT_TRUE(user_note_db.Init());
+  DCHECK_CALLED_ON_VALID_SEQUENCE(user_note_db.sequence_checker_);
+
+  base::UnguessableToken note_id = base::UnguessableToken::Create();
+  UserNote* user_note =
+      new UserNote(note_id, GetTestUserNoteMetadata(), GetTestUserNoteBody(),
+                   GetTestUserNotePageTarget());
+
+  user_note_db.UpdateNote(user_note, "new test note", /*is_creation=*/true);
+  user_note_db.DeleteNote(note_id);
+
+  sql::Statement statement_notes_body(user_note_db.db_.GetCachedStatement(
+      SQL_FROM_HERE, "SELECT note_id FROM notes_body WHERE note_id = ?"));
+  EXPECT_TRUE(statement_notes_body.is_valid());
+  statement_notes_body.BindString(0, note_id.ToString());
+  EXPECT_FALSE(statement_notes_body.Step());
+
+  sql::Statement statement_notes(user_note_db.db_.GetCachedStatement(
+      SQL_FROM_HERE, "SELECT id FROM notes WHERE id = ?"));
+  EXPECT_TRUE(statement_notes.is_valid());
+  statement_notes.BindString(0, note_id.ToString());
+  EXPECT_FALSE(statement_notes.Step());
+
+  sql::Statement statement_notes_text_target(
+      user_note_db.db_.GetCachedStatement(
+          SQL_FROM_HERE,
+          "SELECT note_id FROM notes_text_target WHERE note_id = ?"));
+  EXPECT_TRUE(statement_notes_text_target.is_valid());
+  statement_notes_text_target.BindString(0, note_id.ToString());
+  EXPECT_FALSE(statement_notes_text_target.Step());
+  delete user_note;
+}
+
 }  // namespace user_notes
diff --git a/components/user_notes/storage/user_note_storage_impl.cc b/components/user_notes/storage/user_note_storage_impl.cc
index 5afe40d1..afdc176e 100644
--- a/components/user_notes/storage/user_note_storage_impl.cc
+++ b/components/user_notes/storage/user_note_storage_impl.cc
@@ -40,21 +40,11 @@
       .Then(std::move(callback));
 }
 
-void UserNoteStorageImpl::CreateNote(base::UnguessableToken id,
+void UserNoteStorageImpl::UpdateNote(const UserNote* model,
                                      std::string note_body_text,
-                                     UserNoteTarget::TargetType target_type,
-                                     std::string original_text,
-                                     GURL target_page,
-                                     std::string selector) {
-  database_.AsyncCall(&UserNoteDatabase::CreateNote)
-      .WithArgs(id, note_body_text, target_type, original_text, target_page,
-                selector);
-}
-
-void UserNoteStorageImpl::UpdateNote(base::UnguessableToken id,
-                                     std::string note_body_text) {
+                                     bool is_creation) {
   database_.AsyncCall(&UserNoteDatabase::UpdateNote)
-      .WithArgs(id, note_body_text);
+      .WithArgs(model, note_body_text, is_creation);
 }
 
 void UserNoteStorageImpl::DeleteNote(const base::UnguessableToken& id) {
diff --git a/components/user_notes/storage/user_note_storage_impl.h b/components/user_notes/storage/user_note_storage_impl.h
index 5bae8686..b12a085 100644
--- a/components/user_notes/storage/user_note_storage_impl.h
+++ b/components/user_notes/storage/user_note_storage_impl.h
@@ -38,15 +38,9 @@
       base::OnceCallback<void(std::vector<std::unique_ptr<UserNote>>)> callback)
       override;
 
-  void CreateNote(base::UnguessableToken id,
+  void UpdateNote(const UserNote* model,
                   std::string note_body_text,
-                  UserNoteTarget::TargetType target_type,
-                  std::string original_text,
-                  GURL target_page,
-                  std::string selector) override;
-
-  void UpdateNote(base::UnguessableToken id,
-                  std::string note_body_text) override;
+                  bool is_creation = false) override;
 
   void DeleteNote(const base::UnguessableToken& id) override;
 
diff --git a/components/viz/service/display/software_renderer_unittest.cc b/components/viz/service/display/software_renderer_unittest.cc
index 1c44d56..521d833e 100644
--- a/components/viz/service/display/software_renderer_unittest.cc
+++ b/components/viz/service/display/software_renderer_unittest.cc
@@ -25,6 +25,7 @@
 #include "components/viz/common/quads/compositor_frame_metadata.h"
 #include "components/viz/common/quads/compositor_render_pass.h"
 #include "components/viz/common/quads/compositor_render_pass_draw_quad.h"
+#include "components/viz/common/quads/debug_border_draw_quad.h"
 #include "components/viz/common/quads/solid_color_draw_quad.h"
 #include "components/viz/common/quads/tile_draw_quad.h"
 #include "components/viz/common/resources/bitmap_allocation.h"
@@ -187,6 +188,78 @@
             output->getColor(inner_size.width() - 1, inner_size.height() - 1));
 }
 
+TEST_F(SoftwareRendererTest, DebugBorderDrawQuad) {
+  gfx::Size rect_size(10, 10);
+  gfx::Size full_size(100, 100);
+  gfx::Rect screen_rect(full_size);
+  gfx::Rect rect_1(rect_size);
+  gfx::Rect rect_2(gfx::Point(1, 1), rect_size);
+  gfx::Rect rect_3(gfx::Point(2, 2), rect_size);
+  gfx::Rect rect_4(gfx::Point(3, 3), rect_size);
+
+  InitializeRenderer(std::make_unique<SoftwareOutputDevice>());
+
+  AggregatedRenderPassId root_render_pass_id{1};
+  auto root_render_pass = std::make_unique<AggregatedRenderPass>();
+  root_render_pass->SetNew(root_render_pass_id, screen_rect, screen_rect,
+                           gfx::Transform());
+  SharedQuadState* shared_quad_state =
+      root_render_pass->CreateAndAppendSharedQuadState();
+  shared_quad_state->SetAll(gfx::Transform(), screen_rect, screen_rect,
+                            gfx::MaskFilterInfo(), absl::nullopt, true, 1.0,
+                            SkBlendMode::kSrcOver, 0);
+
+  auto* quad_1 =
+      root_render_pass->CreateAndAppendDrawQuad<DebugBorderDrawQuad>();
+  quad_1->SetNew(shared_quad_state, rect_1, rect_1, SK_ColorCYAN, false);
+  auto* quad_2 =
+      root_render_pass->CreateAndAppendDrawQuad<DebugBorderDrawQuad>();
+  quad_2->SetNew(shared_quad_state, rect_2, rect_2, SK_ColorMAGENTA, false);
+
+  auto* quad_3 =
+      root_render_pass->CreateAndAppendDrawQuad<DebugBorderDrawQuad>();
+  quad_3->SetNew(shared_quad_state, rect_3, rect_3, SK_ColorYELLOW, false);
+
+  // Test one non-opaque color
+  SkColor semi_transparent_white = SkColorSetARGB(127, 255, 255, 255);
+  auto* quad_4 =
+      root_render_pass->CreateAndAppendDrawQuad<DebugBorderDrawQuad>();
+  quad_4->SetNew(shared_quad_state, rect_4, rect_4, semi_transparent_white,
+                 false);
+
+  AggregatedRenderPassList list;
+  list.push_back(std::move(root_render_pass));
+
+  float device_scale_factor = 1.f;
+  std::unique_ptr<SkBitmap> output =
+      DrawAndCopyOutput(&list, device_scale_factor, full_size);
+  EXPECT_EQ(screen_rect.width(), output->info().width());
+  EXPECT_EQ(screen_rect.height(), output->info().height());
+
+  // Top left corners
+  EXPECT_EQ(SK_ColorCYAN, output->getColor(0, 0));
+  EXPECT_EQ(SK_ColorMAGENTA, output->getColor(1, 1));
+  EXPECT_EQ(SK_ColorYELLOW, output->getColor(2, 2));
+  // The corners end up being more opaque due to the miter, go one to the right
+  EXPECT_EQ(semi_transparent_white, output->getColor(3, 4));
+
+  // Un-drawn pixels as the quads are just outlines
+  EXPECT_EQ(SK_ColorTRANSPARENT, output->getColor(4, 4));
+  EXPECT_EQ(SK_ColorTRANSPARENT,
+            output->getColor(rect_size.width() - 2, rect_size.height() - 2));
+
+  // The bottom rightmost pixel of these quads are not filled because of the
+  // SkPaint::kMiter_Join StrokeJoin, go one pixel to the left
+  EXPECT_EQ(SK_ColorCYAN,
+            output->getColor(rect_size.width() - 1, rect_size.height()));
+  EXPECT_EQ(SK_ColorMAGENTA,
+            output->getColor(rect_size.width(), rect_size.height() + 1));
+  EXPECT_EQ(SK_ColorYELLOW,
+            output->getColor(rect_size.width() + 1, rect_size.height() + 2));
+  EXPECT_EQ(semi_transparent_white,
+            output->getColor(rect_size.width() + 2, rect_size.height() + 3));
+}
+
 TEST_F(SoftwareRendererTest, TileQuad) {
   gfx::Size outer_size(100, 100);
   gfx::Size inner_size(98, 98);
diff --git a/components/webcrypto/algorithms/hmac_unittest.cc b/components/webcrypto/algorithms/hmac_unittest.cc
index 604ee91..227df589d 100644
--- a/components/webcrypto/algorithms/hmac_unittest.cc
+++ b/components/webcrypto/algorithms/hmac_unittest.cc
@@ -258,13 +258,11 @@
 
 TEST_F(WebCryptoHmacTest, ImportKeyJwkKeyOpsSignVerify) {
   blink::WebCryptoKey key;
-  base::DictionaryValue dict;
-  dict.SetString("kty", "oct");
-  dict.SetString("k", "GADWrMRHwQfoNaXU5fZvTg");
-  base::Value* key_ops =
-      dict.SetKey("key_ops", base::Value(base::Value::Type::LIST));
-
-  key_ops->Append("sign");
+  base::Value::Dict dict;
+  dict.Set("kty", "oct");
+  dict.Set("k", "GADWrMRHwQfoNaXU5fZvTg");
+  dict.Set("key_ops", base::Value::List());
+  dict.FindList("key_ops")->Append("sign");
 
   EXPECT_EQ(Status::Success(),
             ImportKeyJwkFromDict(dict,
@@ -274,7 +272,7 @@
 
   EXPECT_EQ(blink::kWebCryptoKeyUsageSign, key.Usages());
 
-  key_ops->Append("verify");
+  dict.FindList("key_ops")->Append("verify");
 
   EXPECT_EQ(Status::Success(),
             ImportKeyJwkFromDict(dict,
@@ -288,17 +286,17 @@
 // Test 'use' inconsistent with 'key_ops'.
 TEST_F(WebCryptoHmacTest, ImportKeyJwkUseInconsisteWithKeyOps) {
   blink::WebCryptoKey key;
-  base::DictionaryValue dict;
-  dict.SetString("kty", "oct");
-  dict.SetString("k", "GADWrMRHwQfoNaXU5fZvTg");
-  dict.SetString("alg", "HS256");
-  dict.SetString("use", "sig");
+  base::Value::Dict dict;
+  dict.Set("kty", "oct");
+  dict.Set("k", "GADWrMRHwQfoNaXU5fZvTg");
+  dict.Set("alg", "HS256");
+  dict.Set("use", "sig");
 
-  base::ListValue key_ops;
+  base::Value::List key_ops;
   key_ops.Append("sign");
   key_ops.Append("verify");
   key_ops.Append("encrypt");
-  dict.SetKey("key_ops", std::move(key_ops));
+  dict.Set("key_ops", std::move(key_ops));
   EXPECT_EQ(
       Status::ErrorJwkUseAndKeyopsInconsistent(),
       ImportKeyJwkFromDict(
@@ -312,11 +310,11 @@
 // Test JWK composite 'sig' use
 TEST_F(WebCryptoHmacTest, ImportKeyJwkUseSig) {
   blink::WebCryptoKey key;
-  base::DictionaryValue dict;
-  dict.SetString("kty", "oct");
-  dict.SetString("k", "GADWrMRHwQfoNaXU5fZvTg");
+  base::Value::Dict dict;
+  dict.Set("kty", "oct");
+  dict.Set("k", "GADWrMRHwQfoNaXU5fZvTg");
+  dict.Set("use", "sig");
 
-  dict.SetString("use", "sig");
   EXPECT_EQ(
       Status::Success(),
       ImportKeyJwkFromDict(
@@ -340,9 +338,9 @@
   blink::WebCryptoAlgorithm algorithm =
       CreateHmacImportAlgorithmNoLength(blink::kWebCryptoAlgorithmIdSha256);
   blink::WebCryptoKeyUsageMask usages = blink::kWebCryptoKeyUsageVerify;
-  base::DictionaryValue dict;
-  dict.SetString("kty", "oct");
-  dict.SetString("k", "l3nZEgZCeX8XRwJdWyK3rGB8qwjhdY8vOkbIvh4lxTuMao9Y_--hdg");
+  base::Value::Dict dict;
+  dict.Set("kty", "oct");
+  dict.Set("k", "l3nZEgZCeX8XRwJdWyK3rGB8qwjhdY8vOkbIvh4lxTuMao9Y_--hdg");
   std::vector<uint8_t> json_vec = MakeJsonVector(dict);
   EXPECT_EQ(Status::Success(),
             ImportKey(blink::kWebCryptoKeyFormatJwk, json_vec, algorithm,
@@ -360,12 +358,12 @@
   // Consistency rules when JWK value exists: Fail if inconsistency is found.
 
   // Pass: All input values are consistent with the JWK values.
-  dict.DictClear();
-  dict.SetString("kty", "oct");
-  dict.SetString("alg", "HS256");
-  dict.SetString("use", "sig");
-  dict.SetBoolean("ext", false);
-  dict.SetString("k", "l3nZEgZCeX8XRwJdWyK3rGB8qwjhdY8vOkbIvh4lxTuMao9Y_--hdg");
+  dict.clear();
+  dict.Set("kty", "oct");
+  dict.Set("alg", "HS256");
+  dict.Set("use", "sig");
+  dict.Set("ext", false);
+  dict.Set("k", "l3nZEgZCeX8XRwJdWyK3rGB8qwjhdY8vOkbIvh4lxTuMao9Y_--hdg");
   json_vec = MakeJsonVector(dict);
   EXPECT_EQ(Status::Success(),
             ImportKey(blink::kWebCryptoKeyFormatJwk, json_vec, algorithm,
@@ -383,21 +381,20 @@
             ImportKey(blink::kWebCryptoKeyFormatJwk, json_vec, algorithm, false,
                       usages, &key));
   EXPECT_FALSE(key.Extractable());
-  dict.SetBoolean("ext", true);
+  dict.Set("ext", true);
   EXPECT_EQ(Status::Success(),
             ImportKeyJwkFromDict(dict, algorithm, true, usages, &key));
   EXPECT_TRUE(key.Extractable());
   EXPECT_EQ(Status::Success(),
             ImportKeyJwkFromDict(dict, algorithm, false, usages, &key));
   EXPECT_FALSE(key.Extractable());
-  dict.SetBoolean("ext", true);  // restore previous value
 
   // Fail: Input algorithm (AES-CBC) is inconsistent with JWK value
   // (HMAC SHA256).
-  dict.DictClear();
-  dict.SetString("kty", "oct");
-  dict.SetString("alg", "HS256");
-  dict.SetString("k", "l3nZEgZCeX8XRwJdWyK3rGB8qwjhdY8vOkbIvh4lxTuMao9Y_--hdg");
+  dict.clear();
+  dict.Set("kty", "oct");
+  dict.Set("alg", "HS256");
+  dict.Set("k", "l3nZEgZCeX8XRwJdWyK3rGB8qwjhdY8vOkbIvh4lxTuMao9Y_--hdg");
   EXPECT_EQ(Status::ErrorJwkAlgorithmInconsistent(),
             ImportKeyJwkFromDict(
                 dict, CreateAlgorithm(blink::kWebCryptoAlgorithmIdAesCbc),
@@ -417,14 +414,14 @@
                       extractable, usages, &key));
 
   // Pass: JWK alg missing but input algorithm specified: use input value
-  dict.RemoveKey("alg");
+  dict.Remove("alg");
   EXPECT_EQ(Status::Success(),
             ImportKeyJwkFromDict(dict,
                                  CreateHmacImportAlgorithmNoLength(
                                      blink::kWebCryptoAlgorithmIdSha256),
                                  extractable, usages, &key));
   EXPECT_EQ(blink::kWebCryptoAlgorithmIdHmac, algorithm.Id());
-  dict.SetString("alg", "HS256");
+  dict.Set("alg", "HS256");
 
   // Fail: Input usages (encrypt) is not a subset of the JWK value
   // (sign|verify). Moreover "encrypt" is not a valid usage for HMAC.
@@ -461,12 +458,12 @@
   // Import a symmetric key JWK and HMAC-SHA256 sign()
   // Uses the first SHA256 test vector from the HMAC sample set above.
 
-  base::DictionaryValue dict;
-  dict.SetString("kty", "oct");
-  dict.SetString("alg", "HS256");
-  dict.SetString("use", "sig");
-  dict.SetBoolean("ext", false);
-  dict.SetString("k", "l3nZEgZCeX8XRwJdWyK3rGB8qwjhdY8vOkbIvh4lxTuMao9Y_--hdg");
+  base::Value::Dict dict;
+  dict.Set("kty", "oct");
+  dict.Set("alg", "HS256");
+  dict.Set("use", "sig");
+  dict.Set("ext", false);
+  dict.Set("k", "l3nZEgZCeX8XRwJdWyK3rGB8qwjhdY8vOkbIvh4lxTuMao9Y_--hdg");
 
   ASSERT_EQ(Status::Success(),
             ImportKeyJwkFromDict(dict, algorithm, extractable, usages, &key));
@@ -622,9 +619,9 @@
 
 // The same test as above, but using the JWK format.
 TEST_F(WebCryptoHmacTest, ImportJwkKeyTruncation) {
-  base::DictionaryValue dict;
-  dict.SetString("kty", "oct");
-  dict.SetString("k", "sf8");  // 0xB1FF
+  base::Value::Dict dict;
+  dict.Set("kty", "oct");
+  dict.Set("k", "sf8");  // 0xB1FF
 
   blink::WebCryptoKey key;
   EXPECT_EQ(Status::Success(),
diff --git a/content/browser/file_system_access/file_system_access_file_delegate_host_impl.cc b/content/browser/file_system_access/file_system_access_file_delegate_host_impl.cc
index bea0e0f..d7fd849 100644
--- a/content/browser/file_system_access/file_system_access_file_delegate_host_impl.cc
+++ b/content/browser/file_system_access/file_system_access_file_delegate_host_impl.cc
@@ -31,7 +31,7 @@
 
 void ReadOnIOThread(scoped_refptr<storage::FileSystemContext> context,
                     storage::FileSystemURL url,
-                    uint64_t offset,
+                    int64_t offset,
                     scoped_refptr<storage::BigIOBuffer> buffer,
                     scoped_refptr<base::SequencedTaskRunner> reply_runner,
                     base::OnceCallback<void(int)> callback) {
@@ -83,18 +83,28 @@
 
 struct FileSystemAccessFileDelegateHostImpl::WriteState {
   WriteCallback callback;
-  uint64_t bytes_written = 0;
+  int64_t bytes_written = 0;
 };
 
-void FileSystemAccessFileDelegateHostImpl::Read(uint64_t offset,
-                                                uint64_t bytes_to_read,
+void FileSystemAccessFileDelegateHostImpl::Read(int64_t offset,
+                                                int bytes_to_read,
                                                 ReadCallback callback) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
 
+  if (offset < 0) {
+    receiver_.ReportBadMessage("SyncAccesHandle with a negative read offset.");
+    return;
+  }
+  if (bytes_to_read < 0) {
+    receiver_.ReportBadMessage(
+        "SyncAccesHandle trying to read a negative number of bytes.");
+    return;
+  }
+
   // FileStreamReader::Read takes an int. Do not allocate more memory than
   // Chrome will be allowed to contiguously allocate at once.
   int max_bytes_to_read =
-      std::min(base::saturated_cast<int>(bytes_to_read),
+      std::min(bytes_to_read,
                base::saturated_cast<int>(partition_alloc::MaxDirectMapped()));
 
   auto buffer = base::MakeRefCounted<storage::BigIOBuffer>(max_bytes_to_read);
@@ -136,11 +146,16 @@
 }
 
 void FileSystemAccessFileDelegateHostImpl::Write(
-    uint64_t offset,
+    int64_t offset,
     mojo::ScopedDataPipeConsumerHandle data,
     WriteCallback callback) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
 
+  if (offset < 0) {
+    receiver_.ReportBadMessage("SyncAccesHandle with a negative write offset.");
+    return;
+  }
+
   manager()->DoFileSystemOperation(
       FROM_HERE, &storage::FileSystemOperationRunner::WriteStream,
       base::BindRepeating(&FileSystemAccessFileDelegateHostImpl::DidWrite,
@@ -185,10 +200,16 @@
 }
 
 void FileSystemAccessFileDelegateHostImpl::SetLength(
-    uint64_t length,
+    int64_t length,
     SetLengthCallback callback) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
 
+  if (length < 0) {
+    receiver_.ReportBadMessage(
+        "SyncAccesHandle with a negative truncate length.");
+    return;
+  }
+
   manager()->DoFileSystemOperation(
       FROM_HERE, &storage::FileSystemOperationRunner::Truncate,
       std::move(callback), url(), length);
diff --git a/content/browser/file_system_access/file_system_access_file_delegate_host_impl.h b/content/browser/file_system_access/file_system_access_file_delegate_host_impl.h
index b27a22b3..dae96f9 100644
--- a/content/browser/file_system_access/file_system_access_file_delegate_host_impl.h
+++ b/content/browser/file_system_access/file_system_access_file_delegate_host_impl.h
@@ -32,14 +32,12 @@
   ~FileSystemAccessFileDelegateHostImpl() override;
 
   // blink::mojom::FileSystemAccessFileDelegateHost:
-  void Read(uint64_t offset,
-            uint64_t bytes_to_read,
-            ReadCallback callback) override;
-  void Write(uint64_t offset,
+  void Read(int64_t offset, int bytes_to_read, ReadCallback callback) override;
+  void Write(int64_t offset,
              mojo::ScopedDataPipeConsumerHandle data,
              WriteCallback callback) override;
   void GetLength(GetLengthCallback callback) override;
-  void SetLength(uint64_t length, SetLengthCallback callback) override;
+  void SetLength(int64_t length, SetLengthCallback callback) override;
 
  private:
   // State that is kept for the duration of a write operation, to keep track of
diff --git a/content/browser/network_service_browsertest.cc b/content/browser/network_service_browsertest.cc
index 5e1e56d..a9f510d 100644
--- a/content/browser/network_service_browsertest.cc
+++ b/content/browser/network_service_browsertest.cc
@@ -862,9 +862,6 @@
 #endif
   }
 
- protected:
-  bool in_process_network_service_ = false;
-
 #if BUILDFLAG(IS_WIN)
  private:
   base::test::ScopedFeatureList win_network_sandbox_feature_;
diff --git a/content/browser/webui/web_ui_impl.cc b/content/browser/webui/web_ui_impl.cc
index 22f5308..47ef1f2 100644
--- a/content/browser/webui/web_ui_impl.cc
+++ b/content/browser/webui/web_ui_impl.cc
@@ -90,7 +90,7 @@
   remote_->SetProperty(name, value);
 }
 
-void WebUIImpl::Send(const std::string& message, base::Value args) {
+void WebUIImpl::Send(const std::string& message, base::Value::List args) {
   const GURL& source_url = frame_host_->GetLastCommittedURL();
   if (!ChildProcessSecurityPolicyImpl::GetInstance()->HasWebUIBindings(
           frame_host_->GetProcess()->GetID()) ||
@@ -109,7 +109,7 @@
     return;
   }
 
-  ProcessWebUIMessage(source_url, message, base::Value::AsListValue(args));
+  ProcessWebUIMessage(source_url, message, std::move(args));
 }
 
 void WebUIImpl::WebUIRenderFrameCreated(RenderFrameHost* render_frame_host) {
@@ -272,22 +272,24 @@
 
 void WebUIImpl::ProcessWebUIMessage(const GURL& source_url,
                                     const std::string& message,
-                                    const base::ListValue& args) {
+                                    base::Value::List args) {
   if (controller_->OverrideHandleWebUIMessage(source_url, message, args))
     return;
 
   auto callback_pair = message_callbacks_.find(message);
   if (callback_pair != message_callbacks_.end()) {
     // Forward this message and content on.
-    callback_pair->second.Run(args.GetList());
+    callback_pair->second.Run(args);
     return;
   }
 
   // Look up the deprecated callback for this message.
   auto deprecated_callback_pair = deprecated_message_callbacks_.find(message);
   if (deprecated_callback_pair != deprecated_message_callbacks_.end()) {
+    base::Value value(std::move(args));
+    const base::ListValue& list_value = base::Value::AsListValue(value);
     // Forward this message and content on.
-    deprecated_callback_pair->second.Run(&args);
+    deprecated_callback_pair->second.Run(&list_value);
     return;
   }
 
diff --git a/content/browser/webui/web_ui_impl.h b/content/browser/webui/web_ui_impl.h
index d027b61..58a3bb49 100644
--- a/content/browser/webui/web_ui_impl.h
+++ b/content/browser/webui/web_ui_impl.h
@@ -80,7 +80,7 @@
       const DeprecatedMessageCallback& callback) override;
   void ProcessWebUIMessage(const GURL& source_url,
                            const std::string& message,
-                           const base::ListValue& args) override;
+                           base::Value::List args) override;
   bool CanCallJavascript() override;
   void CallJavascriptFunctionUnsafe(const std::string& function_name) override;
   void CallJavascriptFunctionUnsafe(const std::string& function_name,
@@ -116,7 +116,7 @@
   friend class WebUIMainFrameObserver;
 
   // mojom::WebUIHost
-  void Send(const std::string& message, base::Value args) override;
+  void Send(const std::string& message, base::Value::List args) override;
 
   // Execute a string of raw JavaScript on the page.
   void ExecuteJavascript(const std::u16string& javascript);
diff --git a/content/common/web_ui.mojom b/content/common/web_ui.mojom
index 2d7f0cb..3edbbca 100644
--- a/content/common/web_ui.mojom
+++ b/content/common/web_ui.mojom
@@ -14,7 +14,7 @@
   // Because `args` may be an arbitrarily complex object, we tag this with
   // [UnlimitedSize] to ignore soft message length limits within Mojo.
   [UnlimitedSize]
-  Send(string message, mojo_base.mojom.DeprecatedListValue args);
+  Send(string message, mojo_base.mojom.ListValue args);
 };
 
 // For adding a property to the WebUI binding object, implemented by renderer.
diff --git a/content/public/browser/web_ui.h b/content/public/browser/web_ui.h
index ca541aa..1c7338bd 100644
--- a/content/public/browser/web_ui.h
+++ b/content/public/browser/web_ui.h
@@ -105,7 +105,7 @@
   // then later wants to undo that, or to route it to a different WebUI object.
   virtual void ProcessWebUIMessage(const GURL& source_url,
                                    const std::string& message,
-                                   const base::ListValue& args) = 0;
+                                   base::Value::List args) = 0;
 
   // Returns true if this WebUI can currently call JavaScript.
   virtual bool CanCallJavascript() = 0;
diff --git a/content/public/browser/web_ui_controller.cc b/content/public/browser/web_ui_controller.cc
index 7c7f18cf..d8fa8c3cb 100644
--- a/content/public/browser/web_ui_controller.cc
+++ b/content/public/browser/web_ui_controller.cc
@@ -23,9 +23,10 @@
 
 WebUIController::~WebUIController() = default;
 
-bool WebUIController::OverrideHandleWebUIMessage(const GURL& source_url,
-                                                 const std::string& message,
-                                                 const base::ListValue& args) {
+bool WebUIController::OverrideHandleWebUIMessage(
+    const GURL& source_url,
+    const std::string& message,
+    const base::Value::List& args) {
   return false;
 }
 
diff --git a/content/public/browser/web_ui_controller.h b/content/public/browser/web_ui_controller.h
index 79677967..5db8cfe1 100644
--- a/content/public/browser/web_ui_controller.h
+++ b/content/public/browser/web_ui_controller.h
@@ -10,16 +10,13 @@
 
 #include "base/check.h"
 #include "base/memory/raw_ptr.h"
+#include "base/values.h"
 #include "content/common/content_export.h"
 #include "content/public/browser/page.h"
 #include "content/public/browser/per_web_ui_browser_interface_broker.h"
 
 class GURL;
 
-namespace base {
-class ListValue;
-}
-
 namespace content {
 
 class RenderFrameHost;
@@ -41,7 +38,7 @@
   // Return true if the message handling was overridden.
   virtual bool OverrideHandleWebUIMessage(const GURL& source_url,
                                           const std::string& message,
-                                          const base::ListValue& args);
+                                          const base::Value::List& args);
 
   // Called when a WebUI RenderFrame is created.  This is *not* called for every
   // page load because in some cases a RenderFrame will be reused, for example
diff --git a/content/public/test/test_web_ui.h b/content/public/test/test_web_ui.h
index df62984..2f79095 100644
--- a/content/public/test/test_web_ui.h
+++ b/content/public/test/test_web_ui.h
@@ -58,7 +58,7 @@
       const DeprecatedMessageCallback& callback) override;
   void ProcessWebUIMessage(const GURL& source_url,
                            const std::string& message,
-                           const base::ListValue& args) override {}
+                           base::Value::List args) override {}
   bool CanCallJavascript() override;
   void CallJavascriptFunctionUnsafe(const std::string& function_name) override;
   void CallJavascriptFunctionUnsafe(const std::string& function_name,
diff --git a/content/renderer/web_ui_extension.cc b/content/renderer/web_ui_extension.cc
index ecf5fe1..4a3662816 100644
--- a/content/renderer/web_ui_extension.cc
+++ b/content/renderer/web_ui_extension.cc
@@ -138,19 +138,20 @@
 
   // If they've provided an optional message parameter, convert that into a
   // Value to send to the browser process.
-  std::unique_ptr<base::ListValue> content;
-  if (args->PeekNext().IsEmpty() || args->PeekNext()->IsUndefined()) {
-    content = std::make_unique<base::ListValue>();
-  } else {
+  base::Value::List content;
+  if (!args->PeekNext().IsEmpty() && !args->PeekNext()->IsUndefined()) {
     v8::Local<v8::Object> obj;
     if (!args->GetNext(&obj)) {
       args->ThrowError();
       return;
     }
 
-    content = base::ListValue::From(V8ValueConverter::Create()->FromV8Value(
-        obj, frame->MainWorldScriptContext()));
-    DCHECK(content);
+    std::unique_ptr<base::Value> value =
+        V8ValueConverter::Create()->FromV8Value(
+            obj, frame->MainWorldScriptContext());
+    DCHECK(value->is_list());
+    content = std::move(value->GetList());
+
     // The conversion of |obj| could have triggered arbitrary JavaScript code,
     // so check that the frame is still valid to avoid dereferencing a stale
     // pointer.
diff --git a/content/renderer/web_ui_extension_data.cc b/content/renderer/web_ui_extension_data.cc
index a3455e73..5d1ba71d 100644
--- a/content/renderer/web_ui_extension_data.cc
+++ b/content/renderer/web_ui_extension_data.cc
@@ -40,8 +40,8 @@
 }
 
 void WebUIExtensionData::SendMessage(const std::string& message,
-                                     std::unique_ptr<base::ListValue> args) {
-  remote_->Send(message, std::move(*args));
+                                     base::Value::List args) {
+  remote_->Send(message, std::move(args));
 }
 
 void WebUIExtensionData::SetProperty(const std::string& name,
diff --git a/content/renderer/web_ui_extension_data.h b/content/renderer/web_ui_extension_data.h
index 7d2a936..0ab178b 100644
--- a/content/renderer/web_ui_extension_data.h
+++ b/content/renderer/web_ui_extension_data.h
@@ -8,6 +8,7 @@
 #include <map>
 #include <string>
 
+#include "base/values.h"
 #include "content/common/web_ui.mojom.h"
 #include "content/public/renderer/render_frame_observer.h"
 #include "content/public/renderer/render_frame_observer_tracker.h"
@@ -37,8 +38,7 @@
   // exists in the |variable_map_|.
   std::string GetValue(const std::string& key) const;
 
-  void SendMessage(const std::string& message,
-                   std::unique_ptr<base::ListValue> args);
+  void SendMessage(const std::string& message, base::Value::List args);
 
  private:
   // Use Create() instead.
diff --git a/content/test/content_test_suite.cc b/content/test/content_test_suite.cc
index 528fecf..e247d25 100644
--- a/content/test/content_test_suite.cc
+++ b/content/test/content_test_suite.cc
@@ -63,11 +63,12 @@
   base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
   bool is_child_process = command_line->HasSwitch(switches::kTestChildProcess);
   if (!is_child_process) {
-    gl::GLSurfaceTestSupport::InitializeNoExtensionsOneOff();
+    gl::GLDisplay* display =
+        gl::GLSurfaceTestSupport::InitializeNoExtensionsOneOff();
     auto* gpu_feature_info = gpu::GetTestGpuThreadHolder()->GetGpuFeatureInfo();
     gl::init::SetDisabledExtensionsPlatform(
         gpu_feature_info->disabled_extensions);
-    gl::init::InitializeExtensionSettingsOneOffPlatform();
+    gl::init::InitializeExtensionSettingsOneOffPlatform(display);
   }
 
   RegisterContentWebUIConfigs();
diff --git a/content/test/data/fuzzer_corpus/attribution_simulator/all_fields.textproto b/content/test/data/fuzzer_corpus/attribution_simulator/all_fields.textproto
index 92172b8..701b8fb 100644
--- a/content/test/data/fuzzer_corpus/attribution_simulator/all_fields.textproto
+++ b/content/test/data/fuzzer_corpus/attribution_simulator/all_fields.textproto
@@ -1,5 +1,52 @@
 object_value {
   field {
+    name: "data_clears"
+    value {
+      array_value {
+        value {
+          object_value {
+            field {
+              name: "timestamp"
+              value {
+                string_value {
+                  value: "1643235571"
+                }
+              }
+            }
+            field {
+              name: "delete_begin"
+              value {
+                string_value {
+                  value: "1643235569"
+                }
+              }
+            }
+            field {
+              name: "delete_end"
+              value {
+                string_value {
+                  value: "1643235570"
+                }
+              }
+            }
+            field {
+              name: "origins"
+              value {
+                array_value {
+                  value {
+                    string_value {
+                      value: "https://a.test"
+                    }
+                  }
+                }
+              }
+            }
+          }
+        }
+      }
+    }
+  }
+  field {
     name: "cookies"
     value {
       array_value {
@@ -361,7 +408,7 @@
                           name: "a"
                           value: {
                             number_value {
-                              value: 123
+                              integer_value: 123
                             }
                           }
                         }
diff --git a/content/test/gpu/gpu_tests/fetch_gpu_integration_test_dependencies.py b/content/test/gpu/gpu_tests/fetch_gpu_integration_test_dependencies.py
new file mode 100755
index 0000000..fd71d677
--- /dev/null
+++ b/content/test/gpu/gpu_tests/fetch_gpu_integration_test_dependencies.py
@@ -0,0 +1,24 @@
+#!/usr/bin/env python3
+# Copyright 2022 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import os
+import sys
+
+sys.path.append(os.path.dirname(os.path.dirname(os.path.realpath(__file__))))
+import gpu_path_util  # pylint: disable=wrong-import-position
+
+gpu_path_util.AddDirToPathIfNeeded(gpu_path_util.CATAPULT_DIR, 'devil')
+from devil import devil_env  # pylint: disable=wrong-import-position
+
+
+def main():
+  if len(sys.argv) != 1:
+    print('Usage: {}'.format(sys.argv[0]))
+    return
+  devil_env.config.FetchPath('adb')
+
+
+if __name__ == '__main__':
+  main()
diff --git a/gpu/command_buffer/service/gles2_cmd_decoder_unittest_base.cc b/gpu/command_buffer/service/gles2_cmd_decoder_unittest_base.cc
index ee6bfa52..76450c5 100644
--- a/gpu/command_buffer/service/gles2_cmd_decoder_unittest_base.cc
+++ b/gpu/command_buffer/service/gles2_cmd_decoder_unittest_base.cc
@@ -196,10 +196,10 @@
   DCHECK(normalized_init.extensions.empty() ||
          *normalized_init.extensions.rbegin() == ' ');
   gl::SetGLGetProcAddressProc(gl::MockGLInterface::GetGLProcAddress);
-  gl::GLSurfaceTestSupport::InitializeOneOffWithMockBindings();
+  display_ = gl::GLSurfaceTestSupport::InitializeOneOffWithMockBindings();
 
   gl_ = std::make_unique<StrictMock<MockGLInterface>>();
-  ::gl::MockGLInterface::SetGLInterface(gl_.get());
+  gl::MockGLInterface::SetGLInterface(gl_.get());
 
   SetupMockGLBehaviors();
 
@@ -619,9 +619,9 @@
   decoder_.reset();
   group_->Destroy(mock_decoder_.get(), false);
   command_buffer_service_.reset();
-  ::gl::MockGLInterface::SetGLInterface(nullptr);
+  gl::MockGLInterface::SetGLInterface(nullptr);
   gl_.reset();
-  gl::init::ShutdownGL(false);
+  gl::GLSurfaceTestSupport::ShutdownGL(display_);
 }
 
 void GLES2DecoderTestBase::TearDown() {
@@ -2430,7 +2430,7 @@
 
   gl::init::InitializeStaticGLBindingsImplementation(
       gl::GLImplementationParts(gl::kGLImplementationEGLANGLE), false);
-  gl::init::InitializeGLOneOffPlatformImplementation(
+  display_ = gl::init::InitializeGLOneOffPlatformImplementation(
       /*fallback_to_software_gl=*/false,
       /*disable_gl_drawing=*/false,
       /*init_extensions=*/true,
@@ -2492,7 +2492,7 @@
   decoder_.reset();
   group_ = nullptr;
   command_buffer_service_.reset();
-  gl::init::ShutdownGL(false);
+  gl::init::ShutdownGL(display_, false);
 }
 
 void GLES2DecoderPassthroughTestBase::SetBucketData(uint32_t bucket_id,
diff --git a/gpu/command_buffer/service/gles2_cmd_decoder_unittest_base.h b/gpu/command_buffer/service/gles2_cmd_decoder_unittest_base.h
index 4ce2c3e..7e06c5c 100644
--- a/gpu/command_buffer/service/gles2_cmd_decoder_unittest_base.h
+++ b/gpu/command_buffer/service/gles2_cmd_decoder_unittest_base.h
@@ -42,6 +42,7 @@
 #include "gpu/config/gpu_driver_bug_workarounds.h"
 #include "gpu/config/gpu_preferences.h"
 #include "testing/gtest/include/gtest/gtest.h"
+#include "ui/gl/gl_display.h"
 #include "ui/gl/gl_mock.h"
 #include "ui/gl/gl_surface_stub.h"
 #include "ui/gl/gl_version_info.h"
@@ -702,6 +703,7 @@
   std::unique_ptr<MockGLES2Decoder> mock_decoder_;
   std::unique_ptr<GLES2Decoder> decoder_;
   std::unique_ptr<MemoryTracker> memory_tracker_;
+  gl::GLDisplay* display_ = nullptr;
 
   GLuint client_buffer_id_;
   GLuint client_framebuffer_id_;
@@ -1033,6 +1035,7 @@
   TraceOutputter outputter_;
   std::unique_ptr<GLES2DecoderPassthroughImpl> decoder_;
   scoped_refptr<ContextGroup> group_;
+  gl::GLDisplay* display_ = nullptr;
 };
 
 }  // namespace gles2
diff --git a/gpu/command_buffer/service/gpu_fence_manager_unittest.cc b/gpu/command_buffer/service/gpu_fence_manager_unittest.cc
index 1a77b602..3ccc31f3 100644
--- a/gpu/command_buffer/service/gpu_fence_manager_unittest.cc
+++ b/gpu/command_buffer/service/gpu_fence_manager_unittest.cc
@@ -61,7 +61,7 @@
   void SetupMockEGL(const char* extensions) {
     gl::SetGLGetProcAddressProc(gl::MockEGLInterface::GetGLProcAddress);
     egl_ = std::make_unique<::testing::NiceMock<::gl::MockEGLInterface>>();
-    ::gl::MockEGLInterface::SetEGLInterface(egl_.get());
+    gl::MockEGLInterface::SetEGLInterface(egl_.get());
 
     const EGLDisplay kDummyDisplay = reinterpret_cast<EGLDisplay>(0x1001);
     ON_CALL(*egl_, QueryString(_, EGL_EXTENSIONS))
@@ -73,10 +73,13 @@
 
     gl::ClearBindingsEGL();
     gl::InitializeStaticGLBindingsEGL();
-    gl::GLSurfaceEGL::InitializeOneOffForTesting();
+    display_ = gl::GLSurfaceEGL::InitializeOneOffForTesting();
   }
 
-  void TeardownMockEGL() { egl_.reset(); }
+  void TeardownMockEGL() {
+    gl::GLSurfaceEGL::ShutdownOneOff(display_);
+    egl_.reset();
+  }
 
   void SetupFeatureInfo(const char* gl_extensions,
                         const char* gl_version,
@@ -91,6 +94,7 @@
   std::unique_ptr<GpuFenceManager> manager_;
   std::unique_ptr<MockErrorState> error_state_;
   std::unique_ptr<::testing::NiceMock<::gl::MockEGLInterface>> egl_;
+  gl::GLDisplayEGL* display_ = nullptr;
 };
 
 TEST_F(GpuFenceManagerTest, Basic) {
diff --git a/gpu/command_buffer/service/gpu_service_test.cc b/gpu/command_buffer/service/gpu_service_test.cc
index 85cd16c..7f57e87 100644
--- a/gpu/command_buffer/service/gpu_service_test.cc
+++ b/gpu/command_buffer/service/gpu_service_test.cc
@@ -29,7 +29,7 @@
   testing::Test::SetUp();
 
   gl::SetGLGetProcAddressProc(gl::MockGLInterface::GetGLProcAddress);
-  gl::GLSurfaceTestSupport::InitializeOneOffWithMockBindings();
+  display_ = gl::GLSurfaceTestSupport::InitializeOneOffWithMockBindings();
   gl_ = std::make_unique<::testing::StrictMock<::gl::MockGLInterface>>();
   ::gl::MockGLInterface::SetGLInterface(gl_.get());
 
@@ -51,7 +51,7 @@
   surface_ = nullptr;
   ::gl::MockGLInterface::SetGLInterface(nullptr);
   gl_.reset();
-  gl::init::ShutdownGL(false);
+  gl::GLSurfaceTestSupport::ShutdownGL(display_);
   ran_teardown_ = true;
 
   testing::Test::TearDown();
diff --git a/gpu/command_buffer/service/gpu_service_test.h b/gpu/command_buffer/service/gpu_service_test.h
index fa4d5fa2..1bede07 100644
--- a/gpu/command_buffer/service/gpu_service_test.h
+++ b/gpu/command_buffer/service/gpu_service_test.h
@@ -11,6 +11,7 @@
 #include "base/test/task_environment.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
+#include "ui/gl/gl_display.h"
 #include "ui/gl/gl_mock.h"
 
 namespace gl {
@@ -43,6 +44,7 @@
   scoped_refptr<gl::GLContextStub> context_;
   scoped_refptr<gl::GLSurfaceStub> surface_;
   base::test::SingleThreadTaskEnvironment task_environment_;
+  gl::GLDisplay* display_ = nullptr;
 };
 
 }  // namespace gles2
diff --git a/gpu/command_buffer/service/gr_cache_controller_unittest.cc b/gpu/command_buffer/service/gr_cache_controller_unittest.cc
index 3276a0f..807287c8 100644
--- a/gpu/command_buffer/service/gr_cache_controller_unittest.cc
+++ b/gpu/command_buffer/service/gr_cache_controller_unittest.cc
@@ -26,7 +26,7 @@
 class GrCacheControllerTest : public testing::Test {
  public:
   void SetUp() override {
-    gl::GLSurfaceTestSupport::InitializeOneOffWithStubBindings();
+    display_ = gl::GLSurfaceTestSupport::InitializeOneOffWithStubBindings();
     gpu::GpuDriverBugWorkarounds workarounds;
 
     scoped_refptr<gl::GLShareGroup> share_group = new gl::GLShareGroup();
@@ -53,7 +53,7 @@
     controller_ = nullptr;
     context_state_ = nullptr;
     task_runner_ = nullptr;
-    gl::init::ShutdownGL(false);
+    gl::GLSurfaceTestSupport::ShutdownGL(display_);
   }
 
   GrDirectContext* gr_context() { return context_state_->gr_context(); }
@@ -62,6 +62,7 @@
   scoped_refptr<SharedContextState> context_state_;
   scoped_refptr<base::TestMockTimeTaskRunner> task_runner_;
   std::unique_ptr<GrCacheController> controller_;
+  gl::GLDisplay* display_ = nullptr;
 };
 
 TEST_F(GrCacheControllerTest, PurgeGrCache) {
diff --git a/gpu/command_buffer/service/image_reader_gl_owner_unittest.cc b/gpu/command_buffer/service/image_reader_gl_owner_unittest.cc
index 72445a8..05fb2355 100644
--- a/gpu/command_buffer/service/image_reader_gl_owner_unittest.cc
+++ b/gpu/command_buffer/service/image_reader_gl_owner_unittest.cc
@@ -38,7 +38,7 @@
 
     gl::init::InitializeStaticGLBindingsImplementation(
         gl::GLImplementationParts(gl::kGLImplementationEGLGLES2), false);
-    gl::init::InitializeGLOneOffPlatformImplementation(
+    display_ = gl::init::InitializeGLOneOffPlatformImplementation(
         /*fallback_to_software_gl=*/false,
         /*disable_gl_drawing=*/false,
         /*init_extensions=*/true,
@@ -83,7 +83,7 @@
     context_ = nullptr;
     share_group_ = nullptr;
     surface_ = nullptr;
-    gl::init::ShutdownGL(false);
+    gl::init::ShutdownGL(display_, false);
   }
 
   bool IsImageReaderSupported() const {
@@ -99,6 +99,7 @@
   scoped_refptr<gl::GLShareGroup> share_group_;
   scoped_refptr<gl::GLSurface> surface_;
   base::test::TaskEnvironment task_environment_;
+  gl::GLDisplay* display_ = nullptr;
 };
 
 TEST_F(ImageReaderGLOwnerTest, ImageReaderObjectCreation) {
diff --git a/gpu/command_buffer/service/raster_decoder_unittest.cc b/gpu/command_buffer/service/raster_decoder_unittest.cc
index 4b935793..a71afc4 100644
--- a/gpu/command_buffer/service/raster_decoder_unittest.cc
+++ b/gpu/command_buffer/service/raster_decoder_unittest.cc
@@ -198,7 +198,7 @@
 class RasterDecoderOOPTest : public testing::Test, DecoderClient {
  public:
   void SetUp() override {
-    gl::GLSurfaceTestSupport::InitializeOneOff();
+    display_ = gl::GLSurfaceTestSupport::InitializeOneOff();
     gpu::GpuDriverBugWorkarounds workarounds;
 
     scoped_refptr<gl::GLShareGroup> share_group = new gl::GLShareGroup();
@@ -257,7 +257,7 @@
 
     context_state_.reset();
     context_state_ = nullptr;
-    gl::init::ShutdownGL(false);
+    gl::GLSurfaceTestSupport::ShutdownGL(display_);
   }
 
   RasterDecoderOOPTest() : memory_tracker_(nullptr) {
@@ -374,6 +374,7 @@
   std::unique_ptr<SharedImageFactory> shared_image_factory_;
   SharedImageManager shared_image_manager_;
   gles2::MailboxManagerImpl mailbox_manager_;
+  gl::GLDisplay* display_ = nullptr;
 };
 
 TEST_F(RasterDecoderOOPTest, CopyTexSubImage2DSizeMismatch) {
diff --git a/gpu/command_buffer/service/raster_decoder_unittest_base.cc b/gpu/command_buffer/service/raster_decoder_unittest_base.cc
index 95c5de3..c0da5e4c 100644
--- a/gpu/command_buffer/service/raster_decoder_unittest_base.cc
+++ b/gpu/command_buffer/service/raster_decoder_unittest_base.cc
@@ -99,7 +99,7 @@
 
   // For easier substring/extension matching
   gl::SetGLGetProcAddressProc(gl::MockGLInterface::GetGLProcAddress);
-  gl::GLSurfaceTestSupport::InitializeOneOffWithMockBindings();
+  display_ = gl::GLSurfaceTestSupport::InitializeOneOffWithMockBindings();
 
   gl_ = std::make_unique<StrictMock<MockGLInterface>>();
   ::gl::MockGLInterface::SetGLInterface(gl_.get());
@@ -218,7 +218,7 @@
   shared_context_state_.reset();
   ::gl::MockGLInterface::SetGLInterface(nullptr);
   gl_.reset();
-  gl::init::ShutdownGL(false);
+  gl::GLSurfaceTestSupport::ShutdownGL(display_);
 }
 
 void RasterDecoderTestBase::TearDown() {
diff --git a/gpu/command_buffer/service/raster_decoder_unittest_base.h b/gpu/command_buffer/service/raster_decoder_unittest_base.h
index b8edc537..1625699 100644
--- a/gpu/command_buffer/service/raster_decoder_unittest_base.h
+++ b/gpu/command_buffer/service/raster_decoder_unittest_base.h
@@ -30,6 +30,7 @@
 #include "gpu/config/gpu_driver_bug_workarounds.h"
 #include "gpu/config/gpu_preferences.h"
 #include "testing/gtest/include/gtest/gtest.h"
+#include "ui/gl/gl_display.h"
 #include "ui/gl/gl_mock.h"
 #include "ui/gl/gl_surface_stub.h"
 #include "ui/gl/gl_version_info.h"
@@ -193,6 +194,7 @@
   base::test::SingleThreadTaskEnvironment task_environment_;
   raw_ptr<gles2::MockCopyTextureResourceManager>
       copy_texture_manager_;  // not owned
+  gl::GLDisplay* display_ = nullptr;
 };
 
 class RasterDecoderManualInitTest : public RasterDecoderTestBase {
diff --git a/gpu/command_buffer/service/surface_texture_gl_owner_unittest.cc b/gpu/command_buffer/service/surface_texture_gl_owner_unittest.cc
index fa97fd0..e2de124 100644
--- a/gpu/command_buffer/service/surface_texture_gl_owner_unittest.cc
+++ b/gpu/command_buffer/service/surface_texture_gl_owner_unittest.cc
@@ -39,7 +39,7 @@
   void SetUp() override {
     gl::init::InitializeStaticGLBindingsImplementation(
         gl::GLImplementationParts(gl::kGLImplementationEGLGLES2), false);
-    gl::init::InitializeGLOneOffPlatformImplementation(
+    display_ = gl::init::InitializeGLOneOffPlatformImplementation(
         /*fallback_to_software_gl=*/false,
         /*disable_gl_drawing=*/false,
         /*init_extensions=*/true,
@@ -83,7 +83,7 @@
     context_ = nullptr;
     share_group_ = nullptr;
     surface_ = nullptr;
-    gl::init::ShutdownGL(false);
+    gl::init::ShutdownGL(display_, false);
   }
 
   scoped_refptr<TextureOwner> surface_texture_;
@@ -95,6 +95,7 @@
   scoped_refptr<gl::GLShareGroup> share_group_;
   scoped_refptr<gl::GLSurface> surface_;
   base::test::TaskEnvironment task_environment_;
+  gl::GLDisplay* display_ = nullptr;
 };
 
 TEST_F(SurfaceTextureGLOwnerTest, OwnerReturnsServiceId) {
diff --git a/gpu/command_buffer/tests/gl_test_setup_helper.cc b/gpu/command_buffer/tests/gl_test_setup_helper.cc
index 54e3acf..7b7d8f1 100644
--- a/gpu/command_buffer/tests/gl_test_setup_helper.cc
+++ b/gpu/command_buffer/tests/gl_test_setup_helper.cc
@@ -44,7 +44,7 @@
   ui::OzonePlatform::InitializeForGPU(params);
 #endif  // defined(USE_OZONE)
 
-  gpu::GLTestHelper::InitializeGLDefault();
+  display_ = gpu::GLTestHelper::InitializeGLDefault();
   ::gles2::Initialize();
 }
 
@@ -53,7 +53,7 @@
   // Otherwise the gpu-service tries to access GL during tear-down and causes
   // crashes.
   viz::TestGpuServiceHolder::ResetInstance();
-  gl::init::ShutdownGL(/*due_to_fallback=*/false);
+  gl::init::ShutdownGL(display_, /*due_to_fallback=*/false);
   task_environment_ = nullptr;
 }
 
diff --git a/gpu/command_buffer/tests/gl_test_setup_helper.h b/gpu/command_buffer/tests/gl_test_setup_helper.h
index 07b1d7b4..01d6eba 100644
--- a/gpu/command_buffer/tests/gl_test_setup_helper.h
+++ b/gpu/command_buffer/tests/gl_test_setup_helper.h
@@ -7,6 +7,7 @@
 
 #include "base/test/task_environment.h"
 #include "testing/gtest/include/gtest/gtest.h"
+#include "ui/gl/gl_display.h"
 
 namespace gpu {
 
@@ -26,6 +27,7 @@
 
  private:
   std::unique_ptr<base::test::TaskEnvironment> task_environment_;
+  gl::GLDisplay* display_ = nullptr;
 };
 
 }  // namespace gpu
diff --git a/gpu/command_buffer/tests/gl_test_utils.cc b/gpu/command_buffer/tests/gl_test_utils.cc
index d1f3235..28fc32c 100644
--- a/gpu/command_buffer/tests/gl_test_utils.cc
+++ b/gpu/command_buffer/tests/gl_test_utils.cc
@@ -35,26 +35,27 @@
 const uint8_t GLTestHelper::kCheckClearValue;
 #endif
 
-bool GLTestHelper::InitializeGL(gl::GLImplementation gl_impl) {
+gl::GLDisplay* GLTestHelper::InitializeGL(gl::GLImplementation gl_impl) {
+  gl::GLDisplay* display = nullptr;
   if (gl_impl == gl::GLImplementation::kGLImplementationNone) {
-    if (!gl::init::InitializeGLNoExtensionsOneOff(/*init_bindings=*/true,
-                                                  /*system_device_id=*/0))
-      return false;
+    display = gl::init::InitializeGLNoExtensionsOneOff(/*init_bindings=*/true,
+                                                       /*system_device_id=*/0);
   } else {
     if (!gl::init::InitializeStaticGLBindingsImplementation(
             gl::GLImplementationParts(gl_impl),
             /*fallback_to_software_gl=*/false))
-      return false;
+      return nullptr;
 
-    if (!gl::init::InitializeGLOneOffPlatformImplementation(
-            /*fallback_to_software_gl=*/false,
-            /*disable_gl_drawing=*/false,
-            /*init_extensions=*/false,
-            /*system_device_id=*/0)) {
-      return false;
-    }
+    display = gl::init::InitializeGLOneOffPlatformImplementation(
+        /*fallback_to_software_gl=*/false,
+        /*disable_gl_drawing=*/false,
+        /*init_extensions=*/false,
+        /*system_device_id=*/0);
   }
 
+  if (!display)
+    return nullptr;
+
   gpu::GPUInfo gpu_info;
   gpu::CollectGraphicsInfoForTesting(&gpu_info);
   gpu::GLManager::g_gpu_feature_info = gpu::ComputeGpuFeatureInfo(
@@ -64,10 +65,12 @@
 
   gl::init::SetDisabledExtensionsPlatform(
       gpu::GLManager::g_gpu_feature_info.disabled_extensions);
-  return gl::init::InitializeExtensionSettingsOneOffPlatform();
+  if (!gl::init::InitializeExtensionSettingsOneOffPlatform(display))
+    return nullptr;
+  return display;
 }
 
-bool GLTestHelper::InitializeGLDefault() {
+gl::GLDisplay* GLTestHelper::InitializeGLDefault() {
   return GLTestHelper::InitializeGL(
       gl::GLImplementation::kGLImplementationNone);
 }
@@ -409,8 +412,9 @@
     }
 
     gl_reinitialized_ = true;
-    gl::init::ShutdownGL(false /* due_to_fallback */);
-    if (!GLTestHelper::InitializeGL(new_impl.gl)) {
+    gl::init::ShutdownGL(gl_display_, false /* due_to_fallback */);
+    gl_display_ = GLTestHelper::InitializeGL(new_impl.gl);
+    if (!gl_display_) {
       LOG(INFO) << "Skip test, failed to initialize EGL";
       return false;
     }
@@ -444,8 +448,8 @@
   gl_.Destroy();
 
   if (gl_reinitialized_) {
-    gl::init::ShutdownGL(false /* due_to_fallback */);
-    GLTestHelper::InitializeGLDefault();
+    gl::init::ShutdownGL(gl_display_, false /* due_to_fallback */);
+    gl_display_ = GLTestHelper::InitializeGLDefault();
   }
 
   gl_reinitialized_ = false;
diff --git a/gpu/command_buffer/tests/gl_test_utils.h b/gpu/command_buffer/tests/gl_test_utils.h
index 501f9fd..098f6493 100644
--- a/gpu/command_buffer/tests/gl_test_utils.h
+++ b/gpu/command_buffer/tests/gl_test_utils.h
@@ -14,6 +14,7 @@
 
 #include "build/build_config.h"
 #include "gpu/command_buffer/tests/gl_manager.h"
+#include "ui/gl/gl_display.h"
 #include "ui/gl/gl_implementation.h"
 
 namespace gl {
@@ -26,8 +27,8 @@
  public:
   static const uint8_t kCheckClearValue = 123u;
 
-  static bool InitializeGL(gl::GLImplementation gl_impl);
-  static bool InitializeGLDefault();
+  static gl::GLDisplay* InitializeGL(gl::GLImplementation gl_impl);
+  static gl::GLDisplay* InitializeGLDefault();
 
   static bool HasExtension(const char* extension);
   static bool CheckGLError(const char* msg, int line);
@@ -140,6 +141,7 @@
   gl::GLWindowSystemBindingInfo window_system_binding_info_;
   gfx::ExtensionSet egl_extensions_;
   gfx::ExtensionSet gl_extensions_;
+  gl::GLDisplay* gl_display_ = nullptr;
 };
 
 }  // namespace gpu
diff --git a/gpu/config/gpu_info_collector.cc b/gpu/config/gpu_info_collector.cc
index b0c51a1..56406bb 100644
--- a/gpu/config/gpu_info_collector.cc
+++ b/gpu/config/gpu_info_collector.cc
@@ -289,9 +289,10 @@
   if (CollectGraphicsDeviceInfoFromCommandLine(command_line, gpu_info))
     return true;
 
+  // We can't check if passthrough is supported yet because GL may not be
+  // initialized.
   gpu_info->passthrough_cmd_decoder =
-      gl::UsePassthroughCommandDecoder(command_line) &&
-      gl::PassthroughCommandDecoderSupported();
+      gl::UsePassthroughCommandDecoder(command_line);
 
   bool fallback_to_software = false;
   absl::optional<gl::GLImplementationParts> implementation =
@@ -337,6 +338,11 @@
   TRACE_EVENT0("startup", "gpu_info_collector::CollectGraphicsInfoGL");
   DCHECK_NE(gl::GetGLImplementation(), gl::kGLImplementationNone);
 
+  // Now that we can check GL extensions, update passthrough support info.
+  if (!gl::PassthroughCommandDecoderSupported()) {
+    gpu_info->passthrough_cmd_decoder = false;
+  }
+
   scoped_refptr<gl::GLSurface> surface(InitializeGLSurface());
   if (!surface.get()) {
     LOG(ERROR) << "Could not create surface for info collection.";
diff --git a/gpu/config/gpu_info_collector_unittest.cc b/gpu/config/gpu_info_collector_unittest.cc
index 5dfcf87..9e1881dd 100644
--- a/gpu/config/gpu_info_collector_unittest.cc
+++ b/gpu/config/gpu_info_collector_unittest.cc
@@ -59,7 +59,7 @@
   void SetUp() override {
     testing::Test::SetUp();
     gl::SetGLGetProcAddressProc(gl::MockGLInterface::GetGLProcAddress);
-    gl::GLSurfaceTestSupport::InitializeOneOffWithMockBindings();
+    display_ = gl::GLSurfaceTestSupport::InitializeOneOffWithMockBindings();
     gl_ = std::make_unique<::testing::StrictMock<::gl::MockGLInterface>>();
     ::gl::MockGLInterface::SetGLInterface(gl_.get());
     switch (GetParam()) {
@@ -184,12 +184,12 @@
   void TearDown() override {
     ::gl::MockGLInterface::SetGLInterface(nullptr);
     gl_.reset();
-    gl::init::ShutdownGL(false);
+    gl::GLSurfaceTestSupport::ShutdownGL(display_);
 
     testing::Test::TearDown();
   }
 
- public:
+ protected:
   // Use StrictMock to make 100% sure we know how GL will be called.
   std::unique_ptr<::testing::StrictMock<::gl::MockGLInterface>> gl_;
   GPUInfo test_values_;
@@ -199,6 +199,8 @@
 
   // Persistent storage is needed for the split extension string.
   std::vector<std::string> split_extensions_;
+
+  gl::GLDisplay* display_ = nullptr;
 };
 
 INSTANTIATE_TEST_SUITE_P(GPUConfig,
diff --git a/gpu/config/gpu_util.cc b/gpu/config/gpu_util.cc
index 33b5b58..df0d47d 100644
--- a/gpu/config/gpu_util.cc
+++ b/gpu/config/gpu_util.cc
@@ -47,8 +47,10 @@
 #include "third_party/abseil-cpp/absl/types/optional.h"
 #include "ui/gfx/extension_set.h"
 #include "ui/gl/buildflags.h"
+#include "ui/gl/gl_display.h"
 #include "ui/gl/gl_implementation.h"
 #include "ui/gl/gl_switches.h"
+#include "ui/gl/gl_utils.h"
 
 #if BUILDFLAG(IS_ANDROID)
 #include "base/no_destructor.h"
@@ -127,7 +129,7 @@
   // synchronization, this is based on Android native fence sync
   // support. If that is unavailable, i.e. on emulator or SwiftShader,
   // don't claim SurfaceControl support.
-  if (!gl::GLSurfaceEGL::GetGLDisplayEGL()->IsAndroidNativeFenceSyncSupported())
+  if (!gl::GetDefaultDisplayEGL()->IsAndroidNativeFenceSyncSupported())
     return kGpuFeatureStatusDisabled;
 
   DCHECK(gfx::SurfaceControl::IsSupported());
@@ -747,10 +749,10 @@
 }
 
 #if BUILDFLAG(IS_ANDROID)
-bool InitializeGLThreadSafe(base::CommandLine* command_line,
-                            const GpuPreferences& gpu_preferences,
-                            GPUInfo* out_gpu_info,
-                            GpuFeatureInfo* out_gpu_feature_info) {
+gl::GLDisplay* InitializeGLThreadSafe(base::CommandLine* command_line,
+                                      const GpuPreferences& gpu_preferences,
+                                      GPUInfo* out_gpu_info,
+                                      GpuFeatureInfo* out_gpu_feature_info) {
   static base::NoDestructor<base::Lock> gl_bindings_initialization_lock;
   base::AutoLock auto_lock(*gl_bindings_initialization_lock);
   DCHECK(command_line);
@@ -761,15 +763,21 @@
   if (gpu_info_cached) {
     // GL bindings have already been initialized in another thread.
     DCHECK_NE(gl::kGLImplementationNone, gl::GetGLImplementation());
-    return true;
+    return gl::GetDefaultDisplayEGL();
   }
+
+  gl::GLDisplay* gl_display = nullptr;
   if (gl::GetGLImplementation() == gl::kGLImplementationNone) {
     // Some tests initialize bindings by themselves.
-    if (!gl::init::InitializeGLNoExtensionsOneOff(/*init_bindings=*/true,
-                                                  /*system_device_id=*/0)) {
+    gl_display =
+        gl::init::InitializeGLNoExtensionsOneOff(/*init_bindings=*/true,
+                                                 /*system_device_id=*/0);
+    if (!gl_display) {
       VLOG(1) << "gl::init::InitializeGLNoExtensionsOneOff failed";
-      return false;
+      return nullptr;
     }
+  } else {
+    gl_display = gl::GetDefaultDisplayEGL();
   }
   CollectContextGraphicsInfo(out_gpu_info);
   *out_gpu_feature_info = ComputeGpuFeatureInfo(*out_gpu_info, gpu_preferences,
@@ -778,13 +786,13 @@
     gl::init::SetDisabledExtensionsPlatform(
         out_gpu_feature_info->disabled_extensions);
   }
-  if (!gl::init::InitializeExtensionSettingsOneOffPlatform()) {
+  if (!gl::init::InitializeExtensionSettingsOneOffPlatform(gl_display)) {
     VLOG(1) << "gl::init::InitializeExtensionSettingsOneOffPlatform failed";
-    return false;
+    return nullptr;
   }
   CacheGPUInfo(*out_gpu_info);
   CacheGpuFeatureInfo(*out_gpu_feature_info);
-  return true;
+  return gl_display;
 }
 #endif  // BUILDFLAG(IS_ANDROID)
 
diff --git a/gpu/config/gpu_util.h b/gpu/config/gpu_util.h
index f72970c..08b18c4 100644
--- a/gpu/config/gpu_util.h
+++ b/gpu/config/gpu_util.h
@@ -8,6 +8,7 @@
 #include "build/build_config.h"
 #include "gpu/config/gpu_feature_info.h"
 #include "gpu/gpu_export.h"
+#include "ui/gl/gl_display.h"
 
 namespace base {
 class CommandLine;
@@ -62,10 +63,11 @@
 // bindings, create a GL context, collects GPUInfo, make blocklist and
 // GPU driver bug workaround decisions. This is intended to be called
 // by Android WebView render thread and in-process GPU thread.
-GPU_EXPORT bool InitializeGLThreadSafe(base::CommandLine* command_line,
-                                       const GpuPreferences& gpu_preferences,
-                                       GPUInfo* out_gpu_info,
-                                       GpuFeatureInfo* out_gpu_feature_info);
+GPU_EXPORT gl::GLDisplay* InitializeGLThreadSafe(
+    base::CommandLine* command_line,
+    const GpuPreferences& gpu_preferences,
+    GPUInfo* out_gpu_info,
+    GpuFeatureInfo* out_gpu_feature_info);
 #endif  // BUILDFLAG(IS_ANDROID)
 
 // Returns whether SwiftShader should be enabled. If true, the proper command
diff --git a/gpu/gles2_conform_support/egl/thread_state.cc b/gpu/gles2_conform_support/egl/thread_state.cc
index 624b9dc0..e7764db 100644
--- a/gpu/gles2_conform_support/egl/thread_state.cc
+++ b/gpu/gles2_conform_support/egl/thread_state.cc
@@ -22,6 +22,7 @@
 #include "gpu/gles2_conform_support/egl/surface.h"
 #include "gpu/gles2_conform_support/egl/test_support.h"
 #include "ui/gl/gl_context.h"
+#include "ui/gl/gl_display.h"
 #include "ui/gl/gl_surface.h"
 #include "ui/gl/gl_switches.h"
 #include "ui/gl/init/gl_factory.h"
@@ -88,8 +89,9 @@
 #if defined(USE_OZONE)
       ui::OzonePlatform::InitializeForGPU(ui::OzonePlatform::InitParams());
 #endif
-      gl::init::InitializeGLNoExtensionsOneOff(/*init_bindings=*/true,
-                                               /*system_device_id=*/0);
+      gl::GLDisplay* display =
+          gl::init::InitializeGLNoExtensionsOneOff(/*init_bindings=*/true,
+                                                   /*system_device_id=*/0);
       gpu::GpuFeatureInfo gpu_feature_info;
       if (!command_line->HasSwitch(switches::kDisableGpuDriverBugWorkarounds)) {
         gpu::GPUInfo gpu_info;
@@ -101,7 +103,7 @@
 
       gl::init::SetDisabledExtensionsPlatform(
           gpu_feature_info.disabled_extensions);
-      gl::init::InitializeExtensionSettingsOneOffPlatform();
+      gl::init::InitializeExtensionSettingsOneOffPlatform(display);
     }
 
     g_egl_default_display = new egl::Display();
diff --git a/gpu/gles2_conform_support/generate_gles2_conform_tests.py b/gpu/gles2_conform_support/generate_gles2_conform_tests.py
index c29407c..6def8004 100755
--- a/gpu/gles2_conform_support/generate_gles2_conform_tests.py
+++ b/gpu/gles2_conform_support/generate_gles2_conform_tests.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
 # Copyright (c) 2013 The Chromium Authors. All rights reserved.
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
@@ -8,8 +8,9 @@
 import os
 import re
 import sys
+import typing
 
-def ReadFileAsLines(filename):
+def ReadFileAsLines(filename: str) -> typing.List[str]:
   """Reads a file, removing blank lines and lines that start with #"""
   with open(filename, "r") as in_file:
     raw_lines = in_file.readlines()
@@ -21,7 +22,7 @@
   return lines
 
 
-def GenerateTests(out_file):
+def GenerateTests(out_file: typing.IO) -> None:
   """Generates gles2_conform_test_autogen.cc"""
 
   tests = ReadFileAsLines(
@@ -43,7 +44,7 @@
       }).encode("utf8"))
 
 
-def main(argv):
+def main(argv: typing.List[str]) -> int:
   """This is the main function."""
 
   if len(argv) >= 1:
diff --git a/gpu/gles2_conform_support/generate_gles2_embedded_data.py b/gpu/gles2_conform_support/generate_gles2_embedded_data.py
index d8eb08f..51ed508 100755
--- a/gpu/gles2_conform_support/generate_gles2_embedded_data.py
+++ b/gpu/gles2_conform_support/generate_gles2_embedded_data.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
 # Copyright (c) 2013 The Chromium Authors. All rights reserved.
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
@@ -9,6 +9,7 @@
 
 import os
 import sys
+import typing
 
 class GenerateEmbeddedFiles(object):
   """generates files to embed the gles2 conform test data in executable"""
@@ -28,7 +29,7 @@
       ".run",
   ])
 
-  def __init__(self, scan_dir, base_dir):
+  def __init__(self, scan_dir: str, base_dir: typing.Optional[str]):
     self.scan_dir = scan_dir
     self.base_dir = base_dir
     self.count = 0;
@@ -59,7 +60,7 @@
       self.files_data_h.close()
       self.files_toc_c.close()
 
-  def AddFiles(self, scan_dir):
+  def AddFiles(self, scan_dir: str) -> None:
     """Scan a folder and embed the contents of files."""
     files_to_embed = os.listdir(scan_dir)
     sub_dirs = []
@@ -103,7 +104,7 @@
       self.AddFiles(sub_dir)
 
 
-def main(argv):
+def main(argv: typing.List[str]) -> int:
   """This is the main function."""
 
   if len(argv) >= 1:
diff --git a/gpu/ipc/common/gpu_memory_buffer_impl_test_template.h b/gpu/ipc/common/gpu_memory_buffer_impl_test_template.h
index c8c6bca..cbbffd6c 100644
--- a/gpu/ipc/common/gpu_memory_buffer_impl_test_template.h
+++ b/gpu/ipc/common/gpu_memory_buffer_impl_test_template.h
@@ -22,6 +22,7 @@
 #include "testing/gtest/include/gtest/gtest.h"
 #include "ui/gfx/buffer_format_util.h"
 #include "ui/gfx/mojom/buffer_types.mojom.h"
+#include "ui/gl/gl_display.h"
 
 #if BUILDFLAG(IS_WIN) || defined(USE_OZONE)
 #include "ui/gl/init/gl_factory.h"
@@ -52,8 +53,10 @@
 
 #if BUILDFLAG(IS_WIN) || defined(USE_OZONE)
   // Overridden from testing::Test:
-  void SetUp() override { gl::GLSurfaceTestSupport::InitializeOneOff(); }
-  void TearDown() override { gl::init::ShutdownGL(false); }
+  void SetUp() override {
+    display_ = gl::GLSurfaceTestSupport::InitializeOneOff();
+  }
+  void TearDown() override { gl::GLSurfaceTestSupport::ShutdownGL(display_); }
 #endif
 
  protected:
@@ -62,6 +65,7 @@
 
  private:
   GpuMemoryBufferSupport gpu_memory_buffer_support_;
+  gl::GLDisplay* display_ = nullptr;
 
   void FreeGpuMemoryBuffer(base::OnceClosure free_callback,
                            bool* destroyed,
diff --git a/gpu/ipc/service/gpu_channel_test_common.cc b/gpu/ipc/service/gpu_channel_test_common.cc
index f2dbc67..0149d19 100644
--- a/gpu/ipc/service/gpu_channel_test_common.cc
+++ b/gpu/ipc/service/gpu_channel_test_common.cc
@@ -86,9 +86,9 @@
           new TestGpuChannelManagerDelegate(scheduler_.get())) {
   // We need GL bindings to actually initialize command buffers.
   if (use_stub_bindings) {
-    gl::GLSurfaceTestSupport::InitializeOneOffWithStubBindings();
+    display_ = gl::GLSurfaceTestSupport::InitializeOneOffWithStubBindings();
   } else {
-    gl::GLSurfaceTestSupport::InitializeOneOff();
+    display_ = gl::GLSurfaceTestSupport::InitializeOneOff();
   }
 
   GpuFeatureInfo feature_info;
@@ -110,7 +110,7 @@
   // Command buffers can post tasks and run GL in destruction so do this first.
   channel_manager_ = nullptr;
   task_environment_.RunUntilIdle();
-  gl::init::ShutdownGL(false);
+  gl::GLSurfaceTestSupport::ShutdownGL(display_);
 }
 
 GpuChannel* GpuChannelTestCommon::CreateChannel(int32_t client_id,
diff --git a/gpu/ipc/service/gpu_channel_test_common.h b/gpu/ipc/service/gpu_channel_test_common.h
index d56372d..338a579 100644
--- a/gpu/ipc/service/gpu_channel_test_common.h
+++ b/gpu/ipc/service/gpu_channel_test_common.h
@@ -16,6 +16,7 @@
 #include "gpu/command_buffer/common/context_result.h"
 #include "gpu/ipc/common/gpu_channel.mojom.h"
 #include "testing/gtest/include/gtest/gtest.h"
+#include "ui/gl/gl_display.h"
 
 namespace base {
 namespace trace_event {
@@ -67,6 +68,7 @@
   std::unique_ptr<Scheduler> scheduler_;
   std::unique_ptr<TestGpuChannelManagerDelegate> channel_manager_delegate_;
   std::unique_ptr<GpuChannelManager> channel_manager_;
+  gl::GLDisplay* display_ = nullptr;
 };
 
 }  // namespace gpu
diff --git a/gpu/ipc/service/gpu_init.cc b/gpu/ipc/service/gpu_init.cc
index 6fe9720e..a6e9a877d 100644
--- a/gpu/ipc/service/gpu_init.cc
+++ b/gpu/ipc/service/gpu_init.cc
@@ -37,7 +37,7 @@
 #include "ui/base/ui_base_features.h"
 #include "ui/gfx/switches.h"
 #include "ui/gl/buildflags.h"
-#include "ui/gl/gl_display_manager.h"
+#include "ui/gl/gl_display.h"
 #include "ui/gl/gl_implementation.h"
 #include "ui/gl/gl_surface.h"
 #include "ui/gl/gl_switches.h"
@@ -271,8 +271,7 @@
 
 #if defined(USE_EGL)
   if (system_device_id != 0) {
-    gl::GLDisplayManagerEGL::GetInstance()->SetGpuPreference(
-        gl::GpuPreference::kDefault, system_device_id);
+    gl::SetGpuPreferenceEGL(gl::GpuPreference::kDefault, system_device_id);
   }
 #endif  // USE_EGL
 #endif  // BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC)
@@ -421,11 +420,13 @@
     return false;
 #else   // !(BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS))
     SaveHardwareGpuInfoAndGpuFeatureInfo();
-    gl::init::ShutdownGL(true);
+    gl::init::ShutdownGL(nullptr, true);
     gl_initialized = false;
 #endif  // BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
   }
 
+  gl::GLDisplay* gl_display = nullptr;
+
   if (!gl_initialized) {
     // Pause watchdog. LoadLibrary in GLBindings may take long time.
     if (watchdog_thread_)
@@ -439,8 +440,9 @@
     if (watchdog_thread_)
       watchdog_thread_->ResumeWatchdog();
     if (gl::GetGLImplementation() != gl::kGLImplementationDisabled) {
-      gl_initialized = gl::init::InitializeGLNoExtensionsOneOff(
+      gl_display = gl::init::InitializeGLNoExtensionsOneOff(
           /*init_bindings*/ false, system_device_id);
+      gl_initialized = !!gl_display;
       if (!gl_initialized) {
         VLOG(1) << "gl::init::InitializeGLNoExtensionsOneOff failed";
         return false;
@@ -514,11 +516,12 @@
         return false;
 #else
         SaveHardwareGpuInfoAndGpuFeatureInfo();
-        gl::init::ShutdownGL(true);
+        gl::init::ShutdownGL(gl_display, true);
         watchdog_thread_ = nullptr;
         watchdog_init.SetGpuWatchdogPtr(nullptr);
-        if (!gl::init::InitializeGLNoExtensionsOneOff(/*init_bindings*/ true,
-                                                      system_device_id)) {
+        gl_display = gl::init::InitializeGLNoExtensionsOneOff(
+            /*init_bindings=*/true, system_device_id);
+        if (!gl_display) {
           VLOG(1)
               << "gl::init::InitializeGLNoExtensionsOneOff with SwiftShader "
               << "failed";
@@ -621,7 +624,7 @@
       gl::init::SetDisabledExtensionsPlatform(
           gpu_feature_info_.disabled_extensions);
     }
-    if (!gl::init::InitializeExtensionSettingsOneOffPlatform()) {
+    if (!gl::init::InitializeExtensionSettingsOneOffPlatform(gl_display)) {
       VLOG(1) << "gl::init::InitializeExtensionSettingsOneOffPlatform failed";
       return false;
     }
@@ -804,11 +807,14 @@
   }
 #endif  // !BUILDFLAG(IS_CHROMECAST)
 
+  gl::GLDisplay* gl_display = nullptr;
+
   gl_use_swiftshader_ = EnableSwiftShaderIfNeeded(
       command_line, gpu_feature_info_,
       gpu_preferences_.disable_software_rasterizer, needs_more_info);
-  if (!gl::init::InitializeGLNoExtensionsOneOff(/*init_bindings=*/true,
-                                                /*system_device_id=*/0)) {
+  gl_display = gl::init::InitializeGLNoExtensionsOneOff(/*init_bindings=*/true,
+                                                        /*system_device_id=*/0);
+  if (!gl_display) {
     VLOG(1) << "gl::init::InitializeGLNoExtensionsOneOff failed";
     return;
   }
@@ -823,9 +829,10 @@
         gpu_preferences_.disable_software_rasterizer, false);
     if (gl_use_swiftshader_) {
       SaveHardwareGpuInfoAndGpuFeatureInfo();
-      gl::init::ShutdownGL(true);
-      if (!gl::init::InitializeGLNoExtensionsOneOff(/*init_bindings=*/true,
-                                                    /*system_device_id=*/0)) {
+      gl::init::ShutdownGL(gl_display, true);
+      gl_display = gl::init::InitializeGLNoExtensionsOneOff(
+          /*init_bindings=*/true, /*system_device_id=*/0);
+      if (!gl_display) {
         VLOG(1) << "gl::init::InitializeGLNoExtensionsOneOff failed "
                 << "with SwiftShader";
         return;
@@ -853,7 +860,7 @@
       gl::init::SetDisabledExtensionsPlatform(
           gpu_feature_info_.disabled_extensions);
     }
-    if (!gl::init::InitializeExtensionSettingsOneOffPlatform()) {
+    if (!gl::init::InitializeExtensionSettingsOneOffPlatform(gl_display)) {
       VLOG(1) << "gl::init::InitializeExtensionSettingsOneOffPlatform failed";
     }
     default_offscreen_surface_ =
@@ -878,9 +885,10 @@
         gpu_preferences_.disable_software_rasterizer, false);
     if (gl_use_swiftshader_) {
       SaveHardwareGpuInfoAndGpuFeatureInfo();
-      gl::init::ShutdownGL(true);
-      if (!gl::init::InitializeGLNoExtensionsOneOff(/*init_bindings=*/true,
-                                                    /*system_device_id=*/0)) {
+      gl::init::ShutdownGL(gl_display, true);
+      gl_display = gl::init::InitializeGLNoExtensionsOneOff(
+          /*init_bindings=*/true, /*system_device_id=*/0);
+      if (!gl_display) {
         VLOG(1) << "gl::init::InitializeGLNoExtensionsOneOff failed "
                 << "with SwiftShader";
         return;
diff --git a/gpu/ipc/service/gpu_memory_buffer_factory_test_template.h b/gpu/ipc/service/gpu_memory_buffer_factory_test_template.h
index 8d4888b8..36663b2 100644
--- a/gpu/ipc/service/gpu_memory_buffer_factory_test_template.h
+++ b/gpu/ipc/service/gpu_memory_buffer_factory_test_template.h
@@ -14,6 +14,7 @@
 #include "gpu/ipc/service/gpu_memory_buffer_factory.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "ui/gfx/buffer_format_util.h"
+#include "ui/gl/gl_display.h"
 
 #if BUILDFLAG(IS_WIN) || defined(USE_OZONE)
 #include "base/command_line.h"
@@ -35,9 +36,9 @@
     DCHECK(base::CommandLine::ForCurrentProcess()->HasSwitch(
         switches::kUseGpuInTests));
 #endif
-    gl::GLSurfaceTestSupport::InitializeOneOff();
+    display_ = gl::GLSurfaceTestSupport::InitializeOneOff();
   }
-  void TearDown() override { gl::init::ShutdownGL(false); }
+  void TearDown() override { gl::GLSurfaceTestSupport::ShutdownGL(display_); }
 #endif  // BUILDFLAG(IS_WIN) || defined(USE_OZONE)
 
  protected:
@@ -45,6 +46,7 @@
       base::test::TaskEnvironment::MainThreadType::UI};
 
   GpuMemoryBufferFactoryType factory_;
+  gl::GLDisplay* display_ = nullptr;
 };
 
 TYPED_TEST_SUITE_P(GpuMemoryBufferFactoryTest);
diff --git a/ios/chrome/app/strings/ios_strings.grd b/ios/chrome/app/strings/ios_strings.grd
index ae50c94..968be8f 100644
--- a/ios/chrome/app/strings/ios_strings.grd
+++ b/ios/chrome/app/strings/ios_strings.grd
@@ -1414,6 +1414,18 @@
       <message name="IDS_IOS_NEW_TAB_RECENT_TABS" desc="The header of the panel containing the list of recently closed tabs and the tabs synced from other devices.[Length: 17em]">
         Recent Tabs
       </message>
+      <message name="IDS_IOS_NTP_FEED_SIGNIN_COMPACT_PROMO_BODY" desc="The text on the body of the new tab page Feed Signin Promo (compact version), explaining that users will content based on users interests if they enable sync. [iOS only]">
+        Sync to get content based on your interests.
+      </message>
+      <message name="IDS_IOS_NTP_FEED_SIGNIN_FULL_PROMO_BODY" desc="The text on the body of the new tab page Feed Signin Promo, explaining that users will see more relevant content if they enable sync. [iOS only]">
+        Sync for the most relevant content based on your interests.
+      </message>
+      <message name="IDS_IOS_NTP_FEED_SIGNIN_PROMO_CONTINUE" desc="In Title Case: Button on the new tab page Feed Signin Promo which allow users to signin. [iOS only]">
+        Continue
+      </message>
+      <message name="IDS_IOS_NTP_FEED_SIGNIN_PROMO_TITLE" desc="In Title Case: Title   of the new tab page Feed Signin Promo. [iOS only]">
+        See Content for You
+      </message>
       <message name="IDS_IOS_OPEN_IN" desc="Label of the button that lets users open a document in another application. [Length: 15em] [iOS only]">
         Open in...
       </message>
diff --git a/ios/chrome/app/strings/ios_strings_grd/IDS_IOS_NTP_FEED_SIGNIN_COMPACT_PROMO_BODY.png.sha1 b/ios/chrome/app/strings/ios_strings_grd/IDS_IOS_NTP_FEED_SIGNIN_COMPACT_PROMO_BODY.png.sha1
new file mode 100644
index 0000000..2b10aa90
--- /dev/null
+++ b/ios/chrome/app/strings/ios_strings_grd/IDS_IOS_NTP_FEED_SIGNIN_COMPACT_PROMO_BODY.png.sha1
@@ -0,0 +1 @@
+9a429adbc2ff98971538dd44e17e56287544e27e
\ No newline at end of file
diff --git a/ios/chrome/app/strings/ios_strings_grd/IDS_IOS_NTP_FEED_SIGNIN_FULL_PROMO_BODY.png.sha1 b/ios/chrome/app/strings/ios_strings_grd/IDS_IOS_NTP_FEED_SIGNIN_FULL_PROMO_BODY.png.sha1
new file mode 100644
index 0000000..8ea98ec0
--- /dev/null
+++ b/ios/chrome/app/strings/ios_strings_grd/IDS_IOS_NTP_FEED_SIGNIN_FULL_PROMO_BODY.png.sha1
@@ -0,0 +1 @@
+8400d9b9dd542e9a6b5f24d6aa2870e09700b6f1
\ No newline at end of file
diff --git a/ios/chrome/app/strings/ios_strings_grd/IDS_IOS_NTP_FEED_SIGNIN_PROMO_CONTINUE.png.sha1 b/ios/chrome/app/strings/ios_strings_grd/IDS_IOS_NTP_FEED_SIGNIN_PROMO_CONTINUE.png.sha1
new file mode 100644
index 0000000..8ea98ec0
--- /dev/null
+++ b/ios/chrome/app/strings/ios_strings_grd/IDS_IOS_NTP_FEED_SIGNIN_PROMO_CONTINUE.png.sha1
@@ -0,0 +1 @@
+8400d9b9dd542e9a6b5f24d6aa2870e09700b6f1
\ No newline at end of file
diff --git a/ios/chrome/app/strings/ios_strings_grd/IDS_IOS_NTP_FEED_SIGNIN_PROMO_TITLE.png.sha1 b/ios/chrome/app/strings/ios_strings_grd/IDS_IOS_NTP_FEED_SIGNIN_PROMO_TITLE.png.sha1
new file mode 100644
index 0000000..2b10aa90
--- /dev/null
+++ b/ios/chrome/app/strings/ios_strings_grd/IDS_IOS_NTP_FEED_SIGNIN_PROMO_TITLE.png.sha1
@@ -0,0 +1 @@
+9a429adbc2ff98971538dd44e17e56287544e27e
\ No newline at end of file
diff --git a/ios/chrome/browser/ui/content_suggestions/content_suggestions_feature.mm b/ios/chrome/browser/ui/content_suggestions/content_suggestions_feature.mm
index 5cb2131..572866ac 100644
--- a/ios/chrome/browser/ui/content_suggestions/content_suggestions_feature.mm
+++ b/ios/chrome/browser/ui/content_suggestions/content_suggestions_feature.mm
@@ -21,12 +21,12 @@
 
 // Feature disabled by default.
 const base::Feature kContentSuggestionsHeaderMigration{
-    "ContentSuggestionsHeaderMigration", base::FEATURE_DISABLED_BY_DEFAULT};
+    "ContentSuggestionsHeaderMigration", base::FEATURE_ENABLED_BY_DEFAULT};
 
 // Feature disabled by default.
 const base::Feature kContentSuggestionsUIViewControllerMigration{
     "ContentSuggestionsUIViewControllerMigration",
-    base::FEATURE_DISABLED_BY_DEFAULT};
+    base::FEATURE_ENABLED_BY_DEFAULT};
 
 // Feature disabled by default.
 const base::Feature kContentSuggestionsUIModuleRefresh{
diff --git a/ios/chrome/browser/ui/first_run/ios_first_run_field_trials.h b/ios/chrome/browser/ui/first_run/ios_first_run_field_trials.h
index c9a3cd78..d4a5891 100644
--- a/ios/chrome/browser/ui/first_run/ios_first_run_field_trials.h
+++ b/ios/chrome/browser/ui/first_run/ios_first_run_field_trials.h
@@ -52,7 +52,7 @@
       const base::FieldTrial::EntropyProvider& low_entropy_provider);
 
   // Adds a new FieldTrial group of |name| with a probability of |percentage|.
-  // |variation| defines a server-side variation configuration.
+  // |variation| defines a server-side variation configuration (non-trigger).
   void AddGroup(const std::string& name,
                 variations::VariationID variation,
                 base::FieldTrial::Probability percentage);
diff --git a/ios/chrome/browser/ui/snackbar/snackbar_coordinator.h b/ios/chrome/browser/ui/snackbar/snackbar_coordinator.h
index 7292640..c4f94cc 100644
--- a/ios/chrome/browser/ui/snackbar/snackbar_coordinator.h
+++ b/ios/chrome/browser/ui/snackbar/snackbar_coordinator.h
@@ -13,7 +13,7 @@
 // Coordinator that handles commands to show snackbars.
 @interface SnackbarCoordinator : ChromeCoordinator
 
-// Initializer for a coordinator for |request|.
+// Initializer for a coordinator for `request`.
 - (instancetype)initWithBaseViewController:(UIViewController*)viewController
                                    browser:(Browser*)browser
                                   delegate:
diff --git a/media/gpu/android/codec_image_unittest.cc b/media/gpu/android/codec_image_unittest.cc
index 70dd296..e55f6858 100644
--- a/media/gpu/android/codec_image_unittest.cc
+++ b/media/gpu/android/codec_image_unittest.cc
@@ -52,7 +52,7 @@
 
     gl::init::InitializeStaticGLBindingsImplementation(
         gl::GLImplementationParts(gl::kGLImplementationEGLGLES2), false);
-    gl::init::InitializeGLOneOffPlatformImplementation(
+    display_ = gl::init::InitializeGLOneOffPlatformImplementation(
         /*fallback_to_software_gl=*/false,
         /*disable_gl_drawing=*/false,
         /*init_extensions=*/false,
@@ -83,7 +83,7 @@
     context_ = nullptr;
     share_group_ = nullptr;
     surface_ = nullptr;
-    gl::init::ShutdownGL(false);
+    gl::init::ShutdownGL(display_, false);
     wrapper_->TakeCodecSurfacePair();
   }
 
@@ -121,6 +121,7 @@
   scoped_refptr<gl::GLShareGroup> share_group_;
   scoped_refptr<gl::GLSurface> surface_;
   GLuint texture_id_ = 0;
+  gl::GLDisplay* display_ = nullptr;
 
   class PromotionHintReceiver {
    public:
diff --git a/media/gpu/windows/d3d11_texture_wrapper_unittest.cc b/media/gpu/windows/d3d11_texture_wrapper_unittest.cc
index 3a182d0..313de844 100644
--- a/media/gpu/windows/d3d11_texture_wrapper_unittest.cc
+++ b/media/gpu/windows/d3d11_texture_wrapper_unittest.cc
@@ -44,7 +44,7 @@
 
     task_runner_ = task_environment_.GetMainThreadTaskRunner();
 
-    gl::GLSurfaceTestSupport::InitializeOneOffImplementation(
+    display_ = gl::GLSurfaceTestSupport::InitializeOneOffImplementation(
         gl::GLImplementationParts(gl::ANGLEImplementation::kD3D11), false);
     surface_ = gl::init::CreateOffscreenGLSurface(gfx::Size());
     share_group_ = new gl::GLShareGroup();
@@ -66,7 +66,7 @@
     context_ = nullptr;
     share_group_ = nullptr;
     surface_ = nullptr;
-    gl::init::ShutdownGL(false);
+    gl::GLSurfaceTestSupport::ShutdownGL(display_);
   }
 
   base::test::TaskEnvironment task_environment_;
@@ -83,6 +83,8 @@
   // a wrapper.
   scoped_refptr<FakeCommandBufferHelper> fake_command_buffer_helper_;
   GetCommandBufferHelperCB get_helper_cb_;
+
+  gl::GLDisplay* display_ = nullptr;
 };
 
 TEST_F(D3D11TextureWrapperUnittest, NV12InitSucceeds) {
diff --git a/media/renderers/paint_canvas_video_renderer_unittest.cc b/media/renderers/paint_canvas_video_renderer_unittest.cc
index 98363ec..fa81b71 100644
--- a/media/renderers/paint_canvas_video_renderer_unittest.cc
+++ b/media/renderers/paint_canvas_video_renderer_unittest.cc
@@ -1047,7 +1047,7 @@
   using GetColorCallback = base::RepeatingCallback<SkColor(int, int)>;
 
   void SetUp() override {
-    gl::GLSurfaceTestSupport::InitializeOneOff();
+    display_ = gl::GLSurfaceTestSupport::InitializeOneOff();
     enable_pixels_.emplace();
     media_context_ = base::MakeRefCounted<viz::TestInProcessContextProvider>(
         viz::TestContextType::kGpuRaster, /*support_locking=*/false);
@@ -1074,7 +1074,7 @@
     media_context_.reset();
     enable_pixels_.reset();
     viz::TestGpuServiceHolder::ResetInstance();
-    gl::GLSurfaceTestSupport::ShutdownGL();
+    gl::GLSurfaceTestSupport::ShutdownGL(display_);
   }
 
   // Uses CopyVideoFrameTexturesToGLTexture to copy |frame| into a GL texture,
@@ -1237,6 +1237,7 @@
   PaintCanvasVideoRenderer renderer_;
   scoped_refptr<VideoFrame> cropped_frame_;
   base::test::TaskEnvironment task_environment_;
+  gl::GLDisplay* display_ = nullptr;
 };
 
 TEST_F(PaintCanvasVideoRendererWithGLTest, CopyVideoFrameYUVDataToGLTexture) {
diff --git a/net/cert/internal/signature_algorithm.cc b/net/cert/internal/signature_algorithm.cc
index c579ea6..b733b8e 100644
--- a/net/cert/internal/signature_algorithm.cc
+++ b/net/cert/internal/signature_algorithm.cc
@@ -161,43 +161,6 @@
 const uint8_t kOidMgf1[] = {0x2a, 0x86, 0x48, 0x86, 0xf7,
                             0x0d, 0x01, 0x01, 0x08};
 
-// RFC 5280 section 4.1.1.2 defines signatureAlgorithm as:
-//
-//     AlgorithmIdentifier  ::=  SEQUENCE  {
-//          algorithm               OBJECT IDENTIFIER,
-//          parameters              ANY DEFINED BY algorithm OPTIONAL  }
-[[nodiscard]] bool ParseAlgorithmIdentifier(const der::Input& input,
-                                            der::Input* algorithm,
-                                            der::Input* parameters) {
-  der::Parser parser(input);
-
-  der::Parser algorithm_identifier_parser;
-  if (!parser.ReadSequence(&algorithm_identifier_parser))
-    return false;
-
-  // There shouldn't be anything after the sequence. This is by definition,
-  // as the input to this function is expected to be a single
-  // AlgorithmIdentifier.
-  if (parser.HasMore())
-    return false;
-
-  if (!algorithm_identifier_parser.ReadTag(der::kOid, algorithm))
-    return false;
-
-  // Read the optional parameters to a der::Input. The parameters can be at
-  // most one TLV (for instance NULL or a sequence).
-  //
-  // Note that nothing is allowed after the single optional "parameters" TLV.
-  // This is because RFC 5912's notation for AlgorithmIdentifier doesn't
-  // explicitly list an extension point after "parameters".
-  *parameters = der::Input();
-  if (algorithm_identifier_parser.HasMore() &&
-      !algorithm_identifier_parser.ReadRawTLV(parameters)) {
-    return false;
-  }
-  return !algorithm_identifier_parser.HasMore();
-}
-
 // Returns true if |input| is empty.
 [[nodiscard]] bool IsEmpty(const der::Input& input) {
   return input.Length() == 0;
@@ -456,6 +419,38 @@
 
 }  // namespace
 
+[[nodiscard]] bool ParseAlgorithmIdentifier(const der::Input& input,
+                                            der::Input* algorithm,
+                                            der::Input* parameters) {
+  der::Parser parser(input);
+
+  der::Parser algorithm_identifier_parser;
+  if (!parser.ReadSequence(&algorithm_identifier_parser))
+    return false;
+
+  // There shouldn't be anything after the sequence. This is by definition,
+  // as the input to this function is expected to be a single
+  // AlgorithmIdentifier.
+  if (parser.HasMore())
+    return false;
+
+  if (!algorithm_identifier_parser.ReadTag(der::kOid, algorithm))
+    return false;
+
+  // Read the optional parameters to a der::Input. The parameters can be at
+  // most one TLV (for instance NULL or a sequence).
+  //
+  // Note that nothing is allowed after the single optional "parameters" TLV.
+  // This is because RFC 5912's notation for AlgorithmIdentifier doesn't
+  // explicitly list an extension point after "parameters".
+  *parameters = der::Input();
+  if (algorithm_identifier_parser.HasMore() &&
+      !algorithm_identifier_parser.ReadRawTLV(parameters)) {
+    return false;
+  }
+  return !algorithm_identifier_parser.HasMore();
+}
+
 [[nodiscard]] bool ParseHashAlgorithm(const der::Input& input,
                                       DigestAlgorithm* out) {
   CBS cbs;
diff --git a/net/cert/internal/signature_algorithm.h b/net/cert/internal/signature_algorithm.h
index cf5d4cc..6d209ff 100644
--- a/net/cert/internal/signature_algorithm.h
+++ b/net/cert/internal/signature_algorithm.h
@@ -39,6 +39,15 @@
   Dsa,       // DSA
 };
 
+// Parses AlgorithmIdentifier as defined by RFC 5280 section 4.1.1.2:
+//
+//     AlgorithmIdentifier  ::=  SEQUENCE  {
+//          algorithm               OBJECT IDENTIFIER,
+//          parameters              ANY DEFINED BY algorithm OPTIONAL  }
+[[nodiscard]] NET_EXPORT bool ParseAlgorithmIdentifier(const der::Input& input,
+                                                       der::Input* algorithm,
+                                                       der::Input* parameters);
+
 // Parses a HashAlgorithm as defined by RFC 5912:
 //
 //     HashAlgorithm  ::=  AlgorithmIdentifier{DIGEST-ALGORITHM,
diff --git a/net/cookies/cookie_monster.cc b/net/cookies/cookie_monster.cc
index 5f54517..c9b6a7a 100644
--- a/net/cookies/cookie_monster.cc
+++ b/net/cookies/cookie_monster.cc
@@ -2445,9 +2445,19 @@
       cookie_to_convert = *kv_pair.second[0];
     } else {
       for (const auto* cookie : kv_pair.second) {
-        // If the site only has partitioned cookies, we convert the most
-        // recently accessed cookie to unpartitioned and delete the rest.
+        // If the site only has partitioned cookies, we first check if there is
+        // a cookie whose partition key is same-site with the cookie's domain.
+        //
+        // If there are no partitioned cookies whose partition key is same-site
+        // with the cookie's domain, we convert the most recently accessed
+        // cookie to unpartitioned and delete the rest.
         if (cookie->IsPartitioned() && !cookie->PartitionKey()->nonce()) {
+          if (cookie->PartitionKey()->site() ==
+              SchemefulSite(GURL("https://" + cookie->DomainWithoutDot()))) {
+            should_convert_cookie = true;
+            cookie_to_convert = *cookie;
+            break;
+          }
           if (!should_convert_cookie ||
               cookie->LastAccessDate() > cookie_to_convert.LastAccessDate()) {
             should_convert_cookie = true;
diff --git a/net/cookies/cookie_monster_unittest.cc b/net/cookies/cookie_monster_unittest.cc
index 7f238a04..16a14af6 100644
--- a/net/cookies/cookie_monster_unittest.cc
+++ b/net/cookies/cookie_monster_unittest.cc
@@ -5507,6 +5507,43 @@
   EXPECT_EQ(cookies.size(), 1u);
   EXPECT_EQ(cookies[0].Name(), "__Host-F");
   EXPECT_TRUE(cookies[0].IsPartitioned());
+
+  // Set three partitioned cookies:
+  // - The first has a partition key that is same-site with the cookie URL.
+  //
+  // - The second cookie has a partition key that is cross-site with the cookie
+  // URL because it has a different host.
+  //
+  // - The third cookie has a partition key that is cross-site with the cookie
+  // URL because it has a different scheme.
+  //
+  // The first cookie should be kept, even though it is not the most recently
+  // used. This is because we should cookies whose partition key is same-site
+  // with their URL.
+  EXPECT_TRUE(CreateAndSetCookie(
+      cm.get(), https_www_foo_.url(),
+      "__Host-G=0; Secure; Path=/; SameSite=None; Partitioned", options,
+      absl::nullopt, absl::nullopt,
+      CookiePartitionKey::FromURLForTesting(https_www_foo_.url())));
+  EXPECT_TRUE(CreateAndSetCookie(
+      cm.get(), https_www_foo_.url(),
+      "__Host-G=1; Secure; Path=/; SameSite=None; Partitioned", options,
+      absl::nullopt, absl::nullopt,
+      CookiePartitionKey::FromURLForTesting(GURL("https://2.com"))));
+  EXPECT_TRUE(CreateAndSetCookie(
+      cm.get(), https_www_foo_.url(),
+      "__Host-G=2; Secure; Path=/; SameSite=None; Partitioned", options,
+      absl::nullopt, absl::nullopt,
+      CookiePartitionKey::FromURLForTesting((http_www_foo_.url()))));
+
+  cm->ConvertPartitionedCookiesToUnpartitioned(https_www_foo_.url());
+
+  cookies = GetAllCookiesForURL(cm.get(), https_www_foo_.url(),
+                                CookiePartitionKeyCollection::ContainsAll());
+  EXPECT_EQ(cookies.size(), 6u);
+  EXPECT_EQ(cookies[5].Name(), "__Host-G");
+  EXPECT_EQ(cookies[5].Value(), "0");
+  EXPECT_FALSE(cookies[4].IsPartitioned());
 }
 
 // Tests which use this class verify the expiry date clamping behavior when
diff --git a/net/dns/dns_config.cc b/net/dns/dns_config.cc
index 671574b..2d0cea2 100644
--- a/net/dns/dns_config.cc
+++ b/net/dns/dns_config.cc
@@ -6,6 +6,7 @@
 
 #include <utility>
 
+#include "base/numerics/safe_conversions.h"
 #include "base/values.h"
 #include "net/dns/public/dns_over_https_config.h"
 
@@ -84,34 +85,34 @@
 }
 
 base::Value DnsConfig::ToValue() const {
-  base::Value dict(base::Value::Type::DICTIONARY);
+  base::Value::Dict dict;
 
-  base::Value list(base::Value::Type::LIST);
+  base::Value::List nameserver_list;
   for (const auto& nameserver : nameservers)
-    list.Append(nameserver.ToString());
-  dict.SetKey("nameservers", std::move(list));
+    nameserver_list.Append(nameserver.ToString());
+  dict.Set("nameservers", std::move(nameserver_list));
 
-  dict.SetBoolKey("dns_over_tls_active", dns_over_tls_active);
-  dict.SetStringKey("dns_over_tls_hostname", dns_over_tls_hostname);
+  dict.Set("dns_over_tls_active", dns_over_tls_active);
+  dict.Set("dns_over_tls_hostname", dns_over_tls_hostname);
 
-  list = base::Value(base::Value::Type::LIST);
+  base::Value::List suffix_list;
   for (const auto& suffix : search)
-    list.Append(suffix);
-  dict.SetKey("search", std::move(list));
-  dict.SetBoolKey("unhandled_options", unhandled_options);
-  dict.SetBoolKey("append_to_multi_label_name", append_to_multi_label_name);
-  dict.SetIntKey("ndots", ndots);
-  dict.SetDoubleKey("timeout", fallback_period.InSecondsF());
-  dict.SetIntKey("attempts", attempts);
-  dict.SetIntKey("doh_attempts", doh_attempts);
-  dict.SetBoolKey("rotate", rotate);
-  dict.SetBoolKey("use_local_ipv6", use_local_ipv6);
-  dict.SetIntKey("num_hosts", hosts.size());
-  dict.SetKey("doh_config", base::Value(doh_config.ToValue()));
-  dict.SetIntKey("secure_dns_mode", static_cast<int>(secure_dns_mode));
-  dict.SetBoolKey("allow_dns_over_https_upgrade", allow_dns_over_https_upgrade);
+    suffix_list.Append(suffix);
+  dict.Set("search", std::move(suffix_list));
+  dict.Set("unhandled_options", unhandled_options);
+  dict.Set("append_to_multi_label_name", append_to_multi_label_name);
+  dict.Set("ndots", ndots);
+  dict.Set("timeout", fallback_period.InSecondsF());
+  dict.Set("attempts", attempts);
+  dict.Set("doh_attempts", doh_attempts);
+  dict.Set("rotate", rotate);
+  dict.Set("use_local_ipv6", use_local_ipv6);
+  dict.Set("num_hosts", static_cast<int>(hosts.size()));
+  dict.Set("doh_config", doh_config.ToValue());
+  dict.Set("secure_dns_mode", base::strict_cast<int>(secure_dns_mode));
+  dict.Set("allow_dns_over_https_upgrade", allow_dns_over_https_upgrade);
 
-  return dict;
+  return base::Value(std::move(dict));
 }
 
 }  // namespace net
diff --git a/net/dns/dns_transaction.cc b/net/dns/dns_transaction.cc
index 77a5c44a..3a97987 100644
--- a/net/dns/dns_transaction.cc
+++ b/net/dns/dns_transaction.cc
@@ -123,10 +123,10 @@
 }
 
 base::Value NetLogStartParams(const std::string& hostname, uint16_t qtype) {
-  base::Value dict(base::Value::Type::DICTIONARY);
-  dict.SetStringKey("hostname", hostname);
-  dict.SetIntKey("query_type", qtype);
-  return dict;
+  base::Value::Dict dict;
+  dict.Set("hostname", hostname);
+  dict.Set("query_type", qtype);
+  return base::Value(std::move(dict));
 }
 
 // ----------------------------------------------------------------------------
@@ -166,23 +166,23 @@
   // to the NetLog source source of the UDP socket used.  The request must have
   // completed before this is called.
   base::Value NetLogResponseParams(NetLogCaptureMode capture_mode) const {
-    base::Value dict(base::Value::Type::DICTIONARY);
+    base::Value::Dict dict;
 
     if (GetResponse()) {
       DCHECK(GetResponse()->IsValid());
-      dict.SetIntKey("rcode", GetResponse()->rcode());
-      dict.SetIntKey("answer_count", GetResponse()->answer_count());
-      dict.SetIntKey("additional_answer_count",
-                     GetResponse()->additional_answer_count());
+      dict.Set("rcode", GetResponse()->rcode());
+      dict.Set("answer_count", static_cast<int>(GetResponse()->answer_count()));
+      dict.Set("additional_answer_count",
+               static_cast<int>(GetResponse()->additional_answer_count()));
     }
 
-    GetSocketNetLog().source().AddToEventParameters(&dict);
+    GetSocketNetLog().source().AddToEventParameters(dict);
 
     if (capture_mode == NetLogCaptureMode::kEverything) {
-      dict.SetKey("response_buffer", GetRawResponseBufferForLog());
+      dict.Set("response_buffer", GetRawResponseBufferForLog());
     }
 
-    return dict;
+    return base::Value(std::move(dict));
   }
 
   // True if current attempt is pending (waiting for server response).
diff --git a/net/dns/host_cache.cc b/net/dns/host_cache.cc
index 2f70a5eb..dea347e 100644
--- a/net/dns/host_cache.cc
+++ b/net/dns/host_cache.cc
@@ -77,9 +77,9 @@
 const char kHostPortsKey[] = "host_ports";
 
 base::Value IpEndpointToValue(const IPEndPoint& endpoint) {
-  base::Value::DictStorage dictionary;
-  dictionary.emplace(kEndpointAddressKey, endpoint.ToStringWithoutPort());
-  dictionary.emplace(kEndpointPortKey, int{endpoint.port()});
+  base::Value::Dict dictionary;
+  dictionary.Set(kEndpointAddressKey, endpoint.ToStringWithoutPort());
+  dictionary.Set(kEndpointPortKey, endpoint.port());
   return base::Value(std::move(dictionary));
 }
 
@@ -87,8 +87,9 @@
   if (!value.is_dict())
     return absl::nullopt;
 
-  const std::string* ip_str = value.FindStringKey(kEndpointAddressKey);
-  absl::optional<int> port = value.FindIntKey(kEndpointPortKey);
+  const base::Value::Dict& dict = value.GetDict();
+  const std::string* ip_str = dict.FindString(kEndpointAddressKey);
+  absl::optional<int> port = dict.FindInt(kEndpointPortKey);
 
   if (!ip_str || !port ||
       !base::IsValueInRangeForNumericType<uint16_t>(port.value())) {
@@ -104,9 +105,9 @@
 
 base::Value EndpointMetadataPairToValue(
     const std::pair<HttpsRecordPriority, ConnectionEndpointMetadata>& pair) {
-  base::Value::DictStorage dictionary;
-  dictionary.emplace(kEndpointMetadataWeightKey, int{pair.first});
-  dictionary.emplace(kEndpointMetadataValueKey, pair.second.ToValue());
+  base::Value::Dict dictionary;
+  dictionary.Set(kEndpointMetadataWeightKey, pair.first);
+  dictionary.Set(kEndpointMetadataValueKey, pair.second.ToValue());
   return base::Value(std::move(dictionary));
 }
 
@@ -115,8 +116,9 @@
   if (!value.is_dict())
     return absl::nullopt;
 
-  absl::optional<int> priority = value.FindIntKey(kEndpointMetadataWeightKey);
-  const base::Value* metadata_value = value.FindKey(kEndpointMetadataValueKey);
+  const base::Value::Dict& dict = value.GetDict();
+  absl::optional<int> priority = dict.FindInt(kEndpointMetadataWeightKey);
+  const base::Value* metadata_value = dict.Find(kEndpointMetadataValueKey);
 
   if (!priority || !base::IsValueInRangeForNumericType<HttpsRecordPriority>(
                        priority.value())) {
@@ -135,7 +137,7 @@
       std::move(metadata).value());
 }
 
-bool AddressListFromListValue(const base::Value* value,
+bool AddressListFromListValue(const base::Value::List* value,
                               absl::optional<AddressList>* out_list) {
   if (!value) {
     out_list->reset();
@@ -143,7 +145,7 @@
   }
 
   out_list->emplace();
-  for (const auto& it : value->GetListDeprecated()) {
+  for (const auto& it : *value) {
     IPAddress address;
     const std::string* addr_string = it.GetIfString();
     if (!addr_string || !address.AssignFromIPLiteral(*addr_string)) {
@@ -474,7 +476,7 @@
 }
 
 base::Value HostCache::Entry::NetLogParams() const {
-  return GetAsValue(false /* include_staleness */);
+  return base::Value(GetAsValue(false /* include_staleness */));
 }
 
 void HostCache::Entry::MergeAddressesFrom(const HostCache::Entry& source) {
@@ -537,89 +539,85 @@
   legacy_addresses_->AppendDnsAliases(std::move(deduplicated_source_aliases));
 }
 
-base::Value HostCache::Entry::GetAsValue(bool include_staleness) const {
-  base::Value entry_dict(base::Value::Type::DICTIONARY);
+base::Value::Dict HostCache::Entry::GetAsValue(bool include_staleness) const {
+  base::Value::Dict entry_dict;
 
   if (include_staleness) {
     // The kExpirationKey value is using TimeTicks instead of Time used if
     // |include_staleness| is false, so it cannot be used to deserialize.
     // This is ok as it is used only for netlog.
-    entry_dict.SetStringKey(kExpirationKey,
-                            NetLog::TickCountToString(expires()));
-    entry_dict.SetIntKey(kTtlKey, ttl().InMilliseconds());
-    entry_dict.SetIntKey(kNetworkChangesKey, network_changes());
+    entry_dict.Set(kExpirationKey, NetLog::TickCountToString(expires()));
+    entry_dict.Set(kTtlKey, base::saturated_cast<int>(ttl().InMilliseconds()));
+    entry_dict.Set(kNetworkChangesKey, network_changes());
     // The "pinned" status is meaningful only if "network_changes" is also
     // preserved.
     if (pinning())
-      entry_dict.SetBoolKey(kPinnedKey, *pinning());
+      entry_dict.Set(kPinnedKey, *pinning());
   } else {
     // Convert expiration time in TimeTicks to Time for serialization, using a
     // string because base::Value doesn't handle 64-bit integers.
     base::Time expiration_time =
         base::Time::Now() - (base::TimeTicks::Now() - expires());
-    entry_dict.SetStringKey(
-        kExpirationKey,
-        base::NumberToString(expiration_time.ToInternalValue()));
+    entry_dict.Set(kExpirationKey,
+                   base::NumberToString(expiration_time.ToInternalValue()));
   }
 
   if (error() != OK) {
-    entry_dict.SetIntKey(kNetErrorKey, error());
+    entry_dict.Set(kNetErrorKey, error());
   } else {
     if (ip_endpoints_) {
-      base::Value::ListStorage ip_endpoints_list;
+      base::Value::List ip_endpoints_list;
       for (const IPEndPoint& ip_endpoint : ip_endpoints_.value()) {
-        ip_endpoints_list.push_back(IpEndpointToValue(ip_endpoint));
+        ip_endpoints_list.Append(IpEndpointToValue(ip_endpoint));
       }
-      entry_dict.SetKey(kIpEndpointsKey,
-                        base::Value(std::move(ip_endpoints_list)));
+      entry_dict.Set(kIpEndpointsKey, std::move(ip_endpoints_list));
     }
 
     if (endpoint_metadatas_) {
-      base::Value::ListStorage endpoint_metadatas_list;
+      base::Value::List endpoint_metadatas_list;
       for (const auto& endpoint_metadata_pair : endpoint_metadatas_.value()) {
-        endpoint_metadatas_list.push_back(
+        endpoint_metadatas_list.Append(
             EndpointMetadataPairToValue(endpoint_metadata_pair));
       }
-      entry_dict.SetKey(kEndpointMetadatasKey,
-                        base::Value(std::move(endpoint_metadatas_list)));
+      entry_dict.Set(kEndpointMetadatasKey, std::move(endpoint_metadatas_list));
     }
 
     if (aliases()) {
-      base::Value::ListStorage alias_list;
+      base::Value::List alias_list;
       for (const std::string& alias : *aliases()) {
-        alias_list.emplace_back(alias);
+        alias_list.Append(alias);
       }
-      entry_dict.SetKey(kAliasesKey, base::Value(std::move(alias_list)));
+      entry_dict.Set(kAliasesKey, std::move(alias_list));
     }
 
     if (legacy_addresses()) {
       // Append all of the resolved addresses.
-      base::ListValue addresses_value;
+      base::Value::List addresses_value;
       for (const IPEndPoint& address : legacy_addresses().value()) {
         addresses_value.Append(address.ToStringWithoutPort());
       }
-      entry_dict.SetKey(kAddressesKey, std::move(addresses_value));
+      entry_dict.Set(kAddressesKey, std::move(addresses_value));
     }
 
     if (text_records()) {
       // Append all resolved text records.
-      base::ListValue text_list_value;
+      base::Value::List text_list_value;
       for (const std::string& text_record : text_records().value()) {
         text_list_value.Append(text_record);
       }
-      entry_dict.SetKey(kTextRecordsKey, std::move(text_list_value));
+      entry_dict.Set(kTextRecordsKey, std::move(text_list_value));
     }
 
     if (hostnames()) {
       // Append all the resolved hostnames.
-      base::ListValue hostnames_value;
-      base::ListValue host_ports_value;
+      base::Value::List hostnames_value;
+      base::Value::List host_ports_value;
       for (const HostPortPair& hostname : hostnames().value()) {
         hostnames_value.Append(hostname.host());
         host_ports_value.Append(hostname.port());
       }
-      entry_dict.SetKey(kHostnameResultsKey, std::move(hostnames_value));
-      entry_dict.SetKey(kHostPortsKey, std::move(host_ports_value));
+      entry_dict.Set(kHostnameResultsKey, std::move(hostnames_value));
+      entry_dict.Set(kHostPortsKey, std::move(host_ports_value));
     }
   }
 
@@ -854,12 +852,10 @@
     delegate_->ScheduleWrite();
 }
 
-void HostCache::GetList(base::Value* entry_list,
+void HostCache::GetList(base::Value::List& entry_list,
                         bool include_staleness,
                         SerializationType serialization_type) const {
-  DCHECK(entry_list);
-  DCHECK(entry_list->is_list());
-  entry_list->ClearList();
+  entry_list.clear();
 
   for (const auto& pair : entries_) {
     const Key& key = pair.first;
@@ -878,54 +874,54 @@
           base::Value(key.network_isolation_key.ToDebugString());
     }
 
-    auto entry_dict =
-        std::make_unique<base::Value>(entry.GetAsValue(include_staleness));
+    base::Value::Dict entry_dict = entry.GetAsValue(include_staleness);
 
     const auto* host = absl::get_if<url::SchemeHostPort>(&key.host);
     if (host) {
-      entry_dict->SetStringKey(kSchemeKey, host->scheme());
-      entry_dict->SetStringKey(kHostnameKey, host->host());
-      entry_dict->SetIntKey(kPortKey, host->port());
+      entry_dict.Set(kSchemeKey, host->scheme());
+      entry_dict.Set(kHostnameKey, host->host());
+      entry_dict.Set(kPortKey, host->port());
     } else {
-      entry_dict->SetStringKey(kHostnameKey, absl::get<std::string>(key.host));
+      entry_dict.Set(kHostnameKey, absl::get<std::string>(key.host));
     }
 
-    entry_dict->SetIntKey(kDnsQueryTypeKey,
-                          base::strict_cast<int>(key.dns_query_type));
-    entry_dict->SetIntKey(kFlagsKey, key.host_resolver_flags);
-    entry_dict->SetIntKey(kHostResolverSourceKey,
-                          static_cast<int>(key.host_resolver_source));
-    entry_dict->SetKey(kNetworkIsolationKeyKey,
-                       std::move(network_isolation_key_value));
-    entry_dict->SetBoolKey(kSecureKey, static_cast<bool>(key.secure));
+    entry_dict.Set(kDnsQueryTypeKey,
+                   base::strict_cast<int>(key.dns_query_type));
+    entry_dict.Set(kFlagsKey, key.host_resolver_flags);
+    entry_dict.Set(kHostResolverSourceKey,
+                   base::strict_cast<int>(key.host_resolver_source));
+    entry_dict.Set(kNetworkIsolationKeyKey,
+                   std::move(network_isolation_key_value));
+    entry_dict.Set(kSecureKey, key.secure);
 
-    entry_list->Append(std::move(*entry_dict));
+    entry_list.Append(std::move(entry_dict));
   }
 }
 
-bool HostCache::RestoreFromListValue(const base::Value& old_cache) {
+bool HostCache::RestoreFromListValue(const base::Value::List& old_cache) {
   // Reset the restore size to 0.
   restore_size_ = 0;
 
-  for (const auto& entry_dict : old_cache.GetListDeprecated()) {
+  for (const auto& entry : old_cache) {
     // If the cache is already full, don't bother prioritizing what to evict,
     // just stop restoring.
     if (size() == max_entries_)
       break;
 
-    if (!entry_dict.is_dict())
+    if (!entry.is_dict())
       return false;
 
-    const std::string* hostname_ptr = entry_dict.FindStringKey(kHostnameKey);
+    const base::Value::Dict& entry_dict = entry.GetDict();
+    const std::string* hostname_ptr = entry_dict.FindString(kHostnameKey);
     if (!hostname_ptr || !IsValidHostname(*hostname_ptr)) {
       return false;
     }
 
     // Use presence of scheme to determine host type.
-    const std::string* scheme_ptr = entry_dict.FindStringKey(kSchemeKey);
+    const std::string* scheme_ptr = entry_dict.FindString(kSchemeKey);
     absl::variant<url::SchemeHostPort, std::string> host;
     if (scheme_ptr) {
-      absl::optional<int> port = entry_dict.FindIntKey(kPortKey);
+      absl::optional<int> port = entry_dict.FindInt(kPortKey);
       if (!port || !base::IsValueInRangeForNumericType<uint16_t>(port.value()))
         return false;
 
@@ -938,16 +934,15 @@
       host = *hostname_ptr;
     }
 
-    const std::string* expiration_ptr =
-        entry_dict.FindStringKey(kExpirationKey);
-    absl::optional<int> maybe_flags = entry_dict.FindIntKey(kFlagsKey);
+    const std::string* expiration_ptr = entry_dict.FindString(kExpirationKey);
+    absl::optional<int> maybe_flags = entry_dict.FindInt(kFlagsKey);
     if (expiration_ptr == nullptr || !maybe_flags.has_value())
       return false;
     std::string expiration(*expiration_ptr);
     HostResolverFlags flags = maybe_flags.value();
 
     absl::optional<int> maybe_dns_query_type =
-        entry_dict.FindIntKey(kDnsQueryTypeKey);
+        entry_dict.FindInt(kDnsQueryTypeKey);
     if (!maybe_dns_query_type.has_value())
       return false;
     DnsQueryType dns_query_type =
@@ -955,11 +950,11 @@
 
     // HostResolverSource is optional.
     int host_resolver_source =
-        entry_dict.FindIntKey(kHostResolverSourceKey)
-            .value_or(static_cast<int>(HostResolverSource::ANY));
+        entry_dict.FindInt(kHostResolverSourceKey)
+            .value_or(base::strict_cast<int>(HostResolverSource::ANY));
 
     const base::Value* network_isolation_key_value =
-        entry_dict.FindKey(kNetworkIsolationKeyKey);
+        entry_dict.Find(kNetworkIsolationKeyKey);
     NetworkIsolationKey network_isolation_key;
     if (!network_isolation_key_value ||
         network_isolation_key_value->type() == base::Value::Type::STRING ||
@@ -968,31 +963,31 @@
       return false;
     }
 
-    bool secure = entry_dict.FindBoolKey(kSecureKey).value_or(false);
+    bool secure = entry_dict.FindBool(kSecureKey).value_or(false);
 
     int error = OK;
-    const base::Value* ip_endpoints_value = nullptr;
-    const base::Value* endpoint_metadatas_value = nullptr;
-    const base::Value* aliases_value = nullptr;
-    const base::Value* legacy_addresses_value = nullptr;
-    const base::Value* text_records_value = nullptr;
-    const base::Value* hostname_records_value = nullptr;
-    const base::Value* host_ports_value = nullptr;
-    absl::optional<int> maybe_error = entry_dict.FindIntKey(kNetErrorKey);
-    absl::optional<bool> maybe_pinned = entry_dict.FindBoolKey(kPinnedKey);
+    const base::Value::List* ip_endpoints_list = nullptr;
+    const base::Value::List* endpoint_metadatas_list = nullptr;
+    const base::Value::List* aliases_list = nullptr;
+    const base::Value::List* legacy_addresses_list = nullptr;
+    const base::Value::List* text_records_list = nullptr;
+    const base::Value::List* hostname_records_list = nullptr;
+    const base::Value::List* host_ports_list = nullptr;
+    absl::optional<int> maybe_error = entry_dict.FindInt(kNetErrorKey);
+    absl::optional<bool> maybe_pinned = entry_dict.FindBool(kPinnedKey);
     if (maybe_error.has_value()) {
       error = maybe_error.value();
     } else {
-      ip_endpoints_value = entry_dict.FindListKey(kIpEndpointsKey);
-      endpoint_metadatas_value = entry_dict.FindListKey(kEndpointMetadatasKey);
-      aliases_value = entry_dict.FindListKey(kAliasesKey);
-      legacy_addresses_value = entry_dict.FindListKey(kAddressesKey);
-      text_records_value = entry_dict.FindListKey(kTextRecordsKey);
-      hostname_records_value = entry_dict.FindListKey(kHostnameResultsKey);
-      host_ports_value = entry_dict.FindListKey(kHostPortsKey);
+      ip_endpoints_list = entry_dict.FindList(kIpEndpointsKey);
+      endpoint_metadatas_list = entry_dict.FindList(kEndpointMetadatasKey);
+      aliases_list = entry_dict.FindList(kAliasesKey);
+      legacy_addresses_list = entry_dict.FindList(kAddressesKey);
+      text_records_list = entry_dict.FindList(kTextRecordsKey);
+      hostname_records_list = entry_dict.FindList(kHostnameResultsKey);
+      host_ports_list = entry_dict.FindList(kHostPortsKey);
 
-      if ((hostname_records_value == nullptr && host_ports_value != nullptr) ||
-          (hostname_records_value != nullptr && host_ports_value == nullptr)) {
+      if ((hostname_records_list == nullptr && host_ports_list != nullptr) ||
+          (hostname_records_list != nullptr && host_ports_list == nullptr)) {
         return false;
       }
     }
@@ -1006,10 +1001,9 @@
         (base::Time::Now() - base::Time::FromInternalValue(time_internal));
 
     absl::optional<std::vector<IPEndPoint>> ip_endpoints;
-    if (ip_endpoints_value) {
+    if (ip_endpoints_list) {
       ip_endpoints.emplace();
-      for (const base::Value& ip_endpoint_value :
-           ip_endpoints_value->GetListDeprecated()) {
+      for (const base::Value& ip_endpoint_value : *ip_endpoints_list) {
         absl::optional<IPEndPoint> ip_endpoint =
             IpEndpointFromValue(ip_endpoint_value);
         if (!ip_endpoint)
@@ -1021,10 +1015,10 @@
     absl::optional<
         std::multimap<HttpsRecordPriority, ConnectionEndpointMetadata>>
         endpoint_metadatas;
-    if (endpoint_metadatas_value) {
+    if (endpoint_metadatas_list) {
       endpoint_metadatas.emplace();
       for (const base::Value& endpoint_metadata_value :
-           endpoint_metadatas_value->GetListDeprecated()) {
+           *endpoint_metadatas_list) {
         absl::optional<
             std::pair<HttpsRecordPriority, ConnectionEndpointMetadata>>
             pair = EndpointMetadataPairFromValue(endpoint_metadata_value);
@@ -1035,26 +1029,25 @@
     }
 
     absl::optional<std::set<std::string>> aliases;
-    if (aliases_value) {
+    if (aliases_list) {
       aliases.emplace();
-      for (const base::Value& alias_value :
-           aliases_value->GetListDeprecated()) {
+      for (const base::Value& alias_value : *aliases_list) {
         if (!alias_value.is_string())
           return false;
         aliases->insert(alias_value.GetString());
       }
     }
 
-    absl::optional<AddressList> legacy_address_list;
-    if (!AddressListFromListValue(legacy_addresses_value,
-                                  &legacy_address_list)) {
+    absl::optional<AddressList> legacy_address_value;
+    if (!AddressListFromListValue(legacy_addresses_list,
+                                  &legacy_address_value)) {
       return false;
     }
 
     absl::optional<std::vector<std::string>> text_records;
-    if (text_records_value) {
+    if (text_records_list) {
       text_records.emplace();
-      for (const base::Value& value : text_records_value->GetListDeprecated()) {
+      for (const base::Value& value : *text_records_list) {
         if (!value.is_string())
           return false;
         text_records.value().push_back(value.GetString());
@@ -1062,26 +1055,23 @@
     }
 
     absl::optional<std::vector<HostPortPair>> hostname_records;
-    if (hostname_records_value) {
-      DCHECK(host_ports_value);
-      if (hostname_records_value->GetListDeprecated().size() !=
-          host_ports_value->GetListDeprecated().size()) {
+    if (hostname_records_list) {
+      DCHECK(host_ports_list);
+      if (hostname_records_list->size() != host_ports_list->size()) {
         return false;
       }
 
       hostname_records.emplace();
-      for (size_t i = 0; i < hostname_records_value->GetListDeprecated().size();
-           ++i) {
-        if (!hostname_records_value->GetListDeprecated()[i].is_string() ||
-            !host_ports_value->GetListDeprecated()[i].is_int() ||
+      for (size_t i = 0; i < hostname_records_list->size(); ++i) {
+        if (!(*hostname_records_list)[i].is_string() ||
+            !(*host_ports_list)[i].is_int() ||
             !base::IsValueInRangeForNumericType<uint16_t>(
-                host_ports_value->GetListDeprecated()[i].GetInt())) {
+                (*host_ports_list)[i].GetInt())) {
           return false;
         }
         hostname_records.value().push_back(HostPortPair(
-            hostname_records_value->GetListDeprecated()[i].GetString(),
-            base::checked_cast<uint16_t>(
-                host_ports_value->GetListDeprecated()[i].GetInt())));
+            (*hostname_records_list)[i].GetString(),
+            base::checked_cast<uint16_t>((*host_ports_list)[i].GetInt())));
       }
     }
 
@@ -1090,8 +1080,8 @@
 
     // Assume an empty address list if we have an address type and no results.
     if (IsAddressType(dns_query_type) && !ip_endpoints &&
-        !legacy_address_list && !text_records && !hostname_records) {
-      legacy_address_list.emplace();
+        !legacy_address_value && !text_records && !hostname_records) {
+      legacy_address_value.emplace();
     }
 
     Key key(std::move(host), dns_query_type, flags,
@@ -1104,7 +1094,7 @@
     auto found = entries_.find(key);
     if (found == entries_.end()) {
       Entry entry(error, std::move(ip_endpoints), std::move(endpoint_metadatas),
-                  std::move(aliases), legacy_address_list,
+                  std::move(aliases), legacy_address_value,
                   std::move(text_records), std::move(hostname_records),
                   std::move(experimental_results), Entry::SOURCE_UNKNOWN,
                   expiration_time, network_changes_ - 1);
diff --git a/net/dns/host_cache.h b/net/dns/host_cache.h
index 9924e43..07637c8 100644
--- a/net/dns/host_cache.h
+++ b/net/dns/host_cache.h
@@ -327,7 +327,7 @@
     // DNS aliases and deduplicates.
     void MergeDnsAliasesFrom(const HostCache::Entry& source);
 
-    base::Value GetAsValue(bool include_staleness) const;
+    base::Value::Dict GetAsValue(bool include_staleness) const;
 
     // The resolve results for this entry.
     int error_ = ERR_FAILED;
@@ -462,13 +462,13 @@
   // Fills the provided base::Value with the contents of the cache for
   // serialization. `entry_list` must be non-null list, and will be cleared
   // before adding the cache contents.
-  void GetList(base::Value* entry_list,
+  void GetList(base::Value::List& entry_list,
                bool include_staleness,
                SerializationType serialization_type) const;
   // Takes a base::Value list representing cache entries and stores them in the
   // cache, skipping any that already have entries. Returns true on success,
   // false on failure.
-  bool RestoreFromListValue(const base::Value& old_cache);
+  bool RestoreFromListValue(const base::Value::List& old_cache);
   // Returns the number of entries that were restored in the last call to
   // RestoreFromListValue().
   size_t last_restore_size() const { return restore_size_; }
diff --git a/net/dns/host_cache_fuzzer.cc b/net/dns/host_cache_fuzzer.cc
index c35adbc..8b302a29 100644
--- a/net/dns/host_cache_fuzzer.cc
+++ b/net/dns/host_cache_fuzzer.cc
@@ -77,13 +77,13 @@
   // Parse the HostCache.
   constexpr size_t kMaxEntries = 1000;
   HostCache host_cache(kMaxEntries);
-  if (!host_cache.RestoreFromListValue(*value))
+  if (!host_cache.RestoreFromListValue(value->GetList()))
     return;
 
   // Serialize the HostCache.
-  base::Value serialized(base::Value::Type::LIST);
+  base::Value::List serialized;
   host_cache.GetList(
-      &serialized /* entry_list */, true /* include_staleness */,
+      serialized /* entry_list */, true /* include_staleness */,
       HostCache::SerializationType::kRestorable /* serialization_type */);
 
   CHECK_EQ(*value, serialized);
diff --git a/net/dns/host_cache_unittest.cc b/net/dns/host_cache_unittest.cc
index ee8c983..d53d089 100644
--- a/net/dns/host_cache_unittest.cc
+++ b/net/dns/host_cache_unittest.cc
@@ -1263,8 +1263,8 @@
   // Advance to t=12, and serialize/deserialize the cache.
   now += base::Seconds(7);
 
-  base::Value serialized_cache(base::Value::Type::LIST);
-  cache.GetList(&serialized_cache, false /* include_staleness */,
+  base::Value::List serialized_cache;
+  cache.GetList(serialized_cache, false /* include_staleness */,
                 HostCache::SerializationType::kRestorable);
   HostCache restored_cache(kMaxCacheEntries);
 
@@ -1338,8 +1338,8 @@
   EXPECT_EQ(2u, cache.size());
 
   // Serialize the cache.
-  base::Value serialized_cache(base::Value::Type::LIST);
-  cache.GetList(&serialized_cache, false /* include_staleness */,
+  base::Value::List serialized_cache;
+  cache.GetList(serialized_cache, false /* include_staleness */,
                 HostCache::SerializationType::kRestorable);
   HostCache restored_cache(kMaxCacheEntries);
 
@@ -1440,8 +1440,8 @@
   // Advance to t=12, ansd serialize the cache.
   now += base::Seconds(7);
 
-  base::Value serialized_cache(base::Value::Type::LIST);
-  cache.GetList(&serialized_cache, false /* include_staleness */,
+  base::Value::List serialized_cache;
+  cache.GetList(serialized_cache, false /* include_staleness */,
                 HostCache::SerializationType::kRestorable);
   HostCache restored_cache(kMaxCacheEntries);
 
@@ -1534,8 +1534,8 @@
   ASSERT_TRUE(cache.Lookup(key, now));
   ASSERT_EQ(cache.size(), 1u);
 
-  base::Value serialized_cache(base::Value::Type::LIST);
-  cache.GetList(&serialized_cache, /*include_staleness=*/false,
+  base::Value::List serialized_cache;
+  cache.GetList(serialized_cache, /*include_staleness=*/false,
                 HostCache::SerializationType::kRestorable);
   HostCache restored_cache(kMaxCacheEntries);
   EXPECT_TRUE(restored_cache.RestoreFromListValue(serialized_cache));
@@ -1579,8 +1579,8 @@
             cache.Lookup(key2, now)->first.network_isolation_key);
   EXPECT_EQ(2u, cache.size());
 
-  base::Value serialized_cache(base::Value::Type::LIST);
-  cache.GetList(&serialized_cache, false /* include_staleness */,
+  base::Value::List serialized_cache;
+  cache.GetList(serialized_cache, false /* include_staleness */,
                 HostCache::SerializationType::kRestorable);
   HostCache restored_cache(kMaxCacheEntries);
   EXPECT_TRUE(restored_cache.RestoreFromListValue(serialized_cache));
@@ -1615,18 +1615,18 @@
             cache.Lookup(key, now)->first.network_isolation_key);
   EXPECT_EQ(1u, cache.size());
 
-  base::Value serialized_cache(base::Value::Type::LIST);
-  cache.GetList(&serialized_cache, false /* include_staleness */,
+  base::Value::List serialized_cache;
+  cache.GetList(serialized_cache, false /* include_staleness */,
                 HostCache::SerializationType::kDebug);
   HostCache restored_cache(kMaxCacheEntries);
   EXPECT_FALSE(restored_cache.RestoreFromListValue(serialized_cache));
 
-  base::Value::ListView list = serialized_cache.GetListDeprecated();
-  ASSERT_EQ(1u, list.size());
-  ASSERT_TRUE(list[0].is_dict());
-  base::Value* nik_value = list[0].FindPath("network_isolation_key");
-  ASSERT_TRUE(nik_value);
-  ASSERT_EQ(base::Value(kNetworkIsolationKey.ToDebugString()), *nik_value);
+  ASSERT_EQ(1u, serialized_cache.size());
+  ASSERT_TRUE(serialized_cache[0].is_dict());
+  const std::string* nik_string =
+      serialized_cache[0].GetDict().FindString("network_isolation_key");
+  ASSERT_TRUE(nik_string);
+  ASSERT_EQ(kNetworkIsolationKey.ToDebugString(), *nik_string);
 }
 
 TEST(HostCacheTest, SerializeAndDeserialize_Text) {
@@ -1645,13 +1645,13 @@
   cache.Set(key, entry, now, ttl);
   EXPECT_EQ(1u, cache.size());
 
-  base::Value serialized_cache(base::Value::Type::LIST);
-  cache.GetList(&serialized_cache, false /* include_staleness */,
+  base::Value::List serialized_cache;
+  cache.GetList(serialized_cache, false /* include_staleness */,
                 HostCache::SerializationType::kRestorable);
   HostCache restored_cache(kMaxCacheEntries);
   restored_cache.RestoreFromListValue(serialized_cache);
 
-  ASSERT_EQ(1u, serialized_cache.GetListDeprecated().size());
+  ASSERT_EQ(1u, serialized_cache.size());
   ASSERT_EQ(1u, restored_cache.size());
   HostCache::EntryStaleness stale;
   const std::pair<const HostCache::Key, HostCache::Entry>* result =
@@ -1676,8 +1676,8 @@
   cache.Set(key, entry, now, ttl);
   EXPECT_EQ(1u, cache.size());
 
-  base::Value serialized_cache(base::Value::Type::LIST);
-  cache.GetList(&serialized_cache, false /* include_staleness */,
+  base::Value::List serialized_cache;
+  cache.GetList(serialized_cache, false /* include_staleness */,
                 HostCache::SerializationType::kRestorable);
   HostCache restored_cache(kMaxCacheEntries);
   restored_cache.RestoreFromListValue(serialized_cache);
@@ -1726,8 +1726,8 @@
   cache.Set(key, merged_entry, now, ttl);
   EXPECT_EQ(1u, cache.size());
 
-  base::Value serialized_cache(base::Value::Type::LIST);
-  cache.GetList(&serialized_cache, false /* include_staleness */,
+  base::Value::List serialized_cache;
+  cache.GetList(serialized_cache, false /* include_staleness */,
                 HostCache::SerializationType::kRestorable);
   HostCache restored_cache(kMaxCacheEntries);
   restored_cache.RestoreFromListValue(serialized_cache);
diff --git a/net/dns/host_resolver_manager.cc b/net/dns/host_resolver_manager.cc
index d9b8f68..95aec2d 100644
--- a/net/dns/host_resolver_manager.cc
+++ b/net/dns/host_resolver_manager.cc
@@ -281,14 +281,14 @@
 base::Value NetLogProcTaskFailedParams(uint32_t attempt_number,
                                        int net_error,
                                        int os_error) {
-  base::Value dict(base::Value::Type::DICTIONARY);
+  base::Value::Dict dict;
   if (attempt_number)
-    dict.SetIntKey("attempt_number", attempt_number);
+    dict.Set("attempt_number", base::saturated_cast<int>(attempt_number));
 
-  dict.SetIntKey("net_error", net_error);
+  dict.Set("net_error", net_error);
 
   if (os_error) {
-    dict.SetIntKey("os_error", os_error);
+    dict.Set("os_error", os_error);
 #if BUILDFLAG(IS_WIN)
     // Map the error code to a human-readable string.
     LPWSTR error_string = nullptr;
@@ -299,14 +299,14 @@
                   (LPWSTR)&error_string,
                   0,         // Buffer size.
                   nullptr);  // Arguments (unused).
-    dict.SetStringKey("os_error_string", base::WideToUTF8(error_string));
+    dict.Set("os_error_string", base::WideToUTF8(error_string));
     LocalFree(error_string);
 #elif BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA)
-    dict.SetStringKey("os_error_string", gai_strerror(os_error));
+    dict.Set("os_error_string", gai_strerror(os_error));
 #endif
   }
 
-  return dict;
+  return base::Value(std::move(dict));
 }
 
 // Creates NetLog parameters when the DnsTask failed.
@@ -315,44 +315,45 @@
     absl::optional<DnsQueryType> failed_transaction_type,
     absl::optional<base::TimeDelta> ttl,
     const HostCache::Entry* saved_results) {
-  base::Value dict(base::Value::Type::DICTIONARY);
+  base::Value::Dict dict;
   if (failed_transaction_type) {
-    dict.SetIntKey("dns_query_type",
-                   static_cast<int>(failed_transaction_type.value()));
+    dict.Set("dns_query_type",
+             base::strict_cast<int>(failed_transaction_type.value()));
   }
   if (ttl)
-    dict.SetIntKey("error_ttl_sec", ttl.value().InSeconds());
-  dict.SetIntKey("net_error", net_error);
+    dict.Set("error_ttl_sec",
+             base::saturated_cast<int>(ttl.value().InSeconds()));
+  dict.Set("net_error", net_error);
   if (saved_results)
-    dict.SetKey("saved_results", saved_results->NetLogParams());
-  return dict;
+    dict.Set("saved_results", saved_results->NetLogParams());
+  return base::Value(std::move(dict));
 }
 
 base::Value NetLogDnsTaskExtractionFailureParams(
     DnsResponseResultExtractor::ExtractionError extraction_error,
     DnsQueryType dns_query_type,
     const HostCache::Entry& results) {
-  base::Value dict(base::Value::Type::DICTIONARY);
-  dict.SetIntKey("extraction_error", static_cast<int>(extraction_error));
-  dict.SetIntKey("dns_query_type", static_cast<int>(dns_query_type));
-  dict.SetKey("results", results.NetLogParams());
-  return dict;
+  base::Value::Dict dict;
+  dict.Set("extraction_error", base::strict_cast<int>(extraction_error));
+  dict.Set("dns_query_type", base::strict_cast<int>(dns_query_type));
+  dict.Set("results", results.NetLogParams());
+  return base::Value(std::move(dict));
 }
 
 // Creates NetLog parameters for HOST_RESOLVER_MANAGER_JOB_ATTACH/DETACH events.
 base::Value NetLogJobAttachParams(const NetLogSource& source,
                                   RequestPriority priority) {
-  base::Value dict(base::Value::Type::DICTIONARY);
-  source.AddToEventParameters(&dict);
-  dict.SetStringKey("priority", RequestPriorityToString(priority));
-  return dict;
+  base::Value::Dict dict;
+  source.AddToEventParameters(dict);
+  dict.Set("priority", RequestPriorityToString(priority));
+  return base::Value(std::move(dict));
 }
 
 base::Value NetLogIPv6AvailableParams(bool ipv6_available, bool cached) {
-  base::Value dict(base::Value::Type::DICTIONARY);
-  dict.SetBoolKey("ipv6_available", ipv6_available);
-  dict.SetBoolKey("cached", cached);
-  return dict;
+  base::Value::Dict dict;
+  dict.Set("ipv6_available", ipv6_available);
+  dict.Set("cached", cached);
+  return base::Value(std::move(dict));
 }
 
 // The logging routines are defined here because some requests are resolved
@@ -462,9 +463,9 @@
 };
 
 base::Value NetLogResults(const HostCache::Entry& results) {
-  base::Value dict(base::Value::Type::DICTIONARY);
-  dict.SetKey("results", results.NetLogParams());
-  return dict;
+  base::Value::Dict dict;
+  dict.Set("results", results.NetLogParams());
+  return base::Value(std::move(dict));
 }
 
 base::Value ToLogStringValue(const HostResolver::Host& host) {
@@ -846,19 +847,19 @@
 
     source_net_log_.BeginEvent(
         NetLogEventType::HOST_RESOLVER_MANAGER_REQUEST, [this] {
-          base::Value dict(base::Value::Type::DICTIONARY);
-          dict.SetKey("host", ToLogStringValue(request_host_));
-          dict.SetIntKey("dns_query_type",
-                         base::strict_cast<int>(parameters_.dns_query_type));
-          dict.SetBoolKey("allow_cached_response",
-                          parameters_.cache_usage !=
-                              ResolveHostParameters::CacheUsage::DISALLOWED);
-          dict.SetBoolKey("is_speculative", parameters_.is_speculative);
-          dict.SetStringKey("network_isolation_key",
-                            network_isolation_key_.ToDebugString());
-          dict.SetIntKey("secure_dns_policy",
-                         static_cast<int>(parameters_.secure_dns_policy));
-          return dict;
+          base::Value::Dict dict;
+          dict.Set("host", ToLogStringValue(request_host_));
+          dict.Set("dns_query_type",
+                   base::strict_cast<int>(parameters_.dns_query_type));
+          dict.Set("allow_cached_response",
+                   parameters_.cache_usage !=
+                       ResolveHostParameters::CacheUsage::DISALLOWED);
+          dict.Set("is_speculative", parameters_.is_speculative);
+          dict.Set("network_isolation_key",
+                   network_isolation_key_.ToDebugString());
+          dict.Set("secure_dns_policy",
+                   base::strict_cast<int>(parameters_.secure_dns_policy));
+          return base::Value(std::move(dict));
         });
   }
 
@@ -1358,7 +1359,7 @@
     base::Value::List transactions_needed_value;
     for (const TransactionInfo& info : transactions_needed_) {
       base::Value::Dict transaction_dict;
-      transaction_dict.Set("dns_query_type", static_cast<int>(info.type));
+      transaction_dict.Set("dns_query_type", base::strict_cast<int>(info.type));
       transactions_needed_value.Append(std::move(transaction_dict));
     }
     dict.Set("transactions_needed", std::move(transactions_needed_value));
@@ -1373,7 +1374,8 @@
       base::Value::List list;
       for (const TransactionInfo& info : transactions_in_progress_) {
         base::Value::Dict transaction_dict;
-        transaction_dict.Set("dns_query_type", static_cast<int>(info.type));
+        transaction_dict.Set("dns_query_type",
+                             base::strict_cast<int>(info.type));
         list.Append(std::move(transaction_dict));
       }
       dict.Set("started_transactions", std::move(list));
@@ -1383,7 +1385,8 @@
       base::Value::List list;
       for (const TransactionInfo& info : transactions_needed_) {
         base::Value::Dict transaction_dict;
-        transaction_dict.Set("dns_query_type", static_cast<int>(info.type));
+        transaction_dict.Set("dns_query_type",
+                             base::strict_cast<int>(info.type));
         list.Append(std::move(transaction_dict));
       }
       dict.Set("queued_transactions", std::move(list));
@@ -2493,17 +2496,17 @@
 
  private:
   base::Value NetLogJobCreationParams(const NetLogSource& source) {
-    base::Value dict(base::Value::Type::DICTIONARY);
-    source.AddToEventParameters(&dict);
-    dict.SetKey("host", ToLogStringValue(key_.host));
+    base::Value::Dict dict;
+    source.AddToEventParameters(dict);
+    dict.Set("host", ToLogStringValue(key_.host));
     std::vector<base::Value> query_types_list;
     for (DnsQueryType query_type : key_.query_types)
       query_types_list.emplace_back(kDnsQueryTypes.at(query_type));
-    dict.SetKey("dns_query_types", base::Value(std::move(query_types_list)));
-    dict.SetIntKey("secure_dns_mode", static_cast<int>(key_.secure_dns_mode));
-    dict.SetStringKey("network_isolation_key",
-                      key_.network_isolation_key.ToDebugString());
-    return dict;
+    dict.Set("dns_query_types", base::Value(std::move(query_types_list)));
+    dict.Set("secure_dns_mode", base::strict_cast<int>(key_.secure_dns_mode));
+    dict.Set("network_isolation_key",
+             key_.network_isolation_key.ToDebugString());
+    return base::Value(std::move(dict));
   }
 
   void Finish() {
diff --git a/net/dns/public/dns_over_https_server_config.cc b/net/dns/public/dns_over_https_server_config.cc
index 0554b1d5..f8c3334f 100644
--- a/net/dns/public/dns_over_https_server_config.cc
+++ b/net/dns/public/dns_over_https_server_config.cc
@@ -183,7 +183,7 @@
       if (!dict)
         return absl::nullopt;
       IPAddressList parsed_ips;
-      const base::Value* ips = endpoint.FindKey(kJsonKeyIps);
+      const base::Value* ips = dict->Find(kJsonKeyIps);
       if (ips) {
         const base::Value::List* ip_list = ips->GetIfList();
         if (!ip_list)
diff --git a/net/log/net_log_util.cc b/net/log/net_log_util.cc
index 92309fe0..a24a3ba 100644
--- a/net/log/net_log_util.cc
+++ b/net/log/net_log_util.cc
@@ -366,15 +366,13 @@
       dict.Set("dns_config", std::move(dns_config));
 
       base::Value::Dict cache_info_dict;
-      base::Value cache_contents_list(base::Value::Type::LIST);
+      base::Value::List cache_contents_list;
 
       cache_info_dict.Set("capacity", static_cast<int>(cache->max_entries()));
       cache_info_dict.Set("network_changes", cache->network_changes());
 
-      if (cache_contents_list.is_list()) {
-        cache->GetList(&cache_contents_list, true /* include_staleness */,
-                       HostCache::SerializationType::kDebug);
-      }
+      cache->GetList(cache_contents_list, true /* include_staleness */,
+                     HostCache::SerializationType::kDebug);
       cache_info_dict.Set("entries", std::move(cache_contents_list));
 
       dict.Set("cache", std::move(cache_info_dict));
diff --git a/remoting/codec/webrtc_video_encoder_av1.cc b/remoting/codec/webrtc_video_encoder_av1.cc
index a54ab9e..b537c94 100644
--- a/remoting/codec/webrtc_video_encoder_av1.cc
+++ b/remoting/codec/webrtc_video_encoder_av1.cc
@@ -70,27 +70,88 @@
   error = aom_codec_control(codec.get(), AOME_SET_CPUUSED, 10);
   DCHECK_EQ(error, AOM_CODEC_OK) << "Failed to set AOME_SET_CPUUSED";
 
+  error = aom_codec_control(codec.get(), AV1E_SET_AQ_MODE, 3);
+  DCHECK_EQ(error, AOM_CODEC_OK) << "Failed to set AV1E_SET_AQ_MODE";
+
   if (config_.g_threads > 1) {
     error = aom_codec_control(codec.get(), AV1E_SET_ROW_MT, 1);
     DCHECK_EQ(error, AOM_CODEC_OK) << "Failed to set AV1E_SET_ROW_MT";
   }
 
-  // The param for the tile column/row controls are log2 values so 0 is ok.
-  int log2_tiles = std::min(static_cast<int>(config_.g_threads >> 1), 6);
-
-  error = aom_codec_control(codec.get(), AV1E_SET_TILE_COLUMNS, log2_tiles);
+  // The param for the tile column control is a log2 value so 0 is ok.
+  error =
+      aom_codec_control(codec.get(), AV1E_SET_TILE_COLUMNS,
+                        std::min(static_cast<int>(config_.g_threads >> 1), 6));
   DCHECK_EQ(error, AOM_CODEC_OK) << "Failed to set AV1E_SET_TILE_COLUMNS";
 
-  error = aom_codec_control(codec.get(), AV1E_SET_TILE_ROWS, log2_tiles);
-  DCHECK_EQ(error, AOM_CODEC_OK) << "Failed to set AV1E_SET_TILE_ROWS";
+  // TODO(joedow): Experiment with AV1E_SET_TILE_ROWS. Note that the total
+  // number of COLUMNS and ROWS should add up to, at most, config_.g_threads.
 
-  // TODO(joedow): Set AV1E_SET_TUNE_CONTENT to AOM_CONTENT_SCREEN once the
-  // performance is more acceptable. Selecting AOM_CONTENT_SCREEN over
-  // AOM_CONTENT_DEFAULT increased the amount of latency during encoding by 15ms
-  // per frame during my testing.
+  // These make realtime encoder faster.
+  error =
+      aom_codec_control(codec.get(), AV1E_SET_TUNE_CONTENT, AOM_CONTENT_SCREEN);
+  DCHECK_EQ(error, AOM_CODEC_OK) << "Failed to set AOM_CONTENT_SCREEN";
+  error = aom_codec_control(codec.get(), AV1E_SET_ENABLE_PALETTE, 1);
+  DCHECK_EQ(error, AOM_CODEC_OK) << "Failed to set AV1E_SET_ENABLE_PALETTE";
+  error = aom_codec_control(codec.get(), AV1E_SET_ENABLE_INTRABC, 0);
+  DCHECK_EQ(error, AOM_CODEC_OK) << "Failed to set AV1E_SET_ENABLE_INTRABC";
 
-  error = aom_codec_control(codec.get(), AV1E_SET_AQ_MODE, 3);
-  DCHECK_EQ(error, AOM_CODEC_OK) << "Failed to set AV1E_SET_AQ_MODE";
+  // These default to on but should not be used for real-time encoding.
+  error = aom_codec_control(codec.get(), AV1E_SET_ENABLE_TPL_MODEL, 0);
+  DCHECK_EQ(error, AOM_CODEC_OK) << "Failed to set AV1E_SET_ENABLE_TPL_MODEL";
+  error = aom_codec_control(codec.get(), AV1E_SET_DELTAQ_MODE, 0);
+  DCHECK_EQ(error, AOM_CODEC_OK) << "Failed to set AV1E_SET_DELTAQ_MODE";
+  error = aom_codec_control(codec.get(), AV1E_SET_ENABLE_ORDER_HINT, 0);
+  DCHECK_EQ(error, AOM_CODEC_OK) << "Failed to set AV1E_SET_ENABLE_ORDER_HINT";
+  error = aom_codec_control(codec.get(), AV1E_SET_ENABLE_OBMC, 0);
+  DCHECK_EQ(error, AOM_CODEC_OK) << "Failed to set AV1E_SET_ENABLE_OBMC";
+  error = aom_codec_control(codec.get(), AV1E_SET_ENABLE_WARPED_MOTION, 0);
+  DCHECK_EQ(error, AOM_CODEC_OK)
+      << "Failed to set AV1E_SET_ENABLE_WARPED_MOTION";
+  error = aom_codec_control(codec.get(), AV1E_SET_ENABLE_GLOBAL_MOTION, 0);
+  DCHECK_EQ(error, AOM_CODEC_OK)
+      << "Failed to set AV1E_SET_ENABLE_GLOBAL_MOTION";
+  error = aom_codec_control(codec.get(), AV1E_SET_ENABLE_REF_FRAME_MVS, 0);
+  DCHECK_EQ(error, AOM_CODEC_OK)
+      << "Failed to set AV1E_SET_ENABLE_REF_FRAME_MVS";
+
+  // These should significantly speed up key frame encoding.
+  error = aom_codec_control(codec.get(), AV1E_SET_ENABLE_CFL_INTRA, 0);
+  DCHECK_EQ(error, AOM_CODEC_OK) << "Failed to set AV1E_SET_ENABLE_CFL_INTRA";
+  error = aom_codec_control(codec.get(), AV1E_SET_ENABLE_SMOOTH_INTRA, 0);
+  DCHECK_EQ(error, AOM_CODEC_OK)
+      << "Failed to set AV1E_SET_ENABLE_SMOOTH_INTRA";
+  error = aom_codec_control(codec.get(), AV1E_SET_ENABLE_ANGLE_DELTA, 0);
+  DCHECK_EQ(error, AOM_CODEC_OK) << "Failed to set AV1E_SET_ENABLE_ANGLE_DELTA";
+  error = aom_codec_control(codec.get(), AV1E_SET_ENABLE_FILTER_INTRA, 0);
+  DCHECK_EQ(error, AOM_CODEC_OK)
+      << "Failed to set AV1E_SET_ENABLE_FILTER_INTRA";
+  error = aom_codec_control(codec.get(), AV1E_SET_INTRA_DEFAULT_TX_ONLY, 1);
+  DCHECK_EQ(error, AOM_CODEC_OK)
+      << "Failed to set AV1E_SET_INTRA_DEFAULT_TX_ONLY";
+
+  // Misc. quality settings.
+  error = aom_codec_control(codec.get(), AV1E_SET_ENABLE_CDEF, 1);
+  DCHECK_EQ(error, AOM_CODEC_OK) << "Failed to set AV1E_SET_ENABLE_CDEF";
+  error = aom_codec_control(codec.get(), AV1E_SET_NOISE_SENSITIVITY, 0);
+  DCHECK_EQ(error, AOM_CODEC_OK) << "Failed to set AV1E_SET_NOISE_SENSITIVITY";
+  error = aom_codec_control(codec.get(), AV1E_SET_ENABLE_PAETH_INTRA, 0);
+  DCHECK_EQ(error, AOM_CODEC_OK) << "Failed to set AV1E_SET_ENABLE_PAETH_INTRA";
+  error = aom_codec_control(codec.get(), AV1E_SET_ENABLE_QM, 0);
+  DCHECK_EQ(error, AOM_CODEC_OK) << "Failed to set AV1E_SET_ENABLE_QM";
+  error = aom_codec_control(codec.get(), AV1E_SET_ENABLE_RECT_PARTITIONS, 0);
+  DCHECK_EQ(error, AOM_CODEC_OK)
+      << "Failed to set AV1E_SET_ENABLE_RECT_PARTITIONS";
+  error = aom_codec_control(codec.get(), AV1E_SET_ENABLE_RESTORATION, 0);
+  DCHECK_EQ(error, AOM_CODEC_OK) << "Failed to set AV1E_SET_ENABLE_RESTORATION";
+  error = aom_codec_control(codec.get(), AV1E_SET_ENABLE_SMOOTH_INTERINTRA, 0);
+  DCHECK_EQ(error, AOM_CODEC_OK)
+      << "Failed to set AV1E_SET_ENABLE_SMOOTH_INTERINTRA";
+  error = aom_codec_control(codec.get(), AV1E_SET_ENABLE_TX64, 0);
+  DCHECK_EQ(error, AOM_CODEC_OK) << "Failed to set AV1E_SET_ENABLE_TX64";
+  error = aom_codec_control(codec.get(), AV1E_SET_MAX_REFERENCE_FRAMES, 3);
+  DCHECK_EQ(error, AOM_CODEC_OK)
+      << "Failed to set AV1E_SET_MAX_REFERENCE_FRAMES";
 
   codec_ = std::move(codec);
   return true;
@@ -174,8 +235,11 @@
   config_.rc_max_quantizer = 0;
   config_.rc_min_quantizer = 0;
   config_.rc_dropframe_thresh = 0;
-  config_.rc_undershoot_pct = 100;
-  config_.rc_overshoot_pct = 15;
+  config_.rc_undershoot_pct = 50;
+  config_.rc_overshoot_pct = 50;
+  config_.rc_buf_initial_sz = 600;
+  config_.rc_buf_optimal_sz = 600;
+  config_.rc_buf_sz = 1000;
   config_.rc_end_usage = AOM_CBR;
   config_.rc_target_bitrate = 1024;  // 1024 Kbps = 1Mbps.
 }
diff --git a/remoting/protocol/webrtc_video_encoder_wrapper.cc b/remoting/protocol/webrtc_video_encoder_wrapper.cc
index 49840f86..6fed61c 100644
--- a/remoting/protocol/webrtc_video_encoder_wrapper.cc
+++ b/remoting/protocol/webrtc_video_encoder_wrapper.cc
@@ -402,7 +402,6 @@
 #endif
   } else if (frame.codec == webrtc::kVideoCodecAV1) {
     // TODO(joedow): Set codec specific params for AV1 here.
-    NOTIMPLEMENTED();
   } else {
     NOTREACHED();
   }
diff --git a/services/network/public/mojom/network_context.mojom b/services/network/public/mojom/network_context.mojom
index cc3f990..9dc7502f 100644
--- a/services/network/public/mojom/network_context.mojom
+++ b/services/network/public/mojom/network_context.mojom
@@ -212,7 +212,7 @@
 
   // Points to a path that might contain existing network service data files and
   // databases from network contexts created with the same `data_directory`.
-  // This parameter must be specified if `perform_migration` is set to true.
+  // This parameter must be specified if `trigger_migration` is set to true.
   // Once migration has been successful (see below), data is no longer read from
   // this path. Data could continue to be read from this path if migration
   // fails, or if `trigger_migration` has never been set to true.
diff --git a/testing/buildbot/chromium.chromiumos.json b/testing/buildbot/chromium.chromiumos.json
index ed3b564..e736a5c 100644
--- a/testing/buildbot/chromium.chromiumos.json
+++ b/testing/buildbot/chromium.chromiumos.json
@@ -3394,7 +3394,6 @@
           "--test-launcher-filter-file=../../testing/buildbot/filters/linux-chromeos.browser_tests.require_lacros.filter",
           "--lacros-chrome-path=lacros_clang_x64"
         ],
-        "ci_only": true,
         "isolate_profile_data": true,
         "merge": {
           "args": [],
@@ -5661,21 +5660,21 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.interactive_ui_tests.filter",
-          "--ash-chrome-path-override=../../lacros_version_skew_tests_v104.0.5100.0/test_ash_chrome"
+          "--ash-chrome-path-override=../../lacros_version_skew_tests_v104.0.5101.0/test_ash_chrome"
         ],
         "isolate_profile_data": true,
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
         },
-        "name": "interactive_ui_tests Lacros version skew testing ash 104.0.5100.0",
+        "name": "interactive_ui_tests Lacros version skew testing ash 104.0.5101.0",
         "swarming": {
           "can_use_on_swarming_builders": true,
           "cipd_packages": [
             {
               "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
-              "location": "lacros_version_skew_tests_v104.0.5100.0",
-              "revision": "version:104.0.5100.0"
+              "location": "lacros_version_skew_tests_v104.0.5101.0",
+              "revision": "version:104.0.5101.0"
             }
           ],
           "dimension_sets": [
@@ -5688,7 +5687,7 @@
         },
         "test": "interactive_ui_tests",
         "test_id_prefix": "ninja://chrome/test:interactive_ui_tests/",
-        "variant_id": "Lacros version skew testing ash 104.0.5100.0"
+        "variant_id": "Lacros version skew testing ash 104.0.5101.0"
       },
       {
         "isolate_profile_data": true,
@@ -5826,21 +5825,21 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.skew.filter",
-          "--ash-chrome-path-override=../../lacros_version_skew_tests_v104.0.5100.0/test_ash_chrome"
+          "--ash-chrome-path-override=../../lacros_version_skew_tests_v104.0.5101.0/test_ash_chrome"
         ],
         "isolate_profile_data": true,
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
         },
-        "name": "lacros_chrome_browsertests Lacros version skew testing ash 104.0.5100.0",
+        "name": "lacros_chrome_browsertests Lacros version skew testing ash 104.0.5101.0",
         "swarming": {
           "can_use_on_swarming_builders": true,
           "cipd_packages": [
             {
               "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
-              "location": "lacros_version_skew_tests_v104.0.5100.0",
-              "revision": "version:104.0.5100.0"
+              "location": "lacros_version_skew_tests_v104.0.5101.0",
+              "revision": "version:104.0.5101.0"
             }
           ],
           "dimension_sets": [
@@ -5852,7 +5851,7 @@
         },
         "test": "lacros_chrome_browsertests",
         "test_id_prefix": "ninja://chrome/test:lacros_chrome_browsertests/",
-        "variant_id": "Lacros version skew testing ash 104.0.5100.0"
+        "variant_id": "Lacros version skew testing ash 104.0.5101.0"
       },
       {
         "args": [
@@ -5972,21 +5971,21 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.skew.filter",
-          "--ash-chrome-path-override=../../lacros_version_skew_tests_v104.0.5100.0/test_ash_chrome"
+          "--ash-chrome-path-override=../../lacros_version_skew_tests_v104.0.5101.0/test_ash_chrome"
         ],
         "isolate_profile_data": true,
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
         },
-        "name": "lacros_chrome_browsertests_run_in_series Lacros version skew testing ash 104.0.5100.0",
+        "name": "lacros_chrome_browsertests_run_in_series Lacros version skew testing ash 104.0.5101.0",
         "swarming": {
           "can_use_on_swarming_builders": true,
           "cipd_packages": [
             {
               "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
-              "location": "lacros_version_skew_tests_v104.0.5100.0",
-              "revision": "version:104.0.5100.0"
+              "location": "lacros_version_skew_tests_v104.0.5101.0",
+              "revision": "version:104.0.5101.0"
             }
           ],
           "dimension_sets": [
@@ -5998,7 +5997,7 @@
         },
         "test": "lacros_chrome_browsertests_run_in_series",
         "test_id_prefix": "ninja://chrome/test:lacros_chrome_browsertests_run_in_series/",
-        "variant_id": "Lacros version skew testing ash 104.0.5100.0"
+        "variant_id": "Lacros version skew testing ash 104.0.5101.0"
       },
       {
         "isolate_profile_data": true,
diff --git a/testing/buildbot/chromium.fyi.json b/testing/buildbot/chromium.fyi.json
index 2ad3f0cb..fb866cc 100644
--- a/testing/buildbot/chromium.fyi.json
+++ b/testing/buildbot/chromium.fyi.json
@@ -9091,7 +9091,7 @@
     "gtest_tests": [
       {
         "args": [
-          "--enable-features=PartitionAllocBackupRefPtr",
+          "--enable-features=PartitionAllocBackupRefPtr:enabled-processes/non-renderer",
           "--gs-results-bucket=chromium-result-details",
           "--recover-devices"
         ],
@@ -9143,7 +9143,7 @@
       },
       {
         "args": [
-          "--enable-features=PartitionAllocBackupRefPtr",
+          "--enable-features=PartitionAllocBackupRefPtr:enabled-processes/non-renderer",
           "--gs-results-bucket=chromium-result-details",
           "--recover-devices"
         ],
@@ -9195,7 +9195,7 @@
       },
       {
         "args": [
-          "--enable-features=PartitionAllocBackupRefPtr",
+          "--enable-features=PartitionAllocBackupRefPtr:enabled-processes/non-renderer",
           "--gs-results-bucket=chromium-result-details",
           "--recover-devices"
         ],
@@ -9247,7 +9247,7 @@
       },
       {
         "args": [
-          "--enable-features=PartitionAllocBackupRefPtr",
+          "--enable-features=PartitionAllocBackupRefPtr:enabled-processes/non-renderer",
           "--gs-results-bucket=chromium-result-details",
           "--recover-devices"
         ],
@@ -9301,7 +9301,7 @@
       },
       {
         "args": [
-          "--enable-features=PartitionAllocBackupRefPtr",
+          "--enable-features=PartitionAllocBackupRefPtr:enabled-processes/non-renderer",
           "--gs-results-bucket=chromium-result-details",
           "--recover-devices"
         ],
@@ -9353,7 +9353,7 @@
       },
       {
         "args": [
-          "--enable-features=PartitionAllocBackupRefPtr",
+          "--enable-features=PartitionAllocBackupRefPtr:enabled-processes/non-renderer",
           "--gs-results-bucket=chromium-result-details",
           "--recover-devices"
         ],
@@ -9405,7 +9405,7 @@
       },
       {
         "args": [
-          "--enable-features=PartitionAllocBackupRefPtr",
+          "--enable-features=PartitionAllocBackupRefPtr:enabled-processes/non-renderer",
           "--gs-results-bucket=chromium-result-details",
           "--recover-devices"
         ],
@@ -9458,7 +9458,7 @@
       },
       {
         "args": [
-          "--enable-features=PartitionAllocBackupRefPtr",
+          "--enable-features=PartitionAllocBackupRefPtr:enabled-processes/non-renderer",
           "--gs-results-bucket=chromium-result-details",
           "--recover-devices"
         ],
@@ -9511,7 +9511,7 @@
       },
       {
         "args": [
-          "--enable-features=PartitionAllocBackupRefPtr",
+          "--enable-features=PartitionAllocBackupRefPtr:enabled-processes/non-renderer",
           "--gs-results-bucket=chromium-result-details",
           "--recover-devices"
         ],
@@ -9564,7 +9564,7 @@
       },
       {
         "args": [
-          "--enable-features=PartitionAllocBackupRefPtr",
+          "--enable-features=PartitionAllocBackupRefPtr:enabled-processes/non-renderer",
           "--gs-results-bucket=chromium-result-details",
           "--recover-devices"
         ],
@@ -9620,7 +9620,7 @@
     "gtest_tests": [
       {
         "args": [
-          "--enable-features=PartitionAllocBackupRefPtr",
+          "--enable-features=PartitionAllocBackupRefPtr:enabled-processes/non-renderer",
           "--gs-results-bucket=chromium-result-details",
           "--recover-devices"
         ],
@@ -9672,7 +9672,7 @@
       },
       {
         "args": [
-          "--enable-features=PartitionAllocBackupRefPtr",
+          "--enable-features=PartitionAllocBackupRefPtr:enabled-processes/non-renderer",
           "--gs-results-bucket=chromium-result-details",
           "--recover-devices"
         ],
@@ -9724,7 +9724,7 @@
       },
       {
         "args": [
-          "--enable-features=PartitionAllocBackupRefPtr",
+          "--enable-features=PartitionAllocBackupRefPtr:enabled-processes/non-renderer",
           "--gs-results-bucket=chromium-result-details",
           "--recover-devices"
         ],
@@ -9776,7 +9776,7 @@
       },
       {
         "args": [
-          "--enable-features=PartitionAllocBackupRefPtr",
+          "--enable-features=PartitionAllocBackupRefPtr:enabled-processes/non-renderer",
           "--gs-results-bucket=chromium-result-details",
           "--recover-devices"
         ],
@@ -9830,7 +9830,7 @@
       },
       {
         "args": [
-          "--enable-features=PartitionAllocBackupRefPtr",
+          "--enable-features=PartitionAllocBackupRefPtr:enabled-processes/non-renderer",
           "--gs-results-bucket=chromium-result-details",
           "--recover-devices"
         ],
@@ -9882,7 +9882,7 @@
       },
       {
         "args": [
-          "--enable-features=PartitionAllocBackupRefPtr",
+          "--enable-features=PartitionAllocBackupRefPtr:enabled-processes/non-renderer",
           "--gs-results-bucket=chromium-result-details",
           "--recover-devices"
         ],
@@ -9934,7 +9934,7 @@
       },
       {
         "args": [
-          "--enable-features=PartitionAllocBackupRefPtr",
+          "--enable-features=PartitionAllocBackupRefPtr:enabled-processes/non-renderer",
           "--gs-results-bucket=chromium-result-details",
           "--recover-devices"
         ],
@@ -9987,7 +9987,7 @@
       },
       {
         "args": [
-          "--enable-features=PartitionAllocBackupRefPtr",
+          "--enable-features=PartitionAllocBackupRefPtr:enabled-processes/non-renderer",
           "--gs-results-bucket=chromium-result-details",
           "--recover-devices"
         ],
@@ -10040,7 +10040,7 @@
       },
       {
         "args": [
-          "--enable-features=PartitionAllocBackupRefPtr",
+          "--enable-features=PartitionAllocBackupRefPtr:enabled-processes/non-renderer",
           "--gs-results-bucket=chromium-result-details",
           "--recover-devices"
         ],
@@ -10093,7 +10093,7 @@
       },
       {
         "args": [
-          "--enable-features=PartitionAllocBackupRefPtr",
+          "--enable-features=PartitionAllocBackupRefPtr:enabled-processes/non-renderer",
           "--gs-results-bucket=chromium-result-details",
           "--recover-devices"
         ],
@@ -85363,21 +85363,21 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.interactive_ui_tests.filter",
-          "--ash-chrome-path-override=../../lacros_version_skew_tests_v104.0.5100.0/test_ash_chrome"
+          "--ash-chrome-path-override=../../lacros_version_skew_tests_v104.0.5101.0/test_ash_chrome"
         ],
         "isolate_profile_data": true,
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
         },
-        "name": "interactive_ui_tests Lacros version skew testing ash 104.0.5100.0",
+        "name": "interactive_ui_tests Lacros version skew testing ash 104.0.5101.0",
         "swarming": {
           "can_use_on_swarming_builders": true,
           "cipd_packages": [
             {
               "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
-              "location": "lacros_version_skew_tests_v104.0.5100.0",
-              "revision": "version:104.0.5100.0"
+              "location": "lacros_version_skew_tests_v104.0.5101.0",
+              "revision": "version:104.0.5101.0"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
@@ -85385,7 +85385,7 @@
         },
         "test": "interactive_ui_tests",
         "test_id_prefix": "ninja://chrome/test:interactive_ui_tests/",
-        "variant_id": "Lacros version skew testing ash 104.0.5100.0"
+        "variant_id": "Lacros version skew testing ash 104.0.5101.0"
       },
       {
         "isolate_profile_data": true,
@@ -85498,28 +85498,28 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.skew.filter",
-          "--ash-chrome-path-override=../../lacros_version_skew_tests_v104.0.5100.0/test_ash_chrome"
+          "--ash-chrome-path-override=../../lacros_version_skew_tests_v104.0.5101.0/test_ash_chrome"
         ],
         "isolate_profile_data": true,
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
         },
-        "name": "lacros_chrome_browsertests Lacros version skew testing ash 104.0.5100.0",
+        "name": "lacros_chrome_browsertests Lacros version skew testing ash 104.0.5101.0",
         "swarming": {
           "can_use_on_swarming_builders": true,
           "cipd_packages": [
             {
               "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
-              "location": "lacros_version_skew_tests_v104.0.5100.0",
-              "revision": "version:104.0.5100.0"
+              "location": "lacros_version_skew_tests_v104.0.5101.0",
+              "revision": "version:104.0.5101.0"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
         "test": "lacros_chrome_browsertests",
         "test_id_prefix": "ninja://chrome/test:lacros_chrome_browsertests/",
-        "variant_id": "Lacros version skew testing ash 104.0.5100.0"
+        "variant_id": "Lacros version skew testing ash 104.0.5101.0"
       },
       {
         "args": [
@@ -85619,28 +85619,28 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.skew.filter",
-          "--ash-chrome-path-override=../../lacros_version_skew_tests_v104.0.5100.0/test_ash_chrome"
+          "--ash-chrome-path-override=../../lacros_version_skew_tests_v104.0.5101.0/test_ash_chrome"
         ],
         "isolate_profile_data": true,
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
         },
-        "name": "lacros_chrome_browsertests_run_in_series Lacros version skew testing ash 104.0.5100.0",
+        "name": "lacros_chrome_browsertests_run_in_series Lacros version skew testing ash 104.0.5101.0",
         "swarming": {
           "can_use_on_swarming_builders": true,
           "cipd_packages": [
             {
               "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
-              "location": "lacros_version_skew_tests_v104.0.5100.0",
-              "revision": "version:104.0.5100.0"
+              "location": "lacros_version_skew_tests_v104.0.5101.0",
+              "revision": "version:104.0.5101.0"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
         "test": "lacros_chrome_browsertests_run_in_series",
         "test_id_prefix": "ninja://chrome/test:lacros_chrome_browsertests_run_in_series/",
-        "variant_id": "Lacros version skew testing ash 104.0.5100.0"
+        "variant_id": "Lacros version skew testing ash 104.0.5101.0"
       },
       {
         "isolate_profile_data": true,
@@ -86978,20 +86978,20 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.interactive_ui_tests.filter",
-          "--ash-chrome-path-override=../../lacros_version_skew_tests_v104.0.5100.0/test_ash_chrome"
+          "--ash-chrome-path-override=../../lacros_version_skew_tests_v104.0.5101.0/test_ash_chrome"
         ],
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
         },
-        "name": "interactive_ui_tests Lacros version skew testing ash 104.0.5100.0",
+        "name": "interactive_ui_tests Lacros version skew testing ash 104.0.5101.0",
         "swarming": {
           "can_use_on_swarming_builders": true,
           "cipd_packages": [
             {
               "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
-              "location": "lacros_version_skew_tests_v104.0.5100.0",
-              "revision": "version:104.0.5100.0"
+              "location": "lacros_version_skew_tests_v104.0.5101.0",
+              "revision": "version:104.0.5101.0"
             }
           ],
           "dimension_sets": [
@@ -87005,7 +87005,7 @@
         },
         "test": "interactive_ui_tests",
         "test_id_prefix": "ninja://chrome/test:interactive_ui_tests/",
-        "variant_id": "Lacros version skew testing ash 104.0.5100.0"
+        "variant_id": "Lacros version skew testing ash 104.0.5101.0"
       },
       {
         "merge": {
@@ -87143,20 +87143,20 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.skew.filter",
-          "--ash-chrome-path-override=../../lacros_version_skew_tests_v104.0.5100.0/test_ash_chrome"
+          "--ash-chrome-path-override=../../lacros_version_skew_tests_v104.0.5101.0/test_ash_chrome"
         ],
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
         },
-        "name": "lacros_chrome_browsertests Lacros version skew testing ash 104.0.5100.0",
+        "name": "lacros_chrome_browsertests Lacros version skew testing ash 104.0.5101.0",
         "swarming": {
           "can_use_on_swarming_builders": true,
           "cipd_packages": [
             {
               "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
-              "location": "lacros_version_skew_tests_v104.0.5100.0",
-              "revision": "version:104.0.5100.0"
+              "location": "lacros_version_skew_tests_v104.0.5101.0",
+              "revision": "version:104.0.5101.0"
             }
           ],
           "dimension_sets": [
@@ -87169,7 +87169,7 @@
         },
         "test": "lacros_chrome_browsertests",
         "test_id_prefix": "ninja://chrome/test:lacros_chrome_browsertests/",
-        "variant_id": "Lacros version skew testing ash 104.0.5100.0"
+        "variant_id": "Lacros version skew testing ash 104.0.5101.0"
       },
       {
         "args": [
@@ -87289,20 +87289,20 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.skew.filter",
-          "--ash-chrome-path-override=../../lacros_version_skew_tests_v104.0.5100.0/test_ash_chrome"
+          "--ash-chrome-path-override=../../lacros_version_skew_tests_v104.0.5101.0/test_ash_chrome"
         ],
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
         },
-        "name": "lacros_chrome_browsertests_run_in_series Lacros version skew testing ash 104.0.5100.0",
+        "name": "lacros_chrome_browsertests_run_in_series Lacros version skew testing ash 104.0.5101.0",
         "swarming": {
           "can_use_on_swarming_builders": true,
           "cipd_packages": [
             {
               "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
-              "location": "lacros_version_skew_tests_v104.0.5100.0",
-              "revision": "version:104.0.5100.0"
+              "location": "lacros_version_skew_tests_v104.0.5101.0",
+              "revision": "version:104.0.5101.0"
             }
           ],
           "dimension_sets": [
@@ -87315,7 +87315,7 @@
         },
         "test": "lacros_chrome_browsertests_run_in_series",
         "test_id_prefix": "ninja://chrome/test:lacros_chrome_browsertests_run_in_series/",
-        "variant_id": "Lacros version skew testing ash 104.0.5100.0"
+        "variant_id": "Lacros version skew testing ash 104.0.5101.0"
       },
       {
         "merge": {
@@ -88811,20 +88811,20 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.interactive_ui_tests.filter",
-          "--ash-chrome-path-override=../../lacros_version_skew_tests_v104.0.5100.0/test_ash_chrome"
+          "--ash-chrome-path-override=../../lacros_version_skew_tests_v104.0.5101.0/test_ash_chrome"
         ],
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
         },
-        "name": "interactive_ui_tests Lacros version skew testing ash 104.0.5100.0",
+        "name": "interactive_ui_tests Lacros version skew testing ash 104.0.5101.0",
         "swarming": {
           "can_use_on_swarming_builders": true,
           "cipd_packages": [
             {
               "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
-              "location": "lacros_version_skew_tests_v104.0.5100.0",
-              "revision": "version:104.0.5100.0"
+              "location": "lacros_version_skew_tests_v104.0.5101.0",
+              "revision": "version:104.0.5101.0"
             }
           ],
           "dimension_sets": [
@@ -88838,7 +88838,7 @@
         },
         "test": "interactive_ui_tests",
         "test_id_prefix": "ninja://chrome/test:interactive_ui_tests/",
-        "variant_id": "Lacros version skew testing ash 104.0.5100.0"
+        "variant_id": "Lacros version skew testing ash 104.0.5101.0"
       },
       {
         "merge": {
@@ -88976,20 +88976,20 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.skew.filter",
-          "--ash-chrome-path-override=../../lacros_version_skew_tests_v104.0.5100.0/test_ash_chrome"
+          "--ash-chrome-path-override=../../lacros_version_skew_tests_v104.0.5101.0/test_ash_chrome"
         ],
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
         },
-        "name": "lacros_chrome_browsertests Lacros version skew testing ash 104.0.5100.0",
+        "name": "lacros_chrome_browsertests Lacros version skew testing ash 104.0.5101.0",
         "swarming": {
           "can_use_on_swarming_builders": true,
           "cipd_packages": [
             {
               "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
-              "location": "lacros_version_skew_tests_v104.0.5100.0",
-              "revision": "version:104.0.5100.0"
+              "location": "lacros_version_skew_tests_v104.0.5101.0",
+              "revision": "version:104.0.5101.0"
             }
           ],
           "dimension_sets": [
@@ -89002,7 +89002,7 @@
         },
         "test": "lacros_chrome_browsertests",
         "test_id_prefix": "ninja://chrome/test:lacros_chrome_browsertests/",
-        "variant_id": "Lacros version skew testing ash 104.0.5100.0"
+        "variant_id": "Lacros version skew testing ash 104.0.5101.0"
       },
       {
         "args": [
@@ -89122,20 +89122,20 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.skew.filter",
-          "--ash-chrome-path-override=../../lacros_version_skew_tests_v104.0.5100.0/test_ash_chrome"
+          "--ash-chrome-path-override=../../lacros_version_skew_tests_v104.0.5101.0/test_ash_chrome"
         ],
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
         },
-        "name": "lacros_chrome_browsertests_run_in_series Lacros version skew testing ash 104.0.5100.0",
+        "name": "lacros_chrome_browsertests_run_in_series Lacros version skew testing ash 104.0.5101.0",
         "swarming": {
           "can_use_on_swarming_builders": true,
           "cipd_packages": [
             {
               "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
-              "location": "lacros_version_skew_tests_v104.0.5100.0",
-              "revision": "version:104.0.5100.0"
+              "location": "lacros_version_skew_tests_v104.0.5101.0",
+              "revision": "version:104.0.5101.0"
             }
           ],
           "dimension_sets": [
@@ -89148,7 +89148,7 @@
         },
         "test": "lacros_chrome_browsertests_run_in_series",
         "test_id_prefix": "ninja://chrome/test:lacros_chrome_browsertests_run_in_series/",
-        "variant_id": "Lacros version skew testing ash 104.0.5100.0"
+        "variant_id": "Lacros version skew testing ash 104.0.5101.0"
       },
       {
         "merge": {
@@ -89883,20 +89883,20 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.interactive_ui_tests.filter",
-          "--ash-chrome-path-override=../../lacros_version_skew_tests_v104.0.5100.0/test_ash_chrome"
+          "--ash-chrome-path-override=../../lacros_version_skew_tests_v104.0.5101.0/test_ash_chrome"
         ],
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
         },
-        "name": "interactive_ui_tests Lacros version skew testing ash 104.0.5100.0",
+        "name": "interactive_ui_tests Lacros version skew testing ash 104.0.5101.0",
         "swarming": {
           "can_use_on_swarming_builders": true,
           "cipd_packages": [
             {
               "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
-              "location": "lacros_version_skew_tests_v104.0.5100.0",
-              "revision": "version:104.0.5100.0"
+              "location": "lacros_version_skew_tests_v104.0.5101.0",
+              "revision": "version:104.0.5101.0"
             }
           ],
           "dimension_sets": [
@@ -89909,7 +89909,7 @@
         },
         "test": "interactive_ui_tests",
         "test_id_prefix": "ninja://chrome/test:interactive_ui_tests/",
-        "variant_id": "Lacros version skew testing ash 104.0.5100.0"
+        "variant_id": "Lacros version skew testing ash 104.0.5101.0"
       }
     ]
   },
@@ -95857,7 +95857,7 @@
     "gtest_tests": [
       {
         "args": [
-          "--enable-features=PartitionAllocBackupRefPtr"
+          "--enable-features=PartitionAllocBackupRefPtr:enabled-processes/non-renderer"
         ],
         "merge": {
           "args": [],
@@ -95878,7 +95878,7 @@
       },
       {
         "args": [
-          "--enable-features=PartitionAllocBackupRefPtr"
+          "--enable-features=PartitionAllocBackupRefPtr:enabled-processes/non-renderer"
         ],
         "merge": {
           "args": [],
@@ -95900,7 +95900,7 @@
       {
         "args": [
           "angle_unittests",
-          "--enable-features=PartitionAllocBackupRefPtr"
+          "--enable-features=PartitionAllocBackupRefPtr:enabled-processes/non-renderer"
         ],
         "merge": {
           "args": [],
@@ -95922,7 +95922,7 @@
       },
       {
         "args": [
-          "--enable-features=PartitionAllocBackupRefPtr"
+          "--enable-features=PartitionAllocBackupRefPtr:enabled-processes/non-renderer"
         ],
         "merge": {
           "args": [],
@@ -95943,7 +95943,7 @@
       },
       {
         "args": [
-          "--enable-features=PartitionAllocBackupRefPtr"
+          "--enable-features=PartitionAllocBackupRefPtr:enabled-processes/non-renderer"
         ],
         "merge": {
           "args": [],
@@ -95964,7 +95964,7 @@
       },
       {
         "args": [
-          "--enable-features=PartitionAllocBackupRefPtr"
+          "--enable-features=PartitionAllocBackupRefPtr:enabled-processes/non-renderer"
         ],
         "merge": {
           "args": [],
@@ -95985,7 +95985,7 @@
       },
       {
         "args": [
-          "--enable-features=PartitionAllocBackupRefPtr"
+          "--enable-features=PartitionAllocBackupRefPtr:enabled-processes/non-renderer"
         ],
         "merge": {
           "args": [],
@@ -96006,7 +96006,7 @@
       },
       {
         "args": [
-          "--enable-features=PartitionAllocBackupRefPtr"
+          "--enable-features=PartitionAllocBackupRefPtr:enabled-processes/non-renderer"
         ],
         "merge": {
           "args": [],
@@ -96027,7 +96027,7 @@
       },
       {
         "args": [
-          "--enable-features=PartitionAllocBackupRefPtr"
+          "--enable-features=PartitionAllocBackupRefPtr:enabled-processes/non-renderer"
         ],
         "merge": {
           "args": [],
@@ -96048,7 +96048,7 @@
       },
       {
         "args": [
-          "--enable-features=PartitionAllocBackupRefPtr"
+          "--enable-features=PartitionAllocBackupRefPtr:enabled-processes/non-renderer"
         ],
         "merge": {
           "args": [],
@@ -96069,7 +96069,7 @@
       },
       {
         "args": [
-          "--enable-features=PartitionAllocBackupRefPtr"
+          "--enable-features=PartitionAllocBackupRefPtr:enabled-processes/non-renderer"
         ],
         "merge": {
           "args": [],
@@ -96091,7 +96091,7 @@
       },
       {
         "args": [
-          "--enable-features=PartitionAllocBackupRefPtr"
+          "--enable-features=PartitionAllocBackupRefPtr:enabled-processes/non-renderer"
         ],
         "merge": {
           "args": [],
@@ -96112,7 +96112,7 @@
       },
       {
         "args": [
-          "--enable-features=PartitionAllocBackupRefPtr"
+          "--enable-features=PartitionAllocBackupRefPtr:enabled-processes/non-renderer"
         ],
         "merge": {
           "args": [],
@@ -96133,7 +96133,7 @@
       },
       {
         "args": [
-          "--enable-features=PartitionAllocBackupRefPtr"
+          "--enable-features=PartitionAllocBackupRefPtr:enabled-processes/non-renderer"
         ],
         "merge": {
           "args": [],
@@ -96154,7 +96154,7 @@
       },
       {
         "args": [
-          "--enable-features=PartitionAllocBackupRefPtr",
+          "--enable-features=PartitionAllocBackupRefPtr:enabled-processes/non-renderer",
           "--test-launcher-filter-file=../../testing/buildbot/filters/win_backuprefptr_fyi.browser_tests.filter"
         ],
         "merge": {
@@ -96178,7 +96178,7 @@
       {
         "args": [
           "--gtest_filter=-*UsingRealWebcam*",
-          "--enable-features=PartitionAllocBackupRefPtr"
+          "--enable-features=PartitionAllocBackupRefPtr:enabled-processes/non-renderer"
         ],
         "merge": {
           "args": [],
@@ -96199,7 +96199,7 @@
       },
       {
         "args": [
-          "--enable-features=PartitionAllocBackupRefPtr"
+          "--enable-features=PartitionAllocBackupRefPtr:enabled-processes/non-renderer"
         ],
         "merge": {
           "args": [],
@@ -96220,7 +96220,7 @@
       },
       {
         "args": [
-          "--enable-features=PartitionAllocBackupRefPtr"
+          "--enable-features=PartitionAllocBackupRefPtr:enabled-processes/non-renderer"
         ],
         "merge": {
           "args": [],
@@ -96241,7 +96241,7 @@
       },
       {
         "args": [
-          "--enable-features=PartitionAllocBackupRefPtr"
+          "--enable-features=PartitionAllocBackupRefPtr:enabled-processes/non-renderer"
         ],
         "merge": {
           "args": [],
@@ -96262,7 +96262,7 @@
       },
       {
         "args": [
-          "--enable-features=PartitionAllocBackupRefPtr"
+          "--enable-features=PartitionAllocBackupRefPtr:enabled-processes/non-renderer"
         ],
         "merge": {
           "args": [],
@@ -96283,7 +96283,7 @@
       },
       {
         "args": [
-          "--enable-features=PartitionAllocBackupRefPtr"
+          "--enable-features=PartitionAllocBackupRefPtr:enabled-processes/non-renderer"
         ],
         "merge": {
           "args": [],
@@ -96304,7 +96304,7 @@
       },
       {
         "args": [
-          "--enable-features=PartitionAllocBackupRefPtr"
+          "--enable-features=PartitionAllocBackupRefPtr:enabled-processes/non-renderer"
         ],
         "merge": {
           "args": [],
@@ -96325,7 +96325,7 @@
       },
       {
         "args": [
-          "--enable-features=PartitionAllocBackupRefPtr"
+          "--enable-features=PartitionAllocBackupRefPtr:enabled-processes/non-renderer"
         ],
         "merge": {
           "args": [],
@@ -96346,7 +96346,7 @@
       },
       {
         "args": [
-          "--enable-features=PartitionAllocBackupRefPtr"
+          "--enable-features=PartitionAllocBackupRefPtr:enabled-processes/non-renderer"
         ],
         "merge": {
           "args": [],
@@ -96367,7 +96367,7 @@
       },
       {
         "args": [
-          "--enable-features=PartitionAllocBackupRefPtr"
+          "--enable-features=PartitionAllocBackupRefPtr:enabled-processes/non-renderer"
         ],
         "merge": {
           "args": [],
@@ -96388,7 +96388,7 @@
       },
       {
         "args": [
-          "--enable-features=PartitionAllocBackupRefPtr"
+          "--enable-features=PartitionAllocBackupRefPtr:enabled-processes/non-renderer"
         ],
         "merge": {
           "args": [],
@@ -96409,7 +96409,7 @@
       },
       {
         "args": [
-          "--enable-features=PartitionAllocBackupRefPtr"
+          "--enable-features=PartitionAllocBackupRefPtr:enabled-processes/non-renderer"
         ],
         "merge": {
           "args": [],
@@ -96431,7 +96431,7 @@
       },
       {
         "args": [
-          "--enable-features=PartitionAllocBackupRefPtr"
+          "--enable-features=PartitionAllocBackupRefPtr:enabled-processes/non-renderer"
         ],
         "merge": {
           "args": [],
@@ -96452,7 +96452,7 @@
       },
       {
         "args": [
-          "--enable-features=PartitionAllocBackupRefPtr"
+          "--enable-features=PartitionAllocBackupRefPtr:enabled-processes/non-renderer"
         ],
         "merge": {
           "args": [],
@@ -96473,7 +96473,7 @@
       },
       {
         "args": [
-          "--enable-features=PartitionAllocBackupRefPtr"
+          "--enable-features=PartitionAllocBackupRefPtr:enabled-processes/non-renderer"
         ],
         "merge": {
           "args": [],
@@ -96494,7 +96494,7 @@
       },
       {
         "args": [
-          "--enable-features=PartitionAllocBackupRefPtr"
+          "--enable-features=PartitionAllocBackupRefPtr:enabled-processes/non-renderer"
         ],
         "merge": {
           "args": [],
@@ -96515,7 +96515,7 @@
       },
       {
         "args": [
-          "--enable-features=PartitionAllocBackupRefPtr"
+          "--enable-features=PartitionAllocBackupRefPtr:enabled-processes/non-renderer"
         ],
         "merge": {
           "args": [],
@@ -96536,7 +96536,7 @@
       },
       {
         "args": [
-          "--enable-features=PartitionAllocBackupRefPtr"
+          "--enable-features=PartitionAllocBackupRefPtr:enabled-processes/non-renderer"
         ],
         "merge": {
           "args": [],
@@ -96557,7 +96557,7 @@
       },
       {
         "args": [
-          "--enable-features=PartitionAllocBackupRefPtr"
+          "--enable-features=PartitionAllocBackupRefPtr:enabled-processes/non-renderer"
         ],
         "merge": {
           "args": [],
@@ -96578,7 +96578,7 @@
       },
       {
         "args": [
-          "--enable-features=PartitionAllocBackupRefPtr"
+          "--enable-features=PartitionAllocBackupRefPtr:enabled-processes/non-renderer"
         ],
         "merge": {
           "args": [],
@@ -96599,7 +96599,7 @@
       },
       {
         "args": [
-          "--enable-features=PartitionAllocBackupRefPtr"
+          "--enable-features=PartitionAllocBackupRefPtr:enabled-processes/non-renderer"
         ],
         "merge": {
           "args": [],
@@ -96620,7 +96620,7 @@
       },
       {
         "args": [
-          "--enable-features=PartitionAllocBackupRefPtr"
+          "--enable-features=PartitionAllocBackupRefPtr:enabled-processes/non-renderer"
         ],
         "merge": {
           "args": [],
@@ -96641,7 +96641,7 @@
       },
       {
         "args": [
-          "--enable-features=PartitionAllocBackupRefPtr"
+          "--enable-features=PartitionAllocBackupRefPtr:enabled-processes/non-renderer"
         ],
         "merge": {
           "args": [],
@@ -96662,7 +96662,7 @@
       },
       {
         "args": [
-          "--enable-features=PartitionAllocBackupRefPtr"
+          "--enable-features=PartitionAllocBackupRefPtr:enabled-processes/non-renderer"
         ],
         "merge": {
           "args": [],
@@ -96683,7 +96683,7 @@
       },
       {
         "args": [
-          "--enable-features=PartitionAllocBackupRefPtr"
+          "--enable-features=PartitionAllocBackupRefPtr:enabled-processes/non-renderer"
         ],
         "merge": {
           "args": [],
@@ -96704,7 +96704,7 @@
       },
       {
         "args": [
-          "--enable-features=PartitionAllocBackupRefPtr"
+          "--enable-features=PartitionAllocBackupRefPtr:enabled-processes/non-renderer"
         ],
         "merge": {
           "args": [],
@@ -96725,7 +96725,7 @@
       },
       {
         "args": [
-          "--enable-features=PartitionAllocBackupRefPtr"
+          "--enable-features=PartitionAllocBackupRefPtr:enabled-processes/non-renderer"
         ],
         "merge": {
           "args": [],
@@ -96746,7 +96746,7 @@
       },
       {
         "args": [
-          "--enable-features=PartitionAllocBackupRefPtr"
+          "--enable-features=PartitionAllocBackupRefPtr:enabled-processes/non-renderer"
         ],
         "merge": {
           "args": [],
@@ -96767,7 +96767,7 @@
       },
       {
         "args": [
-          "--enable-features=PartitionAllocBackupRefPtr"
+          "--enable-features=PartitionAllocBackupRefPtr:enabled-processes/non-renderer"
         ],
         "merge": {
           "args": [],
@@ -96788,7 +96788,7 @@
       },
       {
         "args": [
-          "--enable-features=PartitionAllocBackupRefPtr"
+          "--enable-features=PartitionAllocBackupRefPtr:enabled-processes/non-renderer"
         ],
         "merge": {
           "args": [],
@@ -96809,7 +96809,7 @@
       },
       {
         "args": [
-          "--enable-features=PartitionAllocBackupRefPtr"
+          "--enable-features=PartitionAllocBackupRefPtr:enabled-processes/non-renderer"
         ],
         "merge": {
           "args": [],
@@ -96830,7 +96830,7 @@
       },
       {
         "args": [
-          "--enable-features=PartitionAllocBackupRefPtr"
+          "--enable-features=PartitionAllocBackupRefPtr:enabled-processes/non-renderer"
         ],
         "merge": {
           "args": [],
@@ -96851,7 +96851,7 @@
       },
       {
         "args": [
-          "--enable-features=PartitionAllocBackupRefPtr"
+          "--enable-features=PartitionAllocBackupRefPtr:enabled-processes/non-renderer"
         ],
         "merge": {
           "args": [],
@@ -96872,7 +96872,7 @@
       },
       {
         "args": [
-          "--enable-features=PartitionAllocBackupRefPtr"
+          "--enable-features=PartitionAllocBackupRefPtr:enabled-processes/non-renderer"
         ],
         "merge": {
           "args": [],
@@ -96893,7 +96893,7 @@
       },
       {
         "args": [
-          "--enable-features=PartitionAllocBackupRefPtr"
+          "--enable-features=PartitionAllocBackupRefPtr:enabled-processes/non-renderer"
         ],
         "merge": {
           "args": [],
@@ -96914,7 +96914,7 @@
       },
       {
         "args": [
-          "--enable-features=PartitionAllocBackupRefPtr"
+          "--enable-features=PartitionAllocBackupRefPtr:enabled-processes/non-renderer"
         ],
         "merge": {
           "args": [],
@@ -96935,7 +96935,7 @@
       },
       {
         "args": [
-          "--enable-features=PartitionAllocBackupRefPtr"
+          "--enable-features=PartitionAllocBackupRefPtr:enabled-processes/non-renderer"
         ],
         "merge": {
           "args": [],
@@ -96957,7 +96957,7 @@
       },
       {
         "args": [
-          "--enable-features=PartitionAllocBackupRefPtr"
+          "--enable-features=PartitionAllocBackupRefPtr:enabled-processes/non-renderer"
         ],
         "merge": {
           "args": [],
@@ -96979,7 +96979,7 @@
       },
       {
         "args": [
-          "--enable-features=PartitionAllocBackupRefPtr"
+          "--enable-features=PartitionAllocBackupRefPtr:enabled-processes/non-renderer"
         ],
         "merge": {
           "args": [],
@@ -97000,7 +97000,7 @@
       },
       {
         "args": [
-          "--enable-features=PartitionAllocBackupRefPtr"
+          "--enable-features=PartitionAllocBackupRefPtr:enabled-processes/non-renderer"
         ],
         "merge": {
           "args": [],
@@ -97021,7 +97021,7 @@
       },
       {
         "args": [
-          "--enable-features=PartitionAllocBackupRefPtr"
+          "--enable-features=PartitionAllocBackupRefPtr:enabled-processes/non-renderer"
         ],
         "merge": {
           "args": [],
@@ -97042,7 +97042,7 @@
       },
       {
         "args": [
-          "--enable-features=PartitionAllocBackupRefPtr"
+          "--enable-features=PartitionAllocBackupRefPtr:enabled-processes/non-renderer"
         ],
         "merge": {
           "args": [],
@@ -97063,7 +97063,7 @@
       },
       {
         "args": [
-          "--enable-features=PartitionAllocBackupRefPtr"
+          "--enable-features=PartitionAllocBackupRefPtr:enabled-processes/non-renderer"
         ],
         "merge": {
           "args": [],
@@ -97084,7 +97084,7 @@
       },
       {
         "args": [
-          "--enable-features=PartitionAllocBackupRefPtr"
+          "--enable-features=PartitionAllocBackupRefPtr:enabled-processes/non-renderer"
         ],
         "merge": {
           "args": [],
@@ -97105,7 +97105,7 @@
       },
       {
         "args": [
-          "--enable-features=PartitionAllocBackupRefPtr"
+          "--enable-features=PartitionAllocBackupRefPtr:enabled-processes/non-renderer"
         ],
         "merge": {
           "args": [],
@@ -97126,7 +97126,7 @@
       },
       {
         "args": [
-          "--enable-features=PartitionAllocBackupRefPtr"
+          "--enable-features=PartitionAllocBackupRefPtr:enabled-processes/non-renderer"
         ],
         "merge": {
           "args": [],
@@ -97147,7 +97147,7 @@
       },
       {
         "args": [
-          "--enable-features=PartitionAllocBackupRefPtr"
+          "--enable-features=PartitionAllocBackupRefPtr:enabled-processes/non-renderer"
         ],
         "merge": {
           "args": [],
@@ -97168,7 +97168,7 @@
       },
       {
         "args": [
-          "--enable-features=PartitionAllocBackupRefPtr"
+          "--enable-features=PartitionAllocBackupRefPtr:enabled-processes/non-renderer"
         ],
         "merge": {
           "args": [],
@@ -97189,7 +97189,7 @@
       },
       {
         "args": [
-          "--enable-features=PartitionAllocBackupRefPtr"
+          "--enable-features=PartitionAllocBackupRefPtr:enabled-processes/non-renderer"
         ],
         "merge": {
           "args": [],
@@ -97210,7 +97210,7 @@
       },
       {
         "args": [
-          "--enable-features=PartitionAllocBackupRefPtr"
+          "--enable-features=PartitionAllocBackupRefPtr:enabled-processes/non-renderer"
         ],
         "merge": {
           "args": [],
@@ -97231,7 +97231,7 @@
       },
       {
         "args": [
-          "--enable-features=PartitionAllocBackupRefPtr"
+          "--enable-features=PartitionAllocBackupRefPtr:enabled-processes/non-renderer"
         ],
         "merge": {
           "args": [],
@@ -97252,7 +97252,7 @@
       },
       {
         "args": [
-          "--enable-features=PartitionAllocBackupRefPtr"
+          "--enable-features=PartitionAllocBackupRefPtr:enabled-processes/non-renderer"
         ],
         "merge": {
           "args": [],
@@ -97273,7 +97273,7 @@
       },
       {
         "args": [
-          "--enable-features=PartitionAllocBackupRefPtr"
+          "--enable-features=PartitionAllocBackupRefPtr:enabled-processes/non-renderer"
         ],
         "merge": {
           "args": [],
@@ -97294,7 +97294,7 @@
       },
       {
         "args": [
-          "--enable-features=PartitionAllocBackupRefPtr"
+          "--enable-features=PartitionAllocBackupRefPtr:enabled-processes/non-renderer"
         ],
         "merge": {
           "args": [],
@@ -97315,7 +97315,7 @@
       },
       {
         "args": [
-          "--enable-features=PartitionAllocBackupRefPtr"
+          "--enable-features=PartitionAllocBackupRefPtr:enabled-processes/non-renderer"
         ],
         "merge": {
           "args": [],
@@ -97336,7 +97336,7 @@
       },
       {
         "args": [
-          "--enable-features=PartitionAllocBackupRefPtr"
+          "--enable-features=PartitionAllocBackupRefPtr:enabled-processes/non-renderer"
         ],
         "merge": {
           "args": [],
@@ -97357,7 +97357,7 @@
       },
       {
         "args": [
-          "--enable-features=PartitionAllocBackupRefPtr"
+          "--enable-features=PartitionAllocBackupRefPtr:enabled-processes/non-renderer"
         ],
         "merge": {
           "args": [],
@@ -97379,7 +97379,7 @@
       },
       {
         "args": [
-          "--enable-features=PartitionAllocBackupRefPtr"
+          "--enable-features=PartitionAllocBackupRefPtr:enabled-processes/non-renderer"
         ],
         "merge": {
           "args": [],
@@ -97400,7 +97400,7 @@
       },
       {
         "args": [
-          "--enable-features=PartitionAllocBackupRefPtr"
+          "--enable-features=PartitionAllocBackupRefPtr:enabled-processes/non-renderer"
         ],
         "merge": {
           "args": [],
@@ -97421,7 +97421,7 @@
       },
       {
         "args": [
-          "--enable-features=PartitionAllocBackupRefPtr"
+          "--enable-features=PartitionAllocBackupRefPtr:enabled-processes/non-renderer"
         ],
         "merge": {
           "args": [],
@@ -97442,7 +97442,7 @@
       },
       {
         "args": [
-          "--enable-features=PartitionAllocBackupRefPtr"
+          "--enable-features=PartitionAllocBackupRefPtr:enabled-processes/non-renderer"
         ],
         "merge": {
           "args": [],
@@ -97463,7 +97463,7 @@
       },
       {
         "args": [
-          "--enable-features=PartitionAllocBackupRefPtr"
+          "--enable-features=PartitionAllocBackupRefPtr:enabled-processes/non-renderer"
         ],
         "merge": {
           "args": [],
@@ -97485,7 +97485,7 @@
       },
       {
         "args": [
-          "--enable-features=PartitionAllocBackupRefPtr"
+          "--enable-features=PartitionAllocBackupRefPtr:enabled-processes/non-renderer"
         ],
         "merge": {
           "args": [],
@@ -97506,7 +97506,7 @@
       },
       {
         "args": [
-          "--enable-features=PartitionAllocBackupRefPtr"
+          "--enable-features=PartitionAllocBackupRefPtr:enabled-processes/non-renderer"
         ],
         "merge": {
           "args": [],
@@ -97527,7 +97527,7 @@
       },
       {
         "args": [
-          "--enable-features=PartitionAllocBackupRefPtr"
+          "--enable-features=PartitionAllocBackupRefPtr:enabled-processes/non-renderer"
         ],
         "merge": {
           "args": [],
@@ -97548,7 +97548,7 @@
       },
       {
         "args": [
-          "--enable-features=PartitionAllocBackupRefPtr"
+          "--enable-features=PartitionAllocBackupRefPtr:enabled-processes/non-renderer"
         ],
         "merge": {
           "args": [],
@@ -97569,7 +97569,7 @@
       },
       {
         "args": [
-          "--enable-features=PartitionAllocBackupRefPtr"
+          "--enable-features=PartitionAllocBackupRefPtr:enabled-processes/non-renderer"
         ],
         "merge": {
           "args": [],
@@ -97590,7 +97590,7 @@
       },
       {
         "args": [
-          "--enable-features=PartitionAllocBackupRefPtr"
+          "--enable-features=PartitionAllocBackupRefPtr:enabled-processes/non-renderer"
         ],
         "merge": {
           "args": [],
@@ -97611,7 +97611,7 @@
       },
       {
         "args": [
-          "--enable-features=PartitionAllocBackupRefPtr"
+          "--enable-features=PartitionAllocBackupRefPtr:enabled-processes/non-renderer"
         ],
         "merge": {
           "args": [],
@@ -97632,7 +97632,7 @@
       },
       {
         "args": [
-          "--enable-features=PartitionAllocBackupRefPtr"
+          "--enable-features=PartitionAllocBackupRefPtr:enabled-processes/non-renderer"
         ],
         "merge": {
           "args": [],
@@ -97653,7 +97653,7 @@
       },
       {
         "args": [
-          "--enable-features=PartitionAllocBackupRefPtr"
+          "--enable-features=PartitionAllocBackupRefPtr:enabled-processes/non-renderer"
         ],
         "merge": {
           "args": [],
@@ -97677,7 +97677,7 @@
           "--test-launcher-timeout=90000",
           "--ui-test-action-max-timeout=45000",
           "--ui-test-action-timeout=40000",
-          "--enable-features=PartitionAllocBackupRefPtr"
+          "--enable-features=PartitionAllocBackupRefPtr:enabled-processes/non-renderer"
         ],
         "merge": {
           "args": [],
@@ -97703,7 +97703,7 @@
           "--ui-test-action-max-timeout=45000",
           "--ui-test-action-timeout=40000",
           "--ui-test-action-timeout=40000",
-          "--enable-features=PartitionAllocBackupRefPtr"
+          "--enable-features=PartitionAllocBackupRefPtr:enabled-processes/non-renderer"
         ],
         "merge": {
           "args": [],
@@ -97724,7 +97724,7 @@
       },
       {
         "args": [
-          "--enable-features=PartitionAllocBackupRefPtr"
+          "--enable-features=PartitionAllocBackupRefPtr:enabled-processes/non-renderer"
         ],
         "merge": {
           "args": [],
@@ -97745,7 +97745,7 @@
       },
       {
         "args": [
-          "--enable-features=PartitionAllocBackupRefPtr"
+          "--enable-features=PartitionAllocBackupRefPtr:enabled-processes/non-renderer"
         ],
         "merge": {
           "args": [],
@@ -97766,7 +97766,7 @@
       },
       {
         "args": [
-          "--enable-features=PartitionAllocBackupRefPtr"
+          "--enable-features=PartitionAllocBackupRefPtr:enabled-processes/non-renderer"
         ],
         "merge": {
           "args": [],
@@ -97787,7 +97787,7 @@
       },
       {
         "args": [
-          "--enable-features=PartitionAllocBackupRefPtr"
+          "--enable-features=PartitionAllocBackupRefPtr:enabled-processes/non-renderer"
         ],
         "merge": {
           "args": [],
@@ -97808,7 +97808,7 @@
       },
       {
         "args": [
-          "--enable-features=PartitionAllocBackupRefPtr"
+          "--enable-features=PartitionAllocBackupRefPtr:enabled-processes/non-renderer"
         ],
         "merge": {
           "args": [],
@@ -97829,7 +97829,7 @@
       },
       {
         "args": [
-          "--enable-features=PartitionAllocBackupRefPtr"
+          "--enable-features=PartitionAllocBackupRefPtr:enabled-processes/non-renderer"
         ],
         "merge": {
           "args": [],
@@ -97850,7 +97850,7 @@
       },
       {
         "args": [
-          "--enable-features=PartitionAllocBackupRefPtr"
+          "--enable-features=PartitionAllocBackupRefPtr:enabled-processes/non-renderer"
         ],
         "merge": {
           "args": [],
@@ -97871,7 +97871,7 @@
       },
       {
         "args": [
-          "--enable-features=PartitionAllocBackupRefPtr"
+          "--enable-features=PartitionAllocBackupRefPtr:enabled-processes/non-renderer"
         ],
         "merge": {
           "args": [],
@@ -97892,7 +97892,7 @@
       },
       {
         "args": [
-          "--enable-features=PartitionAllocBackupRefPtr"
+          "--enable-features=PartitionAllocBackupRefPtr:enabled-processes/non-renderer"
         ],
         "merge": {
           "args": [],
@@ -97913,7 +97913,7 @@
       },
       {
         "args": [
-          "--enable-features=PartitionAllocBackupRefPtr"
+          "--enable-features=PartitionAllocBackupRefPtr:enabled-processes/non-renderer"
         ],
         "merge": {
           "args": [],
@@ -97934,7 +97934,7 @@
       },
       {
         "args": [
-          "--enable-features=PartitionAllocBackupRefPtr"
+          "--enable-features=PartitionAllocBackupRefPtr:enabled-processes/non-renderer"
         ],
         "merge": {
           "args": [],
@@ -97959,7 +97959,7 @@
     "gtest_tests": [
       {
         "args": [
-          "--enable-features=PartitionAllocBackupRefPtr"
+          "--enable-features=PartitionAllocBackupRefPtr:enabled-processes/non-renderer"
         ],
         "merge": {
           "args": [],
@@ -97980,7 +97980,7 @@
       },
       {
         "args": [
-          "--enable-features=PartitionAllocBackupRefPtr"
+          "--enable-features=PartitionAllocBackupRefPtr:enabled-processes/non-renderer"
         ],
         "merge": {
           "args": [],
@@ -98001,7 +98001,7 @@
       },
       {
         "args": [
-          "--enable-features=PartitionAllocBackupRefPtr"
+          "--enable-features=PartitionAllocBackupRefPtr:enabled-processes/non-renderer"
         ],
         "merge": {
           "args": [],
@@ -98022,7 +98022,7 @@
       },
       {
         "args": [
-          "--enable-features=PartitionAllocBackupRefPtr"
+          "--enable-features=PartitionAllocBackupRefPtr:enabled-processes/non-renderer"
         ],
         "merge": {
           "args": [],
@@ -98044,7 +98044,7 @@
       },
       {
         "args": [
-          "--enable-features=PartitionAllocBackupRefPtr"
+          "--enable-features=PartitionAllocBackupRefPtr:enabled-processes/non-renderer"
         ],
         "merge": {
           "args": [],
@@ -98065,7 +98065,7 @@
       },
       {
         "args": [
-          "--enable-features=PartitionAllocBackupRefPtr"
+          "--enable-features=PartitionAllocBackupRefPtr:enabled-processes/non-renderer"
         ],
         "merge": {
           "args": [],
@@ -98086,7 +98086,7 @@
       },
       {
         "args": [
-          "--enable-features=PartitionAllocBackupRefPtr"
+          "--enable-features=PartitionAllocBackupRefPtr:enabled-processes/non-renderer"
         ],
         "merge": {
           "args": [],
@@ -98107,7 +98107,7 @@
       },
       {
         "args": [
-          "--enable-features=PartitionAllocBackupRefPtr"
+          "--enable-features=PartitionAllocBackupRefPtr:enabled-processes/non-renderer"
         ],
         "merge": {
           "args": [],
@@ -98128,7 +98128,7 @@
       },
       {
         "args": [
-          "--enable-features=PartitionAllocBackupRefPtr"
+          "--enable-features=PartitionAllocBackupRefPtr:enabled-processes/non-renderer"
         ],
         "merge": {
           "args": [],
@@ -98149,7 +98149,7 @@
       },
       {
         "args": [
-          "--enable-features=PartitionAllocBackupRefPtr"
+          "--enable-features=PartitionAllocBackupRefPtr:enabled-processes/non-renderer"
         ],
         "merge": {
           "args": [],
diff --git a/testing/buildbot/chromium.perf.json b/testing/buildbot/chromium.perf.json
index 58a4c7f..6b436bc 100644
--- a/testing/buildbot/chromium.perf.json
+++ b/testing/buildbot/chromium.perf.json
@@ -2366,48 +2366,6 @@
       "script": "//tools/perf/process_perf_results.py"
     }
   },
-  "win32-builder-perf": {
-    "additional_compile_targets": [
-      "chromedriver",
-      "chromium_builder_perf"
-    ],
-    "isolated_scripts": [
-      {
-        "args": [],
-        "isolate_name": "chrome_sizes",
-        "merge": {
-          "script": "//tools/perf/process_perf_results.py"
-        },
-        "name": "chrome_sizes",
-        "override_compile_targets": [
-          "chrome_sizes"
-        ],
-        "resultdb": {
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "cpu": "x86",
-              "os": "Windows",
-              "pool": "chrome.tests"
-            }
-          ],
-          "expiration": 7200,
-          "hard_timeout": 21600,
-          "ignore_task_failure": false,
-          "io_timeout": 21600,
-          "service_account": "chrome-tester@chops-service-accounts.iam.gserviceaccount.com"
-        }
-      }
-    ]
-  },
-  "win32-builder-perf-pgo": {
-    "additional_compile_targets": [
-      "chromium_builder_perf"
-    ]
-  },
   "win64-builder-perf": {
     "additional_compile_targets": [
       "chromedriver",
diff --git a/testing/buildbot/filters/linux-chromeos.browser_tests.require_lacros.filter b/testing/buildbot/filters/linux-chromeos.browser_tests.require_lacros.filter
index ed904a6..4b46e2f4 100644
--- a/testing/buildbot/filters/linux-chromeos.browser_tests.require_lacros.filter
+++ b/testing/buildbot/filters/linux-chromeos.browser_tests.require_lacros.filter
@@ -29,6 +29,12 @@
 -SwitchAccessFocusRingManagerTest.GroupFocus
 -SwitchAccessItemScanManagerTest.*
 -SwitchAccessPointScanManagerTest.PointScanLeftClick
+-BrowserAppShelfControllerBrowserTest.*
+-ChromeVoxAutoScrollHandlerTest.RecyclerViewByPredicate
+-DockedMagnifierVirtualKeyboardTest.WelcomeScreen
+-ChromeVoxTutorialTest.Gestures
+-ChromeVoxTutorialTest.ExitButtonTest
+-ChromeVoxTutorialTest.EscapeTest
 # Flaky (crbug.com/1268842).
 -ChromeVoxBackgroundTest.GestureOnPopUpButton
 -ChromeVoxBackgroundTest.HitTestOnExoSurface
diff --git a/testing/buildbot/test_suites.pyl b/testing/buildbot/test_suites.pyl
index 255d14fd..d34029f9 100644
--- a/testing/buildbot/test_suites.pyl
+++ b/testing/buildbot/test_suites.pyl
@@ -4037,9 +4037,6 @@
           '--test-launcher-filter-file=../../testing/buildbot/filters/linux-chromeos.browser_tests.require_lacros.filter',
           '--lacros-chrome-path=lacros_clang_x64',
         ],
-        # TODO(crbug.com/1195974): Decides whether we want to run this on CQ.
-        # This target is heavy, which currently takes ~1 hour to run.
-        'ci_only': True,
         'test': 'browser_tests',
         'swarming': {
           'shards': 4,
diff --git a/testing/buildbot/variants.pyl b/testing/buildbot/variants.pyl
index 5ae1c622..13856aa 100644
--- a/testing/buildbot/variants.pyl
+++ b/testing/buildbot/variants.pyl
@@ -22,15 +22,15 @@
   },
   'LACROS_VERSION_SKEW_CANARY': {
     'args': [
-      '--ash-chrome-path-override=../../lacros_version_skew_tests_v104.0.5100.0/test_ash_chrome',
+      '--ash-chrome-path-override=../../lacros_version_skew_tests_v104.0.5101.0/test_ash_chrome',
     ],
-    'identifier': 'Lacros version skew testing ash 104.0.5100.0',
+    'identifier': 'Lacros version skew testing ash 104.0.5101.0',
     'swarming': {
       'cipd_packages': [
         {
           'cipd_package': 'chromium/testing/linux-ash-chromium/x86_64/ash.zip',
-          'location': 'lacros_version_skew_tests_v104.0.5100.0',
-          'revision': 'version:104.0.5100.0',
+          'location': 'lacros_version_skew_tests_v104.0.5101.0',
+          'revision': 'version:104.0.5101.0',
         },
       ],
     },
diff --git a/testing/buildbot/waterfalls.pyl b/testing/buildbot/waterfalls.pyl
index e663c23..00cd7a8 100644
--- a/testing/buildbot/waterfalls.pyl
+++ b/testing/buildbot/waterfalls.pyl
@@ -2940,7 +2940,7 @@
         ],
         'os_type': 'android',
         'gtest_args': [
-          '--enable-features=PartitionAllocBackupRefPtr',
+          '--enable-features=PartitionAllocBackupRefPtr:enabled-processes/non-renderer',
         ],
       },
       'android-backuprefptr-arm64-fyi-rel': {
@@ -2954,7 +2954,7 @@
         ],
         'os_type': 'android',
         'gtest_args': [
-          '--enable-features=PartitionAllocBackupRefPtr',
+          '--enable-features=PartitionAllocBackupRefPtr:enabled-processes/non-renderer',
         ],
       },
       'android-code-coverage': {
@@ -3755,7 +3755,7 @@
           'x86-64',
         ],
         'gtest_args': [
-          '--enable-features=PartitionAllocBackupRefPtr',
+          '--enable-features=PartitionAllocBackupRefPtr:enabled-processes/non-renderer',
         ],
       },
       'win-backuprefptr-x86-fyi-rel': {
@@ -3767,7 +3767,7 @@
           'x86-64',
         ],
         'gtest_args': [
-          '--enable-features=PartitionAllocBackupRefPtr',
+          '--enable-features=PartitionAllocBackupRefPtr:enabled-processes/non-renderer',
         ],
       },
       'win-fieldtrial-rel': {
diff --git a/testing/variations/fieldtrial_testing_config.json b/testing/variations/fieldtrial_testing_config.json
index e5fc4e4..b437576 100644
--- a/testing/variations/fieldtrial_testing_config.json
+++ b/testing/variations/fieldtrial_testing_config.json
@@ -250,21 +250,6 @@
             ]
         }
     ],
-    "AndroidMessagesNotificationBlocked": [
-        {
-            "platforms": [
-                "android"
-            ],
-            "experiments": [
-                {
-                    "name": "Enabled_2022-03-02",
-                    "enable_features": [
-                        "MessagesForAndroidNotificationBlocked"
-                    ]
-                }
-            ]
-        }
-    ],
     "AndroidMessagesPWAInstall": [
         {
             "platforms": [
@@ -2753,28 +2738,6 @@
             ]
         }
     ],
-    "ContentSuggestionsHeaderMigration": [
-        {
-            "platforms": [
-                "ios"
-            ],
-            "experiments": [
-                {
-                    "name": "Enabled_Header_Control_UIViewController_20220412",
-                    "enable_features": [
-                        "ContentSuggestionsHeaderMigration"
-                    ]
-                },
-                {
-                    "name": "Enabled_Header_UIViewController_20220412",
-                    "enable_features": [
-                        "ContentSuggestionsHeaderMigration",
-                        "ContentSuggestionsUIViewControllerMigration"
-                    ]
-                }
-            ]
-        }
-    ],
     "ContextMenuGoogleLensChip": [
         {
             "platforms": [
@@ -7605,21 +7568,6 @@
             ]
         }
     ],
-    "SingleCellContentSuggestions": [
-        {
-            "platforms": [
-                "ios"
-            ],
-            "experiments": [
-                {
-                    "name": "SingleCellContentSuggestions",
-                    "enable_features": [
-                        "SingleCellContentSuggestions"
-                    ]
-                }
-            ]
-        }
-    ],
     "SingleNTPRemoveExtraNTPs": [
         {
             "platforms": [
@@ -8846,6 +8794,27 @@
             ]
         }
     ],
+    "ViewportHeightClientHintHeader": [
+        {
+            "platforms": [
+                "windows",
+                "mac",
+                "chromeos",
+                "chromeos_lacros",
+                "linux",
+                "android",
+                "android_webview"
+            ],
+            "experiments": [
+                {
+                    "name": "Enabled",
+                    "enable_features": [
+                        "ViewportHeightClientHintHeader"
+                    ]
+                }
+            ]
+        }
+    ],
     "Vulkan": [
         {
             "platforms": [
diff --git a/third_party/blink/public/mojom/file_system_access/file_system_access_file_delegate_host.mojom b/third_party/blink/public/mojom/file_system_access/file_system_access_file_delegate_host.mojom
index 4c8f802..1fc6fff0 100644
--- a/third_party/blink/public/mojom/file_system_access/file_system_access_file_delegate_host.mojom
+++ b/third_party/blink/public/mojom/file_system_access/file_system_access_file_delegate_host.mojom
@@ -14,20 +14,24 @@
 // behavior of the corresponding methods of the SyncAccessHandle.
 interface FileSystemAccessFileDelegateHost {
   // Read up to `bytes_to_read` from the associated file starting at `offset`.
-  // If the read is successful, returns a buffer with the read data, the number
-  // of bytes read (which may be less than the length of `data`), and an `error`
-  //  of `base::File::Error::FILE_OK`.
+  // Ideally these would each be unsigned, but the backend code expects signed
+  // ints. The FileSystemAccessFileDelegateHostImpl will report a bad message if
+  // either of these values is negative. If the read is successful, returns a
+  // buffer with the read data, the number of bytes read (which may be less than
+  // the length of `data`), and an `error` of `base::File::Error::FILE_OK`.
   // `data` is null and `bytes_to_read` is 0 if the read fails.
   [Sync]
-  Read(uint64 offset, uint64 bytes_to_read) => (mojo_base.mojom.BigBuffer? data,
+  Read(int64 offset, int32 bytes_to_read) => (mojo_base.mojom.BigBuffer? data,
                                               mojo_base.mojom.FileError error,
                                               int32 bytes_read);
 
-  // Writes `data` to the associated file starting at `offset`.
-  // Returns the number of bytes written and a file error, which is
+  // Writes `data` to the associated file starting at `offset`. Ideally `offset`
+  // would be a uint64, but the backend code expects an int64. The
+  // FileSystemAccessFileDelegateHostImpl will report a bad message if `offset`
+  // is negative. Returns the number of bytes written and a file error, which is
   // `base::File::Error::FILE_OK` if the write completed successfully.
   [Sync]
-  Write(uint64 offset, handle<data_pipe_consumer> data) =>
+  Write(int64 offset, handle<data_pipe_consumer> data) =>
       (mojo_base.mojom.FileError error, int32 bytes_written);
 
   // Returns the `length` of the associated file and a file error, which is
@@ -36,8 +40,10 @@
   GetLength() => (mojo_base.mojom.FileError error, uint64 length);
 
   // Truncates a file to `length`. If `length` is larger than the file size, the
-  // file will be extended with null bytes.
-  // Returns a file error, which is `base::File::Error::FILE_OK` if the
-  // operation completed successfully.
-  SetLength(uint64 length) => (mojo_base.mojom.FileError error);
+  // file will be extended with null bytes. Ideally `length` would be a uint64,
+  // but the backend code expects an int64. The
+  // FileSystemAccessFileDelegateHostImpl will report a bad message if `length`
+  // is negative. Returns a file error, which is `base::File::Error::FILE_OK` if
+  // the operation completed successfully.
+  SetLength(int64 length) => (mojo_base.mojom.FileError error);
 };
diff --git a/third_party/blink/renderer/core/css/css_math_expression_node.cc b/third_party/blink/renderer/core/css/css_math_expression_node.cc
index 01e6d95..0d56ece 100644
--- a/third_party/blink/renderer/core/css/css_math_expression_node.cc
+++ b/third_party/blink/renderer/core/css/css_math_expression_node.cc
@@ -1035,12 +1035,80 @@
          base::ValuesEquivalent(fallback_, other_anchor->fallback_);
 }
 
+namespace {
+
+AnchorValue CSSValueIDToAnchorValueEnum(CSSValueID value) {
+  switch (value) {
+    case CSSValueID::kTop:
+      return AnchorValue::kTop;
+    case CSSValueID::kLeft:
+      return AnchorValue::kLeft;
+    case CSSValueID::kRight:
+      return AnchorValue::kRight;
+    case CSSValueID::kBottom:
+      return AnchorValue::kBottom;
+    case CSSValueID::kStart:
+      return AnchorValue::kStart;
+    case CSSValueID::kEnd:
+      return AnchorValue::kEnd;
+    case CSSValueID::kSelfStart:
+      return AnchorValue::kSelfStart;
+    case CSSValueID::kSelfEnd:
+      return AnchorValue::kSelfEnd;
+    case CSSValueID::kCenter:
+      return AnchorValue::kCenter;
+    default:
+      NOTREACHED();
+      return AnchorValue::kCenter;
+  }
+}
+
+AnchorSizeValue CSSValueIDToAnchorSizeValueEnum(CSSValueID value) {
+  switch (value) {
+    case CSSValueID::kWidth:
+      return AnchorSizeValue::kWidth;
+    case CSSValueID::kHeight:
+      return AnchorSizeValue::kHeight;
+    case CSSValueID::kBlock:
+      return AnchorSizeValue::kBlock;
+    case CSSValueID::kInline:
+      return AnchorSizeValue::kInline;
+    case CSSValueID::kSelfBlock:
+      return AnchorSizeValue::kSelfBlock;
+    case CSSValueID::kSelfInline:
+      return AnchorSizeValue::kSelfInline;
+    default:
+      NOTREACHED();
+      return AnchorSizeValue::kSelfInline;
+  }
+}
+
+}  // namespace
+
 scoped_refptr<const CalculationExpressionNode>
 CSSMathExpressionAnchorQuery::ToCalculationExpression(
-    const CSSLengthResolver&) const {
-  // TODO(crbug.com/1309178): Implement.
-  return base::MakeRefCounted<CalculationExpressionPixelsAndPercentNode>(
-      PixelsAndPercent(0, 0));
+    const CSSLengthResolver& length_resolver) const {
+  AtomicString anchor_name = anchor_name_->Value();
+  Length fallback = fallback_ ? fallback_->ConvertToLength(length_resolver)
+                              : Length::Fixed(0);
+
+  if (type_ == CSSAnchorQueryType::kAnchor) {
+    if (const CSSPrimitiveValue* percentage =
+            DynamicTo<CSSPrimitiveValue>(*value_)) {
+      DCHECK(percentage->IsPercentage());
+      return CalculationExpressionAnchorQueryNode::CreateAnchorPercentage(
+          anchor_name, percentage->GetFloatValue(), fallback);
+    }
+    const CSSIdentifierValue& side = To<CSSIdentifierValue>(*value_);
+    return CalculationExpressionAnchorQueryNode::CreateAnchor(
+        anchor_name, CSSValueIDToAnchorValueEnum(side.GetValueID()), fallback);
+  }
+
+  DCHECK_EQ(type_, CSSAnchorQueryType::kAnchorSize);
+  const CSSIdentifierValue& size = To<CSSIdentifierValue>(*value_);
+  return CalculationExpressionAnchorQueryNode::CreateAnchorSize(
+      anchor_name, CSSValueIDToAnchorSizeValueEnum(size.GetValueID()),
+      fallback);
 }
 
 void CSSMathExpressionAnchorQuery::Trace(Visitor* visitor) const {
@@ -1401,6 +1469,56 @@
       op);
 }
 
+namespace {
+
+CSSValue* AnchorQueryValueToCSSValue(
+    const CalculationExpressionAnchorQueryNode& anchor_query) {
+  if (anchor_query.Type() == AnchorQueryType::kAnchor) {
+    switch (anchor_query.AnchorSide()) {
+      case AnchorValue::kTop:
+        return CSSIdentifierValue::Create(CSSValueID::kTop);
+      case AnchorValue::kLeft:
+        return CSSIdentifierValue::Create(CSSValueID::kLeft);
+      case AnchorValue::kRight:
+        return CSSIdentifierValue::Create(CSSValueID::kRight);
+      case AnchorValue::kBottom:
+        return CSSIdentifierValue::Create(CSSValueID::kBottom);
+      case AnchorValue::kStart:
+        return CSSIdentifierValue::Create(CSSValueID::kStart);
+      case AnchorValue::kEnd:
+        return CSSIdentifierValue::Create(CSSValueID::kEnd);
+      case AnchorValue::kSelfStart:
+        return CSSIdentifierValue::Create(CSSValueID::kSelfStart);
+      case AnchorValue::kSelfEnd:
+        return CSSIdentifierValue::Create(CSSValueID::kSelfEnd);
+      case AnchorValue::kCenter:
+        return CSSIdentifierValue::Create(CSSValueID::kCenter);
+      case AnchorValue::kPercentage:
+        return CSSNumericLiteralValue::Create(
+            anchor_query.AnchorSidePercentage(),
+            CSSPrimitiveValue::UnitType::kPercentage);
+    }
+  }
+
+  DCHECK_EQ(anchor_query.Type(), AnchorQueryType::kAnchorSize);
+  switch (anchor_query.AnchorSize()) {
+    case AnchorSizeValue::kWidth:
+      return CSSIdentifierValue::Create(CSSValueID::kWidth);
+    case AnchorSizeValue::kHeight:
+      return CSSIdentifierValue::Create(CSSValueID::kHeight);
+    case AnchorSizeValue::kBlock:
+      return CSSIdentifierValue::Create(CSSValueID::kBlock);
+    case AnchorSizeValue::kInline:
+      return CSSIdentifierValue::Create(CSSValueID::kInline);
+    case AnchorSizeValue::kSelfBlock:
+      return CSSIdentifierValue::Create(CSSValueID::kSelfBlock);
+    case AnchorSizeValue::kSelfInline:
+      return CSSIdentifierValue::Create(CSSValueID::kSelfInline);
+  }
+}
+
+}  // namespace
+
 // static
 CSSMathExpressionNode* CSSMathExpressionNode::Create(
     const CalculationExpressionNode& node) {
@@ -1410,6 +1528,20 @@
     return Create(pixels_and_percent.GetPixelsAndPercent());
   }
 
+  if (node.IsAnchorQuery()) {
+    const auto& anchor_query = To<CalculationExpressionAnchorQueryNode>(node);
+    CSSAnchorQueryType type = anchor_query.Type() == AnchorQueryType::kAnchor
+                                  ? CSSAnchorQueryType::kAnchor
+                                  : CSSAnchorQueryType::kAnchorSize;
+    CSSCustomIdentValue* anchor_name =
+        MakeGarbageCollected<CSSCustomIdentValue>(anchor_query.AnchorName());
+    CSSValue* value = AnchorQueryValueToCSSValue(anchor_query);
+    CSSPrimitiveValue* fallback = CSSPrimitiveValue::CreateFromLength(
+        anchor_query.GetFallback(), /* zoom */ 1);
+    return MakeGarbageCollected<CSSMathExpressionAnchorQuery>(
+        type, *anchor_name, *value, fallback);
+  }
+
   DCHECK(node.IsOperation());
 
   const auto& operation = To<CalculationExpressionOperationNode>(node);
diff --git a/third_party/blink/renderer/core/css/css_properties.json5 b/third_party/blink/renderer/core/css/css_properties.json5
index fd88a0fc7..cc5c9f5 100644
--- a/third_party/blink/renderer/core/css/css_properties.json5
+++ b/third_party/blink/renderer/core/css/css_properties.json5
@@ -495,8 +495,8 @@
     // Theoretically matches
     // https://drafts.csswg.org/css-pseudo-4/#highlight-styling,
     // but includes additional properties for compatibility reasons.
-    // Used when RuntimeEnabledFeatures::HighlightInheritanceEnabled()
-    // is not enabled.
+    // Applied to highlight pseudos that use originating inheritance
+    // instead of highlight inheritance.
     valid_for_highlight_legacy: {
       default: false,
       valid_type: "bool",
diff --git a/third_party/blink/renderer/core/css/resolver/style_resolver.cc b/third_party/blink/renderer/core/css/resolver/style_resolver.cc
index 265e657..cc17be6 100644
--- a/third_party/blink/renderer/core/css/resolver/style_resolver.cc
+++ b/third_party/blink/renderer/core/css/resolver/style_resolver.cc
@@ -953,9 +953,7 @@
 void StyleResolver::ApplyInheritance(Element& element,
                                      const StyleRequest& style_request,
                                      StyleResolverState& state) {
-  if ((RuntimeEnabledFeatures::HighlightInheritanceEnabled() &&
-       IsHighlightPseudoElement(style_request.pseudo_id)) ||
-      style_request.pseudo_id == PseudoId::kPseudoIdHighlight) {
+  if (UsesHighlightPseudoInheritance(style_request.pseudo_id)) {
     // When resolving highlight styles for children, we need to default all
     // properties (whether or not defined as inherited) to parent values.
 
@@ -1680,8 +1678,8 @@
     if (state.Style()->StyleType() == kPseudoIdMarker)
       filter = filter.Add(CSSProperty::kValidForMarker, false);
     if (IsHighlightPseudoElement(state.Style()->StyleType())) {
-      if (RuntimeEnabledFeatures::HighlightInheritanceEnabled() ||
-          state.Style()->StyleType() == PseudoId::kPseudoIdHighlight) {
+      if (StyleResolver::UsesHighlightPseudoInheritance(
+              state.Style()->StyleType())) {
         filter = filter.Add(CSSProperty::kValidForHighlight, false);
       } else {
         filter = filter.Add(CSSProperty::kValidForHighlightLegacy, false);
@@ -1922,6 +1920,15 @@
   return true;
 }
 
+bool StyleResolver::UsesHighlightPseudoInheritance(PseudoId pseudo_id) {
+  // ::highlight() pseudos use highlight inheritance rather than originating
+  // inheritance even if highlight inheritance is not enabled for the other
+  // pseudos.
+  return ((IsHighlightPseudoElement(pseudo_id) &&
+           RuntimeEnabledFeatures::HighlightInheritanceEnabled()) ||
+          pseudo_id == PseudoId::kPseudoIdHighlight);
+}
+
 const CSSValue* StyleResolver::ComputeValue(
     Element* element,
     const CSSPropertyName& property_name,
diff --git a/third_party/blink/renderer/core/css/resolver/style_resolver.h b/third_party/blink/renderer/core/css/resolver/style_resolver.h
index c834fbca..4903087d 100644
--- a/third_party/blink/renderer/core/css/resolver/style_resolver.h
+++ b/third_party/blink/renderer/core/css/resolver/style_resolver.h
@@ -167,6 +167,8 @@
 
   static bool CanReuseBaseComputedStyle(const StyleResolverState& state);
 
+  static bool UsesHighlightPseudoInheritance(PseudoId);
+
   static const CSSValue* ComputeValue(Element* element,
                                       const CSSPropertyName&,
                                       const CSSValue&);
diff --git a/third_party/blink/renderer/core/css/rule_set.h b/third_party/blink/renderer/core/css/rule_set.h
index d2042fe..3955b5e 100644
--- a/third_party/blink/renderer/core/css/rule_set.h
+++ b/third_party/blink/renderer/core/css/rule_set.h
@@ -70,8 +70,9 @@
   // Defined in a highlight pseudo-element scope like ::selection and
   // ::target-text. Theoretically only properties listed in
   // https://drafts.csswg.org/css-pseudo-4/#highlight-styling should be valid,
-  // but when RuntimeEnabledFeatures::HighlightInheritanceEnabled() is disabled
-  // we allow a different set of rules for compatibility reasons.
+  // but for highlight pseudos using originating inheritance instead of
+  // highlight inheritance we allow a different set of rules for
+  // compatibility reasons.
   kHighlightLegacy,
   // Defined in a highlight pseudo-element scope like ::selection and
   // ::target-text. Only properties listed in
diff --git a/third_party/blink/renderer/core/dom/element.cc b/third_party/blink/renderer/core/dom/element.cc
index 2ecc6ad..c5f57161b 100644
--- a/third_party/blink/renderer/core/dom/element.cc
+++ b/third_party/blink/renderer/core/dom/element.cc
@@ -6319,11 +6319,7 @@
   StyleRequest style_request;
   style_request.pseudo_id = pseudo_element_specifier;
   style_request.type = StyleRequest::kForComputedStyle;
-  // IsHighlightPseudoElement returns true for all kinds of highlights including
-  // custom highlights which use Highlight Inheritance even when it is off.
-  if ((RuntimeEnabledFeatures::HighlightInheritanceEnabled() &&
-       IsHighlightPseudoElement(pseudo_element_specifier)) ||
-      pseudo_element_specifier == PseudoId::kPseudoIdHighlight) {
+  if (StyleResolver::UsesHighlightPseudoInheritance(pseudo_element_specifier)) {
     const ComputedStyle* highlight_element_style = nullptr;
     ContainerNode* parent = LayoutTreeBuilderTraversal::Parent(*this);
     if (parent && parent->GetComputedStyle()->HighlightData()) {
@@ -6697,9 +6693,7 @@
     const AtomicString& pseudo_argument) {
   // Highlight pseudos are resolved into StyleHighlightData during originating
   // style recalc, and should never be stored in StyleCachedData.
-  DCHECK((!RuntimeEnabledFeatures::HighlightInheritanceEnabled() ||
-          !IsHighlightPseudoElement(pseudo_id)) &&
-         pseudo_id != PseudoId::kPseudoIdHighlight);
+  DCHECK(!StyleResolver::UsesHighlightPseudoInheritance(pseudo_id));
 
   const ComputedStyle* style = GetComputedStyle();
 
@@ -6725,9 +6719,7 @@
     const StyleRequest& request) {
   // Highlight pseudos are resolved into StyleHighlightData during originating
   // style recalc, where we have the actual StyleRecalcContext.
-  DCHECK((!RuntimeEnabledFeatures::HighlightInheritanceEnabled() ||
-          !IsHighlightPseudoElement(request.pseudo_id)) &&
-         request.pseudo_id != PseudoId::kPseudoIdHighlight);
+  DCHECK(!StyleResolver::UsesHighlightPseudoInheritance(request.pseudo_id));
 
   return StyleForPseudoElement(
       StyleRecalcContext::FromInclusiveAncestors(*this), request);
diff --git a/third_party/blink/renderer/core/html/closewatcher/close_watcher.cc b/third_party/blink/renderer/core/html/closewatcher/close_watcher.cc
index 069e01f6..c283b82a 100644
--- a/third_party/blink/renderer/core/html/closewatcher/close_watcher.cc
+++ b/third_party/blink/renderer/core/html/closewatcher/close_watcher.cc
@@ -71,14 +71,23 @@
   }
 }
 
-bool CloseWatcher::WatcherStack::CheckForCreation() {
-  if (HasActiveWatcher() &&
-      !LocalFrame::ConsumeTransientUserActivation(window_->GetFrame())) {
-    return false;
+void CloseWatcher::WatcherStack::Signal() {
+  while (!watchers_.IsEmpty()) {
+    CloseWatcher* watcher = watchers_.back();
+    watcher->close();
+    if (!watcher->IsGroupedWithPrevious()) {
+      break;
+    }
   }
+}
 
-  ConsumeCloseWatcherCancelability();
-  return true;
+bool CloseWatcher::WatcherStack::HasConsumedFreeWatcher() const {
+  for (const auto& watcher : watchers_) {
+    if (!watcher->created_with_user_activation_) {
+      return true;
+    }
+  }
+  return false;
 }
 
 // static
@@ -86,10 +95,9 @@
                                    CloseWatcherOptions* options) {
   if (!window->GetFrame())
     return nullptr;
-  WatcherStack* stack = window->closewatcher_stack();
-  if (!stack->CheckForCreation())
-    return nullptr;
-  return CreateInternal(window, *stack, options);
+
+  WatcherStack& stack = *window->closewatcher_stack();
+  return CreateInternal(window, stack, options);
 }
 
 // static
@@ -105,15 +113,6 @@
   }
 
   WatcherStack& stack = *window->closewatcher_stack();
-
-  if (!stack.CheckForCreation()) {
-    exception_state.ThrowDOMException(
-        DOMExceptionCode::kNotAllowedError,
-        "Creating more than one CloseWatcher at a time requires a user "
-        "activation.");
-    return nullptr;
-  }
-
   return CreateInternal(window, stack, options);
 }
 
@@ -123,6 +122,19 @@
                                            CloseWatcherOptions* options) {
   CloseWatcher* watcher = MakeGarbageCollected<CloseWatcher>(window);
 
+  if (LocalFrame::ConsumeTransientUserActivation(window->GetFrame())) {
+    watcher->created_with_user_activation_ = true;
+    watcher->grouped_with_previous_ = false;
+  } else if (!stack.HasConsumedFreeWatcher()) {
+    watcher->created_with_user_activation_ = false;
+    watcher->grouped_with_previous_ = false;
+  } else {
+    watcher->created_with_user_activation_ = false;
+    watcher->grouped_with_previous_ = true;
+  }
+
+  stack.ConsumeCloseWatcherCancelability();
+
   if (options && options->hasSignal()) {
     AbortSignal* signal = options->signal();
     if (signal->aborted()) {
diff --git a/third_party/blink/renderer/core/html/closewatcher/close_watcher.h b/third_party/blink/renderer/core/html/closewatcher/close_watcher.h
index 48d47818..078abef 100644
--- a/third_party/blink/renderer/core/html/closewatcher/close_watcher.h
+++ b/third_party/blink/renderer/core/html/closewatcher/close_watcher.h
@@ -27,11 +27,17 @@
   static CloseWatcher* Create(ScriptState*,
                               CloseWatcherOptions*,
                               ExceptionState&);
+
+  // TODO(domenic): remove the CloseWatcherOptions* from this overload. This is
+  // meant to be used by Chromium-internal callers, who will not want to pass an
+  // AbortSignal.
   static CloseWatcher* Create(LocalDOMWindow*, CloseWatcherOptions*);
+
   explicit CloseWatcher(LocalDOMWindow*);
   void Trace(Visitor*) const override;
 
   bool IsClosed() const { return state_ == State::kClosed; }
+  bool IsGroupedWithPrevious() const { return grouped_with_previous_; }
 
   void close();
   void destroy();
@@ -57,25 +63,7 @@
     void Add(CloseWatcher*);
     void Remove(CloseWatcher*);
     bool HasActiveWatcher() const { return !watchers_.IsEmpty(); }
-
-    // This checks whether we can create a new CloseWatcher. Per spec we allow
-    // one "free" CloseWatcher, but if one already exists in the stack, then
-    // doing so will consume transient user activation.
-    //
-    // So the return values mean:
-    //
-    // - true: either we have no close watchers in the stack, and this is our
-    // free close watcher, or we had other close watchers in the stack, and we
-    // successfully consumed transient user activation so we can create a new
-    // one.
-    //
-    // - false: we had other close watchers in the stack, and there was no
-    // transient user activation to consume.
-    //
-    // If true is returned, then CanCloseWatcherFireCancel() will flip to false
-    // until the next user activation; close-watcher-related user activations
-    // are a shared resource between creation and the cancel event.
-    bool CheckForCreation();
+    bool HasConsumedFreeWatcher() const;
 
     void Trace(Visitor*) const;
 
@@ -93,7 +81,7 @@
 
    private:
     // mojom::blink::CloseListener override:
-    void Signal() final { watchers_.back()->close(); }
+    void Signal() final;
 
     HeapLinkedHashSet<Member<CloseWatcher>> watchers_;
 
@@ -118,6 +106,8 @@
   enum class State { kActive, kClosed };
   State state_ = State::kActive;
   bool dispatching_cancel_ = false;
+  bool grouped_with_previous_ = false;
+  bool created_with_user_activation_ = false;
 };
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/html/fenced_frame/html_fenced_frame_element.cc b/third_party/blink/renderer/core/html/fenced_frame/html_fenced_frame_element.cc
index 909df5a..8b166d3 100644
--- a/third_party/blink/renderer/core/html/fenced_frame/html_fenced_frame_element.cc
+++ b/third_party/blink/renderer/core/html/fenced_frame/html_fenced_frame_element.cc
@@ -4,6 +4,7 @@
 
 #include "third_party/blink/renderer/core/html/fenced_frame/html_fenced_frame_element.h"
 
+#include "base/metrics/histogram_macros.h"
 #include "services/network/public/cpp/is_potentially_trustworthy.h"
 #include "third_party/blink/public/common/features.h"
 #include "third_party/blink/public/common/fenced_frame/fenced_frame_utils.h"
@@ -150,6 +151,22 @@
   return wasted_area_fraction + resolution_penalty;
 }
 
+void RecordCreationOutcome(
+    const HTMLFencedFrameElement::CreationOutcome outcome) {
+  UMA_HISTOGRAM_ENUMERATION("Blink.FencedFrame.CreationOrNavigationOutcome",
+                            outcome);
+}
+
+void RecordOpaqueSizeCoercion(bool did_coerce) {
+  UMA_HISTOGRAM_BOOLEAN("Blink.FencedFrame.IsOpaqueFrameSizeCoerced",
+                        did_coerce);
+}
+
+void RecordResizedAfterSizeFrozen() {
+  UMA_HISTOGRAM_BOOLEAN("Blink.FencedFrame.IsFrameResizedAfterSizeFrozen",
+                        true);
+}
+
 }  // namespace
 
 HTMLFencedFrameElement::HTMLFencedFrameElement(Document& document)
@@ -228,6 +245,7 @@
             "allow-same-origin, allow-forms, allow-scripts, allow-popups, "
             "allow-popups-to-escape-sandbox and "
             "allow-top-navigation-by-user-activation."));
+    RecordCreationOutcome(CreationOutcome::kSandboxFlagsNotSet);
     return nullptr;
   }
 
@@ -277,6 +295,7 @@
                 FencedFrameModeToString(outer_element->GetMode()) +
                 "' nested in a fenced frame with mode '" +
                 FencedFrameModeToString(parent_mode) + "'."));
+    RecordCreationOutcome(CreationOutcome::kIncompatibleMode);
     return nullptr;
   }
 
@@ -392,6 +411,7 @@
         mojom::blink::ConsoleMessageLevel::kWarning,
         "A fenced frame was not loaded because the page is not in a secure "
         "context."));
+    RecordCreationOutcome(CreationOutcome::kInsecureContext);
     return;
   }
 
@@ -403,26 +423,31 @@
         "A fenced frame whose mode is " + FencedFrameModeToString(mode_) +
             " must be navigated to an \"https\" URL, an \"http\" localhost URL,"
             " or \"about:blank\"."));
+    RecordCreationOutcome(CreationOutcome::kIncompatibleURLDefault);
     return;
   }
 
-  if (mode_ == mojom::blink::FencedFrameMode::kOpaqueAds) {
-    if (!IsValidUrnUuidURL(GURL(url)) && !IsValidFencedFrameURL(GURL(url))) {
-      GetDocument().AddConsoleMessage(MakeGarbageCollected<ConsoleMessage>(
-          mojom::blink::ConsoleMessageSource::kRendering,
-          mojom::blink::ConsoleMessageLevel::kWarning,
-          "A fenced frame whose mode is " + FencedFrameModeToString(mode_) +
-              " must be navigated to an opaque \"urn:uuid\" URL,"
-              " an \"https\" URL, an \"http\" localhost URL,"
-              " or \"about:blank\"."));
-      return;
-    }
+  if (mode_ == mojom::blink::FencedFrameMode::kOpaqueAds &&
+      !IsValidUrnUuidURL(GURL(url)) && !IsValidFencedFrameURL(GURL(url))) {
+    GetDocument().AddConsoleMessage(MakeGarbageCollected<ConsoleMessage>(
+        mojom::blink::ConsoleMessageSource::kRendering,
+        mojom::blink::ConsoleMessageLevel::kWarning,
+        "A fenced frame whose mode is " + FencedFrameModeToString(mode_) +
+            " must be navigated to an opaque \"urn:uuid\" URL,"
+            " an \"https\" URL, an \"http\" localhost URL,"
+            " or \"about:blank\"."));
+    RecordCreationOutcome(CreationOutcome::kIncompatibleURLOpaque);
+    return;
   }
 
   frame_delegate_->Navigate(url);
 
-  if (!frozen_frame_size_)
+  if (!frozen_frame_size_) {
     FreezeFrameSize();
+    RecordCreationOutcome(mode_ == mojom::blink::FencedFrameMode::kDefault
+                              ? CreationOutcome::kSuccessDefault
+                              : CreationOutcome::kSuccessOpaque);
+  }
 }
 
 void HTMLFencedFrameElement::CreateDelegateAndNavigate() {
@@ -505,6 +530,7 @@
   static_assert(kAllowedAdSizes.size() > 0UL);
   for (const gfx::Size& allowed_size : kAllowedAdSizes) {
     if (SizeMatchesExactly(requested_size, allowed_size)) {
+      RecordOpaqueSizeCoercion(false);
       return requested_size;
     }
   }
@@ -551,6 +577,7 @@
       "A fenced frame in opaque-ads mode attempted to load with an "
       "unsupported size, and was therefore rounded to the nearest supported "
       "size."));
+  RecordOpaqueSizeCoercion(true);
 
   // The best size so far, and its loss. A lower loss represents
   // a better fit, so we will find the size that minimizes it, i.e.
@@ -655,6 +682,11 @@
 }
 
 void HTMLFencedFrameElement::OnResize(const PhysicalRect& content_rect) {
+  if (frozen_frame_size_.has_value() && !size_set_after_freeze_) {
+    // Only log this once per fenced frame.
+    RecordResizedAfterSizeFrozen();
+    size_set_after_freeze_ = true;
+  }
   content_rect_ = content_rect;
   // If the size information at |FreezeFrameSize| is not complete and we
   // needed to postpone freezing until the next resize, do it now. See
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 05c835b..fce4105 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
@@ -103,6 +103,19 @@
   // while keeping the inner frame size unchanged.
   HTMLIFrameElement* InnerIFrameElement() const;
 
+  // These values are persisted to logs. Entries should not be renumbered and
+  // numeric values should never be reused.
+  enum class CreationOutcome {
+    kSuccessDefault = 0,  // creates/navigates in default mode
+    kSuccessOpaque = 1,   // creates/navigates in opaque ads mode
+    kSandboxFlagsNotSet = 2,
+    kIncompatibleMode = 3,
+    kInsecureContext = 4,
+    kIncompatibleURLDefault = 5,
+    kIncompatibleURLOpaque = 6,
+    kMaxValue = kIncompatibleURLOpaque
+  };
+
  private:
   // This method will only navigate the underlying frame if the element
   // `isConnected()`. It will be deferred if the page is currently prerendering.
@@ -168,6 +181,11 @@
   // variable below so we can know when to reject updates to `mode_`.
   mojom::blink::FencedFrameMode mode_ = mojom::blink::FencedFrameMode::kDefault;
   bool freeze_mode_attribute_ = false;
+  // Used to track if the Blink.FencedFrame.IsFrameResizedAfterSizeFrozen
+  // histogram has already been logged for this fenced frame if its size was
+  // set after being frozen. This ensures that multiple logs don't happen
+  // for one fenced frame if it's constantly being resized.
+  bool size_set_after_freeze_ = false;
 
   friend class FencedFrameMPArchDelegate;
   friend class FencedFrameShadowDOMDelegate;
diff --git a/third_party/blink/renderer/core/inspector/inspector_style_resolver.cc b/third_party/blink/renderer/core/inspector/inspector_style_resolver.cc
index 30ccf769..42358cdd 100644
--- a/third_party/blink/renderer/core/inspector/inspector_style_resolver.cc
+++ b/third_party/blink/renderer/core/inspector/inspector_style_resolver.cc
@@ -89,55 +89,33 @@
     match->pseudo_id = kPseudoIdNone;
     parent_rules_.push_back(match);
 
-    if (RuntimeEnabledFeatures::HighlightInheritanceEnabled()) {
-      InspectorCSSMatchedPseudoElements* matched_pseudo_elements =
-          MakeGarbageCollected<InspectorCSSMatchedPseudoElements>();
-      matched_pseudo_elements->element = parent_element;
+    InspectorCSSMatchedPseudoElements* matched_pseudo_elements =
+        MakeGarbageCollected<InspectorCSSMatchedPseudoElements>();
+    matched_pseudo_elements->element = parent_element;
 
-      for (PseudoId pseudo_id = kFirstPublicPseudoId;
-           pseudo_id < kAfterLastInternalPseudoId;
-           pseudo_id = static_cast<PseudoId>(pseudo_id + 1)) {
-        // Only highlight pseudos can be inherited.
-        if (!PseudoElement::IsWebExposed(pseudo_id, element_) ||
-            !IsHighlightPseudoElement(pseudo_id))
-          continue;
-
-        RuleIndexList* matched_rules = style_resolver.PseudoCSSRulesForElement(
-            parent_element, pseudo_id, g_null_atom,
-            StyleResolver::kAllButUACSSRules);
-        if (matched_rules && matched_rules->size()) {
-          InspectorCSSMatchedRules* pseudo_match =
-              MakeGarbageCollected<InspectorCSSMatchedRules>();
-          pseudo_match->element = parent_element;
-          pseudo_match->matched_rules = matched_rules;
-          pseudo_match->pseudo_id = pseudo_id;
-
-          matched_pseudo_elements->pseudo_element_rules.push_back(pseudo_match);
-        }
-      }
-
-      parent_pseudo_element_rules_.push_back(matched_pseudo_elements);
-    } else {
-      InspectorCSSMatchedPseudoElements* matched_pseudo_elements =
-          MakeGarbageCollected<InspectorCSSMatchedPseudoElements>();
-      matched_pseudo_elements->element = parent_element;
+    for (PseudoId pseudo_id = kFirstPublicPseudoId;
+         pseudo_id < kAfterLastInternalPseudoId;
+         pseudo_id = static_cast<PseudoId>(pseudo_id + 1)) {
+      // Only highlight pseudos can be inherited.
+      if (!PseudoElement::IsWebExposed(pseudo_id, element_) ||
+          !StyleResolver::UsesHighlightPseudoInheritance(pseudo_id))
+        continue;
 
       RuleIndexList* matched_rules = style_resolver.PseudoCSSRulesForElement(
-          parent_element, kPseudoIdHighlight, g_null_atom,
+          parent_element, pseudo_id, g_null_atom,
           StyleResolver::kAllButUACSSRules);
       if (matched_rules && matched_rules->size()) {
         InspectorCSSMatchedRules* pseudo_match =
             MakeGarbageCollected<InspectorCSSMatchedRules>();
         pseudo_match->element = parent_element;
         pseudo_match->matched_rules = matched_rules;
-        pseudo_match->pseudo_id = kPseudoIdHighlight;
+        pseudo_match->pseudo_id = pseudo_id;
 
         matched_pseudo_elements->pseudo_element_rules.push_back(pseudo_match);
       }
-
-      parent_pseudo_element_rules_.push_back(matched_pseudo_elements);
     }
 
+    parent_pseudo_element_rules_.push_back(matched_pseudo_elements);
     parent_element = FlatTreeTraversal::ParentElement(*parent_element);
   }
 }
diff --git a/third_party/blink/renderer/core/paint/highlight_painting_utils.cc b/third_party/blink/renderer/core/paint/highlight_painting_utils.cc
index 04831ac3..fa3c15ff 100644
--- a/third_party/blink/renderer/core/paint/highlight_painting_utils.cc
+++ b/third_party/blink/renderer/core/paint/highlight_painting_utils.cc
@@ -5,6 +5,7 @@
 #include "third_party/blink/renderer/core/paint/highlight_painting_utils.h"
 
 #include "components/shared_highlighting/core/common/fragment_directives_constants.h"
+#include "third_party/blink/renderer/core/css/resolver/style_resolver.h"
 #include "third_party/blink/renderer/core/css/style_engine.h"
 #include "third_party/blink/renderer/core/css/style_request.h"
 #include "third_party/blink/renderer/core/dom/element.h"
@@ -208,10 +209,8 @@
                                                    pseudo_argument);
 
   mojom::blink::ColorScheme color_scheme = style.UsedColorScheme();
-  if (pseudo_style &&
-      ((!RuntimeEnabledFeatures::HighlightInheritanceEnabled() &&
-        pseudo != PseudoId::kPseudoIdHighlight) ||
-       !UseUaHighlightColors(pseudo, *pseudo_style))) {
+  if (pseudo_style && (!StyleResolver::UsesHighlightPseudoInheritance(pseudo) ||
+                       !UseUaHighlightColors(pseudo, *pseudo_style))) {
     if (!document.InForcedColorsMode() ||
         pseudo_style->ForcedColorAdjust() != EForcedColorAdjust::kAuto) {
       if (pseudo_style->ColorIsCurrentColor()) {
@@ -241,8 +240,7 @@
     const ComputedStyle& style,
     PseudoId pseudo,
     const AtomicString& pseudo_argument) {
-  if (!RuntimeEnabledFeatures::HighlightInheritanceEnabled() &&
-      pseudo != PseudoId::kPseudoIdHighlight) {
+  if (!StyleResolver::UsesHighlightPseudoInheritance(pseudo)) {
     return HighlightPseudoStyleWithOriginatingInheritance(node, pseudo,
                                                           pseudo_argument);
   }
@@ -283,10 +281,8 @@
       HighlightPseudoStyle(node, style, pseudo, pseudo_argument);
 
   mojom::blink::ColorScheme color_scheme = style.UsedColorScheme();
-  if (pseudo_style &&
-      ((!RuntimeEnabledFeatures::HighlightInheritanceEnabled() &&
-        pseudo != PseudoId::kPseudoIdHighlight) ||
-       !UseUaHighlightColors(pseudo, *pseudo_style))) {
+  if (pseudo_style && (!StyleResolver::UsesHighlightPseudoInheritance(pseudo) ||
+                       !UseUaHighlightColors(pseudo, *pseudo_style))) {
     if (!document.InForcedColorsMode() ||
         pseudo_style->ForcedColorAdjust() != EForcedColorAdjust::kAuto) {
       Color highlight_color =
diff --git a/third_party/blink/renderer/core/style/computed_style.cc b/third_party/blink/renderer/core/style/computed_style.cc
index 4008776..26a26310 100644
--- a/third_party/blink/renderer/core/style/computed_style.cc
+++ b/third_party/blink/renderer/core/style/computed_style.cc
@@ -205,9 +205,7 @@
       continue;
     // Highlight pseudo styles are stored in StyleHighlightData, and compared
     // like any other inherited field, yielding Difference::kInherited.
-    if ((RuntimeEnabledFeatures::HighlightInheritanceEnabled() &&
-         IsHighlightPseudoElement(pseudo_id)) ||
-        pseudo_id == PseudoId::kPseudoIdHighlight)
+    if (StyleResolver::UsesHighlightPseudoInheritance(pseudo_id))
       continue;
     const ComputedStyle* new_pseudo_style =
         new_style.GetCachedPseudoElementStyle(pseudo_id);
diff --git a/third_party/blink/renderer/modules/file_system_access/file_system_access_file_delegate.h b/third_party/blink/renderer/modules/file_system_access/file_system_access_file_delegate.h
index 29462c1d..d528a1f 100644
--- a/third_party/blink/renderer/modules/file_system_access/file_system_access_file_delegate.h
+++ b/third_party/blink/renderer/modules/file_system_access/file_system_access_file_delegate.h
@@ -36,14 +36,14 @@
           incognito_file_remote);
 
   // Reads the given number of bytes (or until EOF is reached) into the span
-  // starting with the given offset. Returns the number of bytes read, or a file
-  // error on failure.
+  // starting with the given offset. `offset` cannot be negative. Returns the
+  // number of bytes read, or a file error on failure.
   virtual base::FileErrorOr<int> Read(int64_t offset,
                                       base::span<uint8_t> data) = 0;
 
   // Writes the span into the file at the given offset, overwriting any data
-  // that was previously there. Returns the number of bytes written, or a file
-  // error on failure.
+  // that was previously there. `offset` cannot be negative. Returns the number
+  // of bytes written, or a file error on failure.
   virtual base::FileErrorOr<int> Write(int64_t offset,
                                        const base::span<uint8_t> data) = 0;
 
@@ -52,8 +52,9 @@
   virtual void GetLength(
       base::OnceCallback<void(base::FileErrorOr<int64_t>)> callback) = 0;
 
-  // Asynchronously truncates the file to the given length. If `length` is
-  // greater than the current size of the file, the file is extended with zeros.
+  // Asynchronously truncates the file to the given length. `length` cannot be
+  // negative. If `length` is greater than the current size of the file, the
+  // file is extended with zeros.
   virtual void SetLength(
       int64_t length,
       base::OnceCallback<void(base::File::Error)> callback) = 0;
diff --git a/third_party/blink/renderer/modules/file_system_access/file_system_access_incognito_file_delegate.cc b/third_party/blink/renderer/modules/file_system_access/file_system_access_incognito_file_delegate.cc
index 0547c9f..0d54498 100644
--- a/third_party/blink/renderer/modules/file_system_access/file_system_access_incognito_file_delegate.cc
+++ b/third_party/blink/renderer/modules/file_system_access/file_system_access_incognito_file_delegate.cc
@@ -8,6 +8,7 @@
 #include "base/files/file_error_or.h"
 #include "base/memory/scoped_refptr.h"
 #include "base/notreached.h"
+#include "base/numerics/safe_conversions.h"
 #include "base/task/thread_pool.h"
 #include "mojo/public/cpp/bindings/pending_remote.h"
 #include "mojo/public/cpp/system/string_data_source.h"
@@ -100,18 +101,21 @@
 base::FileErrorOr<int> FileSystemAccessIncognitoFileDelegate::Read(
     int64_t offset,
     base::span<uint8_t> data) {
+  CHECK_GE(offset, 0);
+
   base::File::Error file_error;
   int bytes_read;
   absl::optional<mojo_base::BigBuffer> buffer;
-  mojo_ptr_->Read(offset, data.size(), &buffer, &file_error, &bytes_read);
+  int bytes_to_read = base::saturated_cast<int>(data.size());
+  mojo_ptr_->Read(offset, bytes_to_read, &buffer, &file_error, &bytes_read);
 
   CHECK_EQ(buffer.has_value(), file_error == base::File::FILE_OK);
 
   if (buffer.has_value()) {
-    CHECK_LE(static_cast<uint64_t>(bytes_read), data.size());
-    CHECK_LE(buffer->size(), data.size());
+    CHECK_LE(bytes_read, bytes_to_read);
+    CHECK_LE(buffer->size(), static_cast<uint64_t>(bytes_to_read));
 
-    memcpy(data.data(), buffer->data(), buffer->size());
+    memcpy(data.data(), buffer->data(), bytes_to_read);
   } else {
     CHECK_EQ(bytes_read, 0);
   }
@@ -122,6 +126,8 @@
 base::FileErrorOr<int> FileSystemAccessIncognitoFileDelegate::Write(
     int64_t offset,
     const base::span<uint8_t> data) {
+  CHECK_GE(offset, 0);
+
   mojo::ScopedDataPipeProducerHandle producer_handle;
   mojo::ScopedDataPipeConsumerHandle consumer_handle;
   if (!CreateDataPipeForSize(data.size(), producer_handle, consumer_handle)) {
@@ -169,14 +175,7 @@
     int64_t length,
     base::OnceCallback<void(base::File::Error)> callback) {
   DCHECK(task_runner_->RunsTasksInCurrentSequence());
-  if (length < 0) {
-    // This method is expected to finish asynchronously, so post a task to the
-    // current sequence to return the error.
-    task_runner_->PostTask(
-        FROM_HERE, WTF::Bind(std::move(callback),
-                             base::File::Error::FILE_ERROR_INVALID_OPERATION));
-    return;
-  }
+  CHECK_GE(length, 0);
 
   mojo_ptr_->SetLength(length, WTF::Bind(std::move(callback)));
 }
diff --git a/third_party/blink/renderer/modules/file_system_access/file_system_access_regular_file_delegate.cc b/third_party/blink/renderer/modules/file_system_access/file_system_access_regular_file_delegate.cc
index e072fc2..a396f6d12 100644
--- a/third_party/blink/renderer/modules/file_system_access/file_system_access_regular_file_delegate.cc
+++ b/third_party/blink/renderer/modules/file_system_access/file_system_access_regular_file_delegate.cc
@@ -68,7 +68,7 @@
 base::FileErrorOr<int> FileSystemAccessRegularFileDelegate::Read(
     int64_t offset,
     base::span<uint8_t> data) {
-  DCHECK_GE(offset, 0);
+  CHECK_GE(offset, 0);
 
   int size = base::checked_cast<int>(data.size());
   int result =
@@ -82,7 +82,7 @@
 base::FileErrorOr<int> FileSystemAccessRegularFileDelegate::Write(
     int64_t offset,
     const base::span<uint8_t> data) {
-  DCHECK_GE(offset, 0);
+  CHECK_GE(offset, 0);
 
   int write_size = base::checked_cast<int>(data.size());
 
@@ -164,14 +164,8 @@
     int64_t new_length,
     base::OnceCallback<void(base::File::Error)> callback) {
   DCHECK(task_runner_->RunsTasksInCurrentSequence());
-  if (new_length < 0) {
-    // This method is expected to finish asynchronously, so post a task to the
-    // current sequence to return the error.
-    task_runner_->PostTask(
-        FROM_HERE, WTF::Bind(std::move(callback),
-                             base::File::Error::FILE_ERROR_INVALID_OPERATION));
-    return;
-  }
+  CHECK_GE(new_length, 0);
+
   capacity_tracker_->RequestFileCapacityChange(
       new_length,
       WTF::Bind(&FileSystemAccessRegularFileDelegate::DidCheckSetLengthCapacity,
diff --git a/third_party/blink/renderer/modules/file_system_access/file_system_read_write_options.idl b/third_party/blink/renderer/modules/file_system_access/file_system_read_write_options.idl
index 28bc992..3af55da 100644
--- a/third_party/blink/renderer/modules/file_system_access/file_system_read_write_options.idl
+++ b/third_party/blink/renderer/modules/file_system_access/file_system_read_write_options.idl
@@ -6,5 +6,5 @@
 // https://docs.google.com/document/d/1g7ZCqZ5NdiU7oqyCpsc2iZ7rRAY1ZXO-9VoG4LfP7fM
 
 dictionary FileSystemReadWriteOptions {
-  unsigned long long at;
+  [EnforceRange] unsigned long long at;
 };
diff --git a/third_party/blink/renderer/modules/file_system_access/file_system_sync_access_handle.cc b/third_party/blink/renderer/modules/file_system_access/file_system_sync_access_handle.cc
index 1d709c9a..dda2472 100644
--- a/third_party/blink/renderer/modules/file_system_access/file_system_sync_access_handle.cc
+++ b/third_party/blink/renderer/modules/file_system_access/file_system_sync_access_handle.cc
@@ -5,6 +5,7 @@
 #include "third_party/blink/renderer/modules/file_system_access/file_system_sync_access_handle.h"
 
 #include "base/files/file_error_or.h"
+#include "base/numerics/checked_math.h"
 #include "build/build_config.h"
 #include "third_party/blink/renderer/bindings/core/v8/script_promise_resolver.h"
 #include "third_party/blink/renderer/bindings/core/v8/v8_throw_dom_exception.h"
@@ -206,8 +207,14 @@
   DCHECK(file_delegate()->IsValid())
       << "file I/O operation queued after file closed";
 
+  if (!base::CheckedNumeric<int64_t>(size).IsValid()) {
+    exception_state.ThrowDOMException(DOMExceptionCode::kNotSupportedError,
+                                      "Cannot truncate file to given length");
+    return result;
+  }
+
   file_delegate()->SetLength(
-      base::checked_cast<int64_t>(size),
+      size,
       WTF::Bind(
           [](ScriptPromiseResolver* resolver,
              FileSystemSyncAccessHandle* access_handle,
diff --git a/third_party/blink/renderer/modules/screen_enumeration/screen_detailed.cc b/third_party/blink/renderer/modules/screen_enumeration/screen_detailed.cc
index 7cab183..a5edd91 100644
--- a/third_party/blink/renderer/modules/screen_enumeration/screen_detailed.cc
+++ b/third_party/blink/renderer/modules/screen_enumeration/screen_detailed.cc
@@ -97,12 +97,10 @@
 String ScreenDetailed::label() const {
   if (!DomWindow())
     return String();
-  if (RuntimeEnabledFeatures::WindowPlacementEnhancedScreenLabelsEnabled()) {
-    // Return a user-friendly label for the screen, determined by the platform.
-    const std::string& label = GetScreenInfo().label;
-    if (!label.empty())
-      return String(label);
-  }
+  // If enabled, return a more accurate screen label determined by the platform.
+  if (RuntimeEnabledFeatures::WindowPlacementEnhancedScreenLabelsEnabled())
+    return String(GetScreenInfo().label);
+
   // Return a placeholder label, e.g. "Internal Display 1".
   // These don't have to be unique, but it's nice to be able to differentiate
   // if a user has two external screens, for example.
diff --git a/third_party/blink/renderer/platform/BUILD.gn b/third_party/blink/renderer/platform/BUILD.gn
index 5ef9ab2..61f875dd 100644
--- a/third_party/blink/renderer/platform/BUILD.gn
+++ b/third_party/blink/renderer/platform/BUILD.gn
@@ -714,6 +714,7 @@
     "fonts/web_font_render_style.cc",
     "fonts/web_font_typeface_factory.cc",
     "fonts/web_font_typeface_factory.h",
+    "geometry/anchor_query_enums.h",
     "geometry/blend.h",
     "geometry/calculation_expression_node.cc",
     "geometry/calculation_expression_node.h",
diff --git a/third_party/blink/renderer/platform/geometry/anchor_query_enums.h b/third_party/blink/renderer/platform/geometry/anchor_query_enums.h
new file mode 100644
index 0000000..3f40798
--- /dev/null
+++ b/third_party/blink/renderer/platform/geometry/anchor_query_enums.h
@@ -0,0 +1,49 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_GEOMETRY_ANCHOR_QUERY_ENUMS_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_GEOMETRY_ANCHOR_QUERY_ENUMS_H_
+
+namespace blink {
+
+// Enum types for CSS anchor() and anchor-size() functions.
+// See https://tabatkins.github.io/specs/css-anchor-position/#anchoring
+
+// TODO(crbug.com/1309178): Combine this with `CSSAnchorQueryType`.
+enum class AnchorQueryType {
+  kAnchor,
+  kAnchorSize,
+};
+
+// TODO(crbug.com/1309178): We currently keep all keywords as is in the computed
+// value of anchor(), but may try to simplify it (e.g., resolve logical
+// keywords) in the future.
+enum class AnchorValue {
+  kTop,
+  kLeft,
+  kRight,
+  kBottom,
+  kStart,
+  kEnd,
+  kSelfStart,
+  kSelfEnd,
+  kCenter,
+  kPercentage,
+};
+
+// TODO(crbug.com/1309178): We currently keep all keywords as is in the computed
+// value of anchor-size(), but may try to simplify it (e.g., resolve logical
+// keywords) in the future.
+enum class AnchorSizeValue {
+  kWidth,
+  kHeight,
+  kBlock,
+  kInline,
+  kSelfBlock,
+  kSelfInline,
+};
+
+}  // namespace blink
+
+#endif  // THIRD_PARTY_BLINK_RENDERER_PLATFORM_GEOMETRY_ANCHOR_QUERY_ENUMS_H_
diff --git a/third_party/blink/renderer/platform/geometry/calculation_expression_node.cc b/third_party/blink/renderer/platform/geometry/calculation_expression_node.cc
index 1a06514..5f13198 100644
--- a/third_party/blink/renderer/platform/geometry/calculation_expression_node.cc
+++ b/third_party/blink/renderer/platform/geometry/calculation_expression_node.cc
@@ -322,4 +322,74 @@
 }
 #endif
 
+// ------ CalculationExpressionAnchorQueryNode ------
+
+// static
+scoped_refptr<const CalculationExpressionAnchorQueryNode>
+CalculationExpressionAnchorQueryNode::CreateAnchor(const AtomicString& name,
+                                                   AnchorValue side,
+                                                   const Length& fallback) {
+  AnchorQueryValue value = {.anchor_side = side};
+  return base::MakeRefCounted<CalculationExpressionAnchorQueryNode>(
+      AnchorQueryType::kAnchor, name, value, /* percentage */ 0, fallback);
+}
+
+// static
+scoped_refptr<const CalculationExpressionAnchorQueryNode>
+CalculationExpressionAnchorQueryNode::CreateAnchorPercentage(
+    const AtomicString& name,
+    float percentage,
+    const Length& fallback) {
+  AnchorQueryValue value = {.anchor_side = AnchorValue::kPercentage};
+  return base::MakeRefCounted<CalculationExpressionAnchorQueryNode>(
+      AnchorQueryType::kAnchor, name, value, percentage, fallback);
+}
+
+//  static
+scoped_refptr<const CalculationExpressionAnchorQueryNode>
+CalculationExpressionAnchorQueryNode::CreateAnchorSize(const AtomicString& name,
+                                                       AnchorSizeValue size,
+                                                       const Length& fallback) {
+  AnchorQueryValue value = {.anchor_size = size};
+  return base::MakeRefCounted<CalculationExpressionAnchorQueryNode>(
+      AnchorQueryType::kAnchorSize, name, value, /* percentage */ 0, fallback);
+}
+
+bool CalculationExpressionAnchorQueryNode::operator==(
+    const CalculationExpressionNode& other) const {
+  const auto* other_anchor_query =
+      DynamicTo<CalculationExpressionAnchorQueryNode>(other);
+  if (!other_anchor_query)
+    return false;
+  if (type_ != other_anchor_query->type_)
+    return false;
+  if (anchor_name_ != other_anchor_query->anchor_name_)
+    return false;
+  if (type_ == AnchorQueryType::kAnchor) {
+    if (AnchorSide() != other_anchor_query->AnchorSide())
+      return false;
+    if (AnchorSide() == AnchorValue::kPercentage &&
+        AnchorSidePercentage() != other_anchor_query->AnchorSidePercentage()) {
+      return false;
+    }
+  } else {
+    if (AnchorSize() != other_anchor_query->AnchorSize())
+      return false;
+  }
+  if (fallback_ != other_anchor_query->fallback_)
+    return false;
+  return true;
+}
+
+scoped_refptr<const CalculationExpressionNode>
+CalculationExpressionAnchorQueryNode::Zoom(double factor) const {
+  return base::MakeRefCounted<CalculationExpressionAnchorQueryNode>(
+      type_, anchor_name_, value_, side_percentage_, fallback_.Zoom(factor));
+}
+
+float CalculationExpressionAnchorQueryNode::Evaluate(float) const {
+  // TODO(crbug.com/1309178): Implement.
+  return 0;
+}
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/platform/geometry/calculation_expression_node.h b/third_party/blink/renderer/platform/geometry/calculation_expression_node.h
index 9dd5c2e5..ab0d1f9 100644
--- a/third_party/blink/renderer/platform/geometry/calculation_expression_node.h
+++ b/third_party/blink/renderer/platform/geometry/calculation_expression_node.h
@@ -6,9 +6,11 @@
 #define THIRD_PARTY_BLINK_RENDERER_PLATFORM_GEOMETRY_CALCULATION_EXPRESSION_NODE_H_
 
 #include "base/check_op.h"
+#include "third_party/blink/renderer/platform/geometry/anchor_query_enums.h"
 #include "third_party/blink/renderer/platform/geometry/length.h"
 #include "third_party/blink/renderer/platform/wtf/casting.h"
 #include "third_party/blink/renderer/platform/wtf/ref_counted.h"
+#include "third_party/blink/renderer/platform/wtf/text/atomic_string.h"
 #include "third_party/blink/renderer/platform/wtf/vector.h"
 
 namespace blink {
@@ -38,6 +40,7 @@
   virtual bool IsNumber() const { return false; }
   virtual bool IsPixelsAndPercent() const { return false; }
   virtual bool IsOperation() const { return false; }
+  virtual bool IsAnchorQuery() const { return false; }
 
   virtual scoped_refptr<const CalculationExpressionNode> Zoom(
       double factor) const = 0;
@@ -169,6 +172,84 @@
   }
 };
 
+class PLATFORM_EXPORT CalculationExpressionAnchorQueryNode final
+    : public CalculationExpressionNode {
+ public:
+  static scoped_refptr<const CalculationExpressionAnchorQueryNode> CreateAnchor(
+      const AtomicString& name,
+      AnchorValue side,
+      const Length& fallback);
+  static scoped_refptr<const CalculationExpressionAnchorQueryNode>
+  CreateAnchorPercentage(const AtomicString& name,
+                         float percentage,
+                         const Length& fallback);
+  static scoped_refptr<const CalculationExpressionAnchorQueryNode>
+  CreateAnchorSize(const AtomicString& name,
+                   AnchorSizeValue size,
+                   const Length& fallback);
+
+  AnchorQueryType Type() const { return type_; }
+  const AtomicString& AnchorName() const { return anchor_name_; }
+  AnchorValue AnchorSide() const {
+    DCHECK_EQ(type_, AnchorQueryType::kAnchor);
+    return value_.anchor_side;
+  }
+  float AnchorSidePercentage() const {
+    DCHECK_EQ(type_, AnchorQueryType::kAnchor);
+    DCHECK_EQ(AnchorSide(), AnchorValue::kPercentage);
+    return side_percentage_;
+  }
+  AnchorSizeValue AnchorSize() const {
+    DCHECK_EQ(type_, AnchorQueryType::kAnchorSize);
+    return value_.anchor_size;
+  }
+  const Length& GetFallback() const { return fallback_; }
+
+  // Implement |CalculationExpressionNode|:
+  float Evaluate(float max_value) const final;
+  bool operator==(const CalculationExpressionNode& other) const final;
+  scoped_refptr<const CalculationExpressionNode> Zoom(
+      double factor) const final;
+  bool IsAnchorQuery() const final { return true; }
+  ~CalculationExpressionAnchorQueryNode() final = default;
+
+#if DCHECK_IS_ON()
+  ResultType ResolvedResultType() const final {
+    return ResultType::kPixelsAndPercent;
+  }
+#endif
+
+  union AnchorQueryValue {
+    AnchorValue anchor_side;
+    AnchorSizeValue anchor_size;
+  };
+
+  CalculationExpressionAnchorQueryNode(AnchorQueryType type,
+                                       const AtomicString& anchor_name,
+                                       AnchorQueryValue value,
+                                       float side_percentage,
+                                       const Length& fallback)
+      : type_(type),
+        anchor_name_(anchor_name),
+        value_(value),
+        side_percentage_(side_percentage),
+        fallback_(fallback) {}
+
+ private:
+  AnchorQueryType type_;
+  AtomicString anchor_name_;
+  AnchorQueryValue value_;
+  float side_percentage_ = 0;  // For AnchorSideValue::kPercentage only
+  Length fallback_;
+};
+
+template <>
+struct DowncastTraits<CalculationExpressionAnchorQueryNode> {
+  static bool AllowFrom(const CalculationExpressionNode& node) {
+    return node.IsAnchorQuery();
+  }
+};
+
 }  // namespace blink
 
 #endif  // THIRD_PARTY_BLINK_RENDERER_PLATFORM_GEOMETRY_CALCULATION_EXPRESSION_NODE_H_
diff --git a/third_party/blink/web_tests/NeverFixTests b/third_party/blink/web_tests/NeverFixTests
index 9328891..86463a8 100644
--- a/third_party/blink/web_tests/NeverFixTests
+++ b/third_party/blink/web_tests/NeverFixTests
@@ -148,6 +148,10 @@
 external/wpt/permissions-policy/experimental-features/vertical-scroll-touch-block-manual.tentative.html [ Skip ]
 [ Linux ] virtual/threaded/external/wpt/permissions-policy/experimental-features/vertical-scroll-touch-block-manual.tentative.html [ Pass ]
 
+# These tests require command line arguments so only virtual/conversions-debug-mode version is run.
+wpt_internal/attribution-reporting/* [ Skip ]
+virtual/conversions-debug-mode/wpt_internal/attribution-reporting/* [ Pass ]
+
 # This test requires a compositor to pass as root scrollbar scrolling is always
 # done on the compositor thread.
 fast/scrolling/scrollbars/mouse-scrolling-on-div-scrollbar-thumb-scaled.html [ Skip ]
diff --git a/third_party/blink/web_tests/VirtualTestSuites b/third_party/blink/web_tests/VirtualTestSuites
index 75086663..ebe9c6a 100644
--- a/third_party/blink/web_tests/VirtualTestSuites
+++ b/third_party/blink/web_tests/VirtualTestSuites
@@ -60,6 +60,12 @@
     "args": ["--enable-threaded-compositing"]
   },
   {
+    "prefix": "conversions-debug-mode",
+    "platforms": ["Linux", "Mac", "Win"],
+    "bases": ["wpt_internal/attribution-reporting"],
+    "args": ["--conversions-debug-mode"]
+  },
+  {
     "prefix": "composite-relative-keyframes",
     "platforms": ["Linux", "Mac", "Win"],
     "bases": ["external/wpt/css/css-transforms/animation"],
diff --git a/third_party/blink/web_tests/external/wpt/.well-known/attribution-reporting/report-event-attribution b/third_party/blink/web_tests/external/wpt/.well-known/attribution-reporting/report-event-attribution
new file mode 100644
index 0000000..8ca40faa
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/.well-known/attribution-reporting/report-event-attribution
@@ -0,0 +1,84 @@
+"""Endpoint to receive and return event-level attribution reports."""
+import json
+from typing import List, Optional, Tuple, TypedDict
+
+from wptserve.request import Request
+from wptserve.stash import Stash
+from wptserve.utils import isomorphic_decode, isomorphic_encode
+
+# Key used to access the reports in the stash. Since stash is unique per URL
+# path, we can use the same key for both event-level and aggregatable reports.
+STASH_KEY = "4691a2d7fca5430fb0f33b1bd8a9d388"
+
+Header = Tuple[str, str]
+Status = Tuple[int, str]
+Response = Tuple[Status, List[Header], str]
+
+CLEAR_STASH = isomorphic_encode("clear_stash")
+
+
+def handle_post_report(request: Request, headers: List[Header]) -> Response:
+  """Handles POST request for reports.
+
+  Retrieves the report from the request body and stores the report in the
+  stash. If clear_stash is specified in the query params, clears the stash.
+  """
+  if request.GET.get(CLEAR_STASH):
+    clear_stash(request.server.stash)
+    return (200, "OK"), headers, json.dumps({
+        "code": 200,
+        "message": "Stash successfully cleared.",
+    })
+  store_report(request.server.stash, request.body.decode("utf-8"))
+  return (201, "OK"), headers, json.dumps({
+      "code": 201,
+      "message": "Report successfully stored."
+  })
+
+
+def handle_get_reports(request: Request, headers: List[Header]) -> Response:
+  """Handles GET request for reports.
+
+  Retrieves and returns all reports from the stash.
+  """
+  reports = take_reports(request.server.stash)
+  return (200, "OK"), headers, json.dumps({
+      "code": 200,
+      "reports": reports,
+  })
+
+
+def store_report(stash: Stash, report: str) -> None:
+  """Stores the report in the stash. Report here is a JSON."""
+  reports = stash.take(STASH_KEY)
+  if not reports:
+    reports = []
+  reports.append(report)
+  stash.put(STASH_KEY, reports)
+  return None
+
+
+def take_reports(stash: Stash) -> List[str]:
+  """Takes all the reports from the stash and returns them."""
+  reports = stash.take(STASH_KEY)
+  if not reports:
+    reports = []
+  return reports
+
+
+def clear_stash(stash: Stash) -> None:
+  "Clears the stash."
+  stash.take(STASH_KEY)
+  return None
+
+
+def main(request, response):
+  headers = [("Content-Type", "application/json")]
+  if request.method == "POST":
+    return handle_post_report(request, headers)
+  if request.method == "GET":
+    return handle_get_reports(request, headers)
+  return (405, "Method Not Allowed"), headers, json.dumps({
+      "code": 405,
+      "message": "Only GET or POST methods are supported."
+  })
diff --git a/third_party/blink/web_tests/external/wpt/close-watcher/basic.html b/third_party/blink/web_tests/external/wpt/close-watcher/basic.html
index cb8bdaa..1c26c0ce 100644
--- a/third_party/blink/web_tests/external/wpt/close-watcher/basic.html
+++ b/third_party/blink/web_tests/external/wpt/close-watcher/basic.html
@@ -121,135 +121,6 @@
   assert_false(onclose_called);
 }, "close via synthesized escape key should not work");
 
-test(t => {
-  let watcher = new CloseWatcher();
-  t.add_cleanup(() => watcher.destroy());
-  assert_throws_dom("NotAllowedError", () => new CloseWatcher());
-}, "Multiple CloseWatchers require a user activation.");
-
-promise_test(async t => {
-  const watcher1 = new CloseWatcher();
-  t.add_cleanup(() => watcher1.destroy());
-
-  await test_driver.bless("create second CloseWater", () => {
-    const watcher2 = new CloseWatcher();
-    t.add_cleanup(() => watcher2.destroy());
-
-    assert_throws_dom("NotAllowedError", () => new CloseWatcher());
-  });
-}, "Cannot create multiple CloseWatchers from a single user activation");
-
-promise_test(async t => {
-  let watcher1 = new CloseWatcher();
-  let watcher1_closed = false;
-  watcher1.onclose = () => watcher1_closed = true;
-  let watcher2 = null;
-  let watcher2_closed = false;
-
-  await test_driver.bless("create second CloseWater", () => {
-    watcher2 = new CloseWatcher();
-    watcher2.onclose = () => watcher2_closed = true;
-  });
-
-  await test_driver.send_keys(document.getElementById('d'), ESC);
-
-  assert_false(watcher1_closed);
-  assert_true(watcher2_closed);
-
-  await test_driver.send_keys(document.getElementById('d'), ESC);
-
-  assert_true(watcher1_closed);
-}, "Multiple CloseWatchers work as a stack if secondary watchers are created with a user activation.");
-
-promise_test(async t => {
-  const events = [];
-
-  const watcher1 = create(t, "watcher1");
-  const watcher2 = await createBlessed(t, "watcher2");
-  const watcher3 = await createBlessed(t, "watcher3");
-  const watcher4 = await createBlessed(t, "watcher4");
-
-  assert_throws_dom("NotAllowedError", () => new CloseWatcher());
-
-  watcher4.close();
-  watcher3.close();
-  watcher2.close();
-  watcher1.close();
-
-  assert_array_equals(
-    events,
-    ["watcher4 onclose", "watcher3 onclose", "watcher2 onclose", "watcher1 onclose"]
-  );
-
-  function createBlessed(t, name) {
-    return test_driver.bless(`create ${name}`, () => create(t, name));
-  }
-
-  function create(t, name) {
-    const watcher = new CloseWatcher();
-    watcher.oncancel = () => events.push(`${name} oncancel`);
-    watcher.onclose = () => events.push(`${name} onclose`);
-    t.add_cleanup(() => watcher.destroy());
-    return watcher;
-  }
-
-}, "3 user activations let you have 3 + 1 = 4 close watchers/0 cancel events");
-
-promise_test(async t => {
-  const events = [];
-
-  const watcher1 = create(t, "watcher1");
-  const watcher2 = await createBlessed(t, "watcher2");
-
-  assert_throws_dom("NotAllowedError", () => new CloseWatcher());
-
-  // Send an arbitrary click to trigger user activation.
-  await test_driver.click(document.getElementById('d'));
-
-  watcher2.close();
-  assert_array_equals(events, ["watcher2 oncancel"]);
-
-  // This time we go straight to close, without a second cancel.
-  watcher2.close();
-  assert_array_equals(events, ["watcher2 oncancel", "watcher2 onclose"]);
-
-  watcher1.close();
-  assert_array_equals(events, ["watcher2 oncancel", "watcher2 onclose", "watcher1 onclose"]);
-
-  function createBlessed(t, name) {
-    return test_driver.bless(`create ${name}`, () => create(t, name));
-  }
-
-  function create(t, name) {
-    const watcher = new CloseWatcher();
-    watcher.oncancel = e => { events.push(`${name} oncancel`); e.preventDefault(); };
-    watcher.onclose = () => events.push(`${name} onclose`);
-    t.add_cleanup(() => watcher.destroy());
-    return watcher;
-  }
-}, "3 user activations let you have 2 close watchers with 1 cancel event, even if the first cancel event is prevented");
-
-test(t => {
-  let watcher = new CloseWatcher();
-  watcher.destroy();
-  let watcher2 = new CloseWatcher();
-  t.add_cleanup(() => watcher2.destroy());
-}, "destroying a free CloseWatcher allows a new one to be created without a user activation.");
-
-test(t => {
-  let watcher = new CloseWatcher();
-  watcher.close();
-  let watcher2 = new CloseWatcher();
-  t.add_cleanup(() => watcher2.destroy());
-}, "close()ing a free CloseWatcher allows a new one to be created without a user activation.");
-
-promise_test(async t => {
-  let watcher = new CloseWatcher();
-  await test_driver.send_keys(document.getElementById('d'), ESC);
-  let watcher2 = new CloseWatcher();
-  t.add_cleanup(() => watcher2.destroy());
-}, "Closing a free CloseWatcher via the escape key allows a new one to be created without a user activation.");
-
 promise_test(async t => {
   let watcher = new CloseWatcher();
   watcher.oncancel = () => { watcher.destroy(); }
diff --git a/third_party/blink/web_tests/external/wpt/close-watcher/resources/helpers.js b/third_party/blink/web_tests/external/wpt/close-watcher/resources/helpers.js
new file mode 100644
index 0000000..4e024a1
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/close-watcher/resources/helpers.js
@@ -0,0 +1,21 @@
+// TODO(domenic): consider using these in all test files.
+
+window.createRecordingCloseWatcher = (t, events, name) => {
+  const watcher = new CloseWatcher();
+  t.add_cleanup(() => watcher.destroy());
+  watcher.oncancel = () => events.push(name + " cancel");
+  watcher.onclose = () => events.push(name + " close");
+
+  return watcher;
+};
+
+window.createBlessedRecordingCloseWatcher = (t, events, name) => {
+  return test_driver.bless("create " + name, () => createRecordingCloseWatcher(t, events, name));
+};
+
+window.sendCloseSignal = () => {
+  // *not* \uu001B; see https://w3c.github.io/webdriver/#keyboard-actions
+  const ESC = '\uE00C';
+
+  return test_driver.send_keys(document.getElementById("d"), ESC);
+};
diff --git a/third_party/blink/web_tests/external/wpt/close-watcher/user-activation-multiple-plus-free.html b/third_party/blink/web_tests/external/wpt/close-watcher/user-activation-multiple-plus-free.html
new file mode 100644
index 0000000..8a86624
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/close-watcher/user-activation-multiple-plus-free.html
@@ -0,0 +1,32 @@
+<!doctype html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<script src="/resources/testdriver-actions.js"></script>
+<script src="resources/helpers.js"></script>
+
+<div id="d" style="height: 100px; width: 100px;"></div>
+<script>
+
+// This test needs to be separate from user-activation.html since, unlike those,
+// it relies on there not being any lingering user activation from previous
+// tests hanging around. That is, we need to be sure freeWatcher is created with
+// no user activation, to ensure that activationWatcher1 and activationWatcher2
+// get grouped as expected.
+promise_test(async t => {
+  const events = [];
+  createRecordingCloseWatcher(t, events, "freeWatcher");
+
+  await test_driver.bless("create two more CloseWatchers", () => {
+    createRecordingCloseWatcher(t, events, "activationWatcher1");
+    createRecordingCloseWatcher(t, events, "activationWatcher2");
+  });
+
+  await sendCloseSignal();
+  assert_array_equals(events, ["activationWatcher2 close", "activationWatcher1 close"]);
+
+  await sendCloseSignal();
+  assert_array_equals(events, ["activationWatcher2 close", "activationWatcher1 close", "freeWatcher close"]);
+}, "Multiple CloseWatchers created from a single user activation close together, but original free CloseWatcher closes separately");
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/close-watcher/user-activation.html b/third_party/blink/web_tests/external/wpt/close-watcher/user-activation.html
new file mode 100644
index 0000000..375488de
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/close-watcher/user-activation.html
@@ -0,0 +1,212 @@
+<!doctype html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<script src="/resources/testdriver-actions.js"></script>
+<script src="resources/helpers.js"></script>
+
+<div id="d" style="height: 100px; width: 100px;"></div>
+<script>
+promise_test(async t => {
+  const events = [];
+  const freeWatcher = createRecordingCloseWatcher(t, events, "freeWatcher");
+
+  await test_driver.bless("call close()", () => freeWatcher.close());
+
+  assert_array_equals(events, ["freeWatcher cancel", "freeWatcher close"]);
+}, "CloseWatchers created without user activation, but close()d via user activation, fires cancel");
+
+promise_test(async t => {
+  const events = [];
+  const freeWatcher = createRecordingCloseWatcher(t, events, "freeWatcher");
+  freeWatcher.addEventListener("cancel", e => e.preventDefault());
+
+  await test_driver.bless("call close()", () => freeWatcher.close());
+
+  assert_array_equals(events, ["freeWatcher cancel"]);
+}, "CloseWatchers created without user activation, but close()d via user activation, fires cancel, which can be preventDefault()ed");
+
+promise_test(async t => {
+  const events = [];
+  const freeWatcher = createRecordingCloseWatcher(t, events, "freeWatcher");
+
+  await test_driver.bless("grant user activation", () => sendCloseSignal());
+
+  assert_array_equals(events, ["freeWatcher cancel", "freeWatcher close"]);
+}, "CloseWatchers created without user activation, but closed via a close signal after user activation, fires cancel");
+
+promise_test(async t => {
+  const events = [];
+  const freeWatcher = createRecordingCloseWatcher(t, events, "freeWatcher");
+  freeWatcher.addEventListener("cancel", e => e.preventDefault());
+
+  await test_driver.bless("grant user activation", () => sendCloseSignal());
+
+  assert_array_equals(events, ["freeWatcher cancel"]);
+}, "CloseWatchers created without user activation, but closed via a close signal after user activation, fires cancel, which can be preventDefault()ed");
+
+promise_test(async t => {
+  const events = [];
+  createRecordingCloseWatcher(t, events, "freeWatcher");
+  createRecordingCloseWatcher(t, events, "watcher1");
+  createRecordingCloseWatcher(t, events, "watcher2");
+
+  await sendCloseSignal();
+  assert_array_equals(events, ["watcher2 close", "watcher1 close"]);
+}, "Multiple CloseWatchers created without user activation close together (with no cancel)");
+
+promise_test(async t => {
+  const events = [];
+  createRecordingCloseWatcher(t, events, "freeWatcher");
+  await createBlessedRecordingCloseWatcher(t, events, "activationWatcher");
+
+  await sendCloseSignal();
+  assert_array_equals(events, ["activationWatcher close"]);
+
+  await sendCloseSignal();
+  assert_array_equals(events, ["activationWatcher close", "freeWatcher close"]);
+}, "Creating a CloseWatcher from user activation keeps it separate from the free CloseWatcher, but they don't fire cancel");
+
+promise_test(async t => {
+  const events = [];
+  const freeWatcher = createRecordingCloseWatcher(t, events, "freeWatcher");
+  const activationWatcher = await createBlessedRecordingCloseWatcher(t, events, "activationWatcher");
+
+  await test_driver.bless("call activationWatcher.close()", () => activationWatcher.close());
+  assert_array_equals(events, ["activationWatcher cancel", "activationWatcher close"]);
+
+  await test_driver.bless("call freeWatcher.close()", () => freeWatcher.close());
+  assert_array_equals(events, ["activationWatcher cancel", "activationWatcher close", "freeWatcher cancel", "freeWatcher close"]);
+}, "Creating a CloseWatcher from user activation, and close()ing CloseWatchers with user activation, fires cancel");
+
+promise_test(async t => {
+  const events = [];
+  const freeWatcher = createRecordingCloseWatcher(t, events, "freeWatcher");
+  const activationWatcher = await createBlessedRecordingCloseWatcher(t, events, "activationWatcher");
+
+  await test_driver.bless("grant user activation", () => sendCloseSignal());
+  assert_array_equals(events, ["activationWatcher cancel", "activationWatcher close"]);
+
+  await test_driver.bless("grant user activation", () => sendCloseSignal());
+  assert_array_equals(events, ["activationWatcher cancel", "activationWatcher close", "freeWatcher cancel", "freeWatcher close"]);
+}, "Creating a CloseWatcher from user activation, and closing CloseWatchers with a close signal after user activation, fires cancel");
+
+promise_test(async t => {
+  const events = [];
+  createRecordingCloseWatcher(t, events, "freeWatcher");
+  await createBlessedRecordingCloseWatcher(t, events, "activationWatcher1");
+  await createBlessedRecordingCloseWatcher(t, events, "activationWatcher2");
+
+  await sendCloseSignal();
+  assert_array_equals(events, ["activationWatcher2 close"]);
+
+  await sendCloseSignal();
+  assert_array_equals(events, ["activationWatcher2 close", "activationWatcher1 close"]);
+}, "Multiple CloseWatchers created with user activation close in reverse order");
+
+promise_test(async t => {
+  const events = [];
+  createRecordingCloseWatcher(t, events, "freeWatcher");
+  await createBlessedRecordingCloseWatcher(t, events, "activationWatcher1");
+  await createBlessedRecordingCloseWatcher(t, events, "activationWatcher2");
+  await createBlessedRecordingCloseWatcher(t, events, "activationWatcher3");
+  createRecordingCloseWatcher(t, events, "watcher4");
+
+  await sendCloseSignal();
+  assert_array_equals(events, ["watcher4 close", "activationWatcher3 close"]);
+
+  await sendCloseSignal();
+  assert_array_equals(events, ["watcher4 close", "activationWatcher3 close", "activationWatcher2 close"]);
+
+  await sendCloseSignal();
+  assert_array_equals(events, ["watcher4 close", "activationWatcher3 close", "activationWatcher2 close", "activationWatcher1 close"]);
+
+  await sendCloseSignal();
+  assert_array_equals(events, ["watcher4 close", "activationWatcher3 close", "activationWatcher2 close", "activationWatcher1 close", "freeWatcher close"]);
+}, "3 user activations let you have 3 + 1 = 4 ungrouped close watchers/0 cancel events");
+
+promise_test(async t => {
+  const events = [];
+  const freeWatcher = createRecordingCloseWatcher(t, events, "freeWatcher");
+  const activationWatcher1 = await createBlessedRecordingCloseWatcher(t, events, "activationWatcher1");
+  activationWatcher1.addEventListener("cancel", e => e.preventDefault());
+
+  await test_driver.bless("call activationWatcher1.close()", () => activationWatcher1.close());
+  assert_array_equals(events, ["activationWatcher1 cancel"]);
+
+  // This time we go straight to close, without a second cancel.
+  activationWatcher1.close();
+  assert_array_equals(events, ["activationWatcher1 cancel", "activationWatcher1 close"]);
+
+  freeWatcher.close();
+  assert_array_equals(events, ["activationWatcher1 cancel", "activationWatcher1 close", "freeWatcher close"]);
+}, "3 user activations let you have 2 close watchers with 1 cancel event, even if the first cancel event is prevented");
+
+promise_test(async t => {
+  const events = [];
+  const freeWatcher1 = createRecordingCloseWatcher(t, events, "freeWatcher1");
+
+  freeWatcher1.destroy();
+  assert_array_equals(events, []);
+
+  const freeWatcher2 = createRecordingCloseWatcher(t, events, "freeWatcher2");
+
+  await sendCloseSignal();
+  assert_array_equals(events, ["freeWatcher2 close"]);
+}, "destroy()ing the free CloseWatcher allows a new free one to be created without user activation, and it receives the close signal");
+
+promise_test(async t => {
+  const events = [];
+  const freeWatcher1 = createRecordingCloseWatcher(t, events, "freeWatcher1");
+
+  freeWatcher1.close();
+  assert_array_equals(events, ["freeWatcher1 close"]);
+
+  const freeWatcher2 = createRecordingCloseWatcher(t, events, "freeWatcher2");
+
+  await sendCloseSignal();
+  assert_array_equals(events, ["freeWatcher1 close", "freeWatcher2 close"]);
+}, "close()ing the free CloseWatcher allows a new free one to be created without user activation, and it receives the close signal");
+
+promise_test(async t => {
+  const events = [];
+  const freeWatcher1 = createRecordingCloseWatcher(t, events, "freeWatcher1");
+
+  await sendCloseSignal();
+  assert_array_equals(events, ["freeWatcher1 close"]);
+
+  const freeWatcher2 = createRecordingCloseWatcher(t, events, "freeWatcher2");
+
+  await sendCloseSignal();
+  assert_array_equals(events, ["freeWatcher1 close", "freeWatcher2 close"]);
+}, "closing the free CloseWatcher via a close signal allows a new free one to be created without user activation, and it receives a second close signal");
+
+promise_test(async t => {
+  const events = [];
+  const activationWatcher = await createBlessedRecordingCloseWatcher(t, events, "activationWatcher");
+  const freeWatcher = createRecordingCloseWatcher(t, events, "freeWatcher");
+
+  await sendCloseSignal();
+  assert_array_equals(events, ["freeWatcher close"]);
+
+  await sendCloseSignal();
+  assert_array_equals(events, ["freeWatcher close", "activationWatcher close"]);
+}, "The second watcher can be the free watcher, if the first is created with user activation");
+
+promise_test(async t => {
+  const events = [];
+  const activationWatcher1 = await createBlessedRecordingCloseWatcher(t, events, "activationWatcher1");
+  const activationWatcher2 = await createBlessedRecordingCloseWatcher(t, events, "activationWatcher2");
+  const freeWatcher = createRecordingCloseWatcher(t, events, "freeWatcher");
+
+  await sendCloseSignal();
+  assert_array_equals(events, ["freeWatcher close"]);
+
+  await sendCloseSignal();
+  assert_array_equals(events, ["freeWatcher close", "activationWatcher2 close"]);
+
+  await sendCloseSignal();
+  assert_array_equals(events, ["freeWatcher close", "activationWatcher2 close", "activationWatcher1 close"]);
+}, "The third watcher can be the free watcher, if the first two are created with user activation");
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/dom/events/scrolling/scroll_support.js b/third_party/blink/web_tests/external/wpt/dom/events/scrolling/scroll_support.js
index 0a73f34f..d3be274 100644
--- a/third_party/blink/web_tests/external/wpt/dom/events/scrolling/scroll_support.js
+++ b/third_party/blink/web_tests/external/wpt/dom/events/scrolling/scroll_support.js
@@ -50,6 +50,31 @@
   })
 }
 
+// Scrolls in target according to move_path with pauses in between
+function touchScrollInTargetSequentiallyWithPause(target, move_path, pause_time_in_ms = 100) {
+  const test_driver_actions = new test_driver.Actions()
+    .addPointer("pointer1", "touch")
+    .pointerMove(0, 0, {origin: target})
+    .pointerDown();
+
+  const substeps = 5;
+  let x = 0;
+  let y = 0;
+  // Do each move in 5 steps
+  for(let move of move_path) {
+    let step_x = (move.x - x) / substeps;
+    let step_y = (move.y - y) / substeps;
+    for(let step = 0; step < substeps; step++) {
+      x += step_x;
+      y += step_y;
+      test_driver_actions.pointerMove(x, y, {origin: target});
+    }
+    test_driver_actions.pause(pause_time_in_ms);
+  }
+
+  return test_driver_actions.pointerUp().send();
+}
+
 function touchScrollInTarget(pixels_to_scroll, target, direction, pause_time_in_ms = 100) {
   var x_delta = 0;
   var y_delta = 0;
@@ -61,7 +86,7 @@
   } else if (direction == "right") {
     x_delta = -1 * pixels_to_scroll / num_movs;
   } else if (direction == "left") {
-    x_delta = pixels_to_scroll / num_movs;;
+    x_delta = pixels_to_scroll / num_movs;
   } else {
     throw("scroll direction '" + direction + "' is not expected, direction should be 'down', 'up', 'left' or 'right'");
   }
@@ -95,3 +120,23 @@
     .pointerUp()
     .send();
 }
+
+// Returns a promise that resolves when the given condition holds for 10
+// animation frames or rejects if the condition changes to false within 10
+// animation frames.
+function conditionHolds(condition, error_message = 'Condition is not true anymore.') {
+  const MAX_FRAME = 10;
+  return new Promise((resolve, reject) => {
+    function tick(frames) {
+      // We requestAnimationFrame either for 10 frames or until condition is
+      // violated.
+      if (frames >= MAX_FRAME)
+        resolve();
+      else if (!condition())
+        reject(error_message);
+      else
+        requestAnimationFrame(tick.bind(this, frames + 1));
+    }
+    tick(0);
+  });
+}
diff --git a/third_party/blink/web_tests/external/wpt/dom/events/scrolling/scrollend-event-fired-after-sequence-of-scrolls.tentative.html b/third_party/blink/web_tests/external/wpt/dom/events/scrolling/scrollend-event-fired-after-sequence-of-scrolls.tentative.html
new file mode 100644
index 0000000..ae3c26f
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/dom/events/scrolling/scrollend-event-fired-after-sequence-of-scrolls.tentative.html
@@ -0,0 +1,62 @@
+<!DOCTYPE HTML>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-actions.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<script src="scroll_support.js"></script>
+<style>
+#targetDiv {
+  width: 200px;
+  height: 200px;
+  overflow: scroll;
+}
+
+#innerDiv {
+  width: 500px;
+  height: 4000px;
+}
+</style>
+
+<body style="margin:0" onload=runTest()>
+<div id="targetDiv">
+  <div id="innerDiv">
+  </div>
+</div>
+</body>
+
+<script>
+const target_div = document.getElementById('targetDiv');
+let scrollend_arrived = false;
+let scrollend_event_count = 0;
+
+function onScrollEnd(event) {
+  assert_false(event.cancelable);
+  assert_false(event.bubbles);
+  scrollend_arrived = true;
+  scrollend_event_count += 1;
+}
+
+function runTest() {
+  promise_test (async (t) => {
+    // Make sure that no scrollend event is sent to document.
+    document.addEventListener("scrollend",
+        t.unreached_func("document got unexpected scrollend event."));
+    await waitForCompositorCommit();
+
+    // Scroll down & up & down on target div and wait for the target_div to get scrollend event.
+    target_div.addEventListener("scrollend", onScrollEnd);
+    const move_path = [
+      { x: 0, y: -300}, // down
+      { x: 0, y: -100}, // up
+      { x: 0, y: -400}, // down
+      { x: 0, y: -200}, // up
+    ];
+    await touchScrollInTargetSequentiallyWithPause(target_div, move_path, 150);
+
+    await waitFor(() => {return scrollend_arrived;},
+        'target_div did not receive scrollend event after sequence of scrolls on target.');
+    assert_equals(scrollend_event_count, 1);
+  }, "Move down, up and down again, receive scrollend event only once");
+}
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/dom/events/scrolling/scrollend-event-not-fired-after-removing-scroller.tentative.html b/third_party/blink/web_tests/external/wpt/dom/events/scrolling/scrollend-event-not-fired-after-removing-scroller.tentative.html
new file mode 100644
index 0000000..575a5ac
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/dom/events/scrolling/scrollend-event-not-fired-after-removing-scroller.tentative.html
@@ -0,0 +1,83 @@
+<!DOCTYPE HTML>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-actions.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<script src="scroll_support.js"></script>
+<style>
+#rootDiv {
+  width: 500px;
+  height: 500px;
+}
+
+#targetDiv {
+  width: 200px;
+  height: 200px;
+  overflow: scroll;
+}
+
+#innerDiv {
+  width: 500px;
+  height: 4000px;
+}
+</style>
+
+<body style="margin:0" onload=runTest()>
+</body>
+
+<script>
+let scrollend_arrived = false;
+
+async function setupHtmlAndScrollAndRemoveElement(element_to_remove_id) {
+  document.body.innerHTML=`
+    <div id="rootDiv">
+      <div id="targetDiv">
+        <div id="innerDiv">
+        </div>
+      </div>
+    </div>
+  `;
+  await waitForCompositorCommit();
+
+  const target_div = document.getElementById('targetDiv');
+  const element_to_remove = document.getElementById(element_to_remove_id);
+  let reached_half_scroll = false;
+  scrollend_arrived = false;
+
+  target_div.addEventListener("scrollend", () => {
+    scrollend_arrived = true;
+  });
+
+  target_div.onscroll = () => {
+    // Remove the element after reached half of the scroll offset,
+    if(target_div.scrollTop >= 1000) {
+      reached_half_scroll = true;
+      element_to_remove.remove();
+    }
+  };
+
+  target_div.scrollTo({top:2000, left:0, behavior:"smooth"});
+  await waitFor(() => {return reached_half_scroll; },
+    "target_div never reached scroll offset of 1000");
+  await waitForCompositorCommit();
+}
+
+function runTest() {
+  promise_test (async (t) => {
+    await setupHtmlAndScrollAndRemoveElement("rootDiv");
+    await conditionHolds(() => { return !scrollend_arrived; });
+  }, "No scrollend is received after removing parent div");
+
+  promise_test (async (t) => {
+    await setupHtmlAndScrollAndRemoveElement("targetDiv");
+    await conditionHolds(() => { return !scrollend_arrived; });
+  }, "No scrollend is received after removing scrolling element");
+
+  promise_test (async (t) => {
+    await setupHtmlAndScrollAndRemoveElement("innerDiv");
+    await waitFor(() => { return scrollend_arrived; },
+        'target_div did not receive scrollend event after vertical scroll.');
+  }, "scrollend is received after removing descendant div");
+}
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/fs/FileSystemSyncAccessHandle-read-write.https.tentative.worker.js b/third_party/blink/web_tests/external/wpt/fs/FileSystemSyncAccessHandle-read-write.https.tentative.worker.js
index eb93c06..9f2a61d 100644
--- a/third_party/blink/web_tests/external/wpt/fs/FileSystemSyncAccessHandle-read-write.https.tentative.worker.js
+++ b/third_party/blink/web_tests/external/wpt/fs/FileSystemSyncAccessHandle-read-write.https.tentative.worker.js
@@ -182,8 +182,17 @@
 
 sync_access_handle_test((t, handle) => {
   const readBuffer = new Uint8Array(24);
-  assert_throws_dom(
-    'NotSupportedError', () => handle.read(readBuffer, { at: -1 }));
+  assert_throws_js(TypeError, () => handle.read(readBuffer, {at: -1}));
 }, 'Test reading at a negative offset fails.');
 
+sync_access_handle_test((t, handle) => {
+  const text = 'foobar';
+  const writeBuffer = new TextEncoder().encode(text);
+  assert_throws_js(TypeError, () => handle.write(writeBuffer, {at: -1}));
+
+  const readBuffer = new Uint8Array(24);
+  const readBytes = handle.read(readBuffer, {at: 0});
+  assert_equals(0, readBytes, 'Check that no bytes were written');
+}, 'Test writing at a negative offset fails.');
+
 done();
diff --git a/third_party/blink/web_tests/fast/compositor-wheel-scroll-latching/animated-scroll/touchpad-scroll-impl-to-main.html b/third_party/blink/web_tests/fast/compositor-wheel-scroll-latching/animated-scroll/touchpad-scroll-impl-to-main.html
index 6a59046..e09ca389 100644
--- a/third_party/blink/web_tests/fast/compositor-wheel-scroll-latching/animated-scroll/touchpad-scroll-impl-to-main.html
+++ b/third_party/blink/web_tests/fast/compositor-wheel-scroll-latching/animated-scroll/touchpad-scroll-impl-to-main.html
@@ -37,12 +37,11 @@
 div.addEventListener("wheel", changeStyleToScrollOnMain, {passive: true});
 
 promise_test(async () => {
-  const expectedScrollY = 500;
-  const pixelsToScrollY = calculatePixelsToScroll(div, 0, expectedScrollY).y;
+  const pixelsToScrollY = 500;
   const x = (rect.left + rect.right) / 2;
   const y = (rect.top + rect.bottom) / 2;
-  await smoothScroll(
-    pixelsToScrollY, x, y, GESTURE_SOURCE_TYPE, 'down', SPEED_INSTANT
+  await percentBasedSmoothScroll(
+    pixelsToScrollY, x, y, GESTURE_SOURCE_TYPE, 'down', SPEED_INSTANT, div
   );
   await waitForAnimationEndTimeBased(() => {
     return div.scrollTop;
diff --git a/third_party/blink/web_tests/fast/events/wheel/wheelevent-in-vertical-scrollbar-in-rtl.html b/third_party/blink/web_tests/fast/events/wheel/wheelevent-in-vertical-scrollbar-in-rtl.html
index f56f77b..0694451 100644
--- a/third_party/blink/web_tests/fast/events/wheel/wheelevent-in-vertical-scrollbar-in-rtl.html
+++ b/third_party/blink/web_tests/fast/events/wheel/wheelevent-in-vertical-scrollbar-in-rtl.html
@@ -5,18 +5,12 @@
 <script src="../../../virtual/percent-based-scrolling/resources/percent-based-util.js"></script>
 <script>
   const pixelsToScroll1 = 10;
-  const { x: pixelsToScrollX1, y: pixelsToScrollY1 } = calculatePixelsToScroll(
-    document.scrollingElement, pixelsToScroll1, pixelsToScroll1
-  );
   const pixelsToScroll2 = 30;
-  const { x: pixelsToScrollX2, y: pixelsToScrollY2 } = calculatePixelsToScroll(
-    document.scrollingElement, pixelsToScroll2, pixelsToScroll2
-  );
   const testScrolls = [
-    {distance: pixelsToScrollX1, expectedX: 110, expectedY: -100, direction: 'right'},
-    {distance: pixelsToScrollX2, expectedX: 80, expectedY: -100, direction: 'left'},
-    {distance: pixelsToScrollY1, expectedX: 80, expectedY: -90, direction: 'down'},
-    {distance: pixelsToScrollY2, expectedX: 80, expectedY: -120, direction: 'up'},
+    {distance: pixelsToScroll1, expectedX: 110, expectedY: -100, direction: 'right'},
+    {distance: pixelsToScroll2, expectedX: 80, expectedY: -100, direction: 'left'},
+    {distance: pixelsToScroll1, expectedX: 80, expectedY: -90, direction: 'down'},
+    {distance: pixelsToScroll2, expectedX: 80, expectedY: -120, direction: 'up'},
   ];
   var currentTest = -1;
 
@@ -28,7 +22,7 @@
     var testCase = testScrolls[currentTest];
     promise_test(async () => {
       await mouseMoveTo(100, 100);
-      await smoothScroll(testCase.distance, 100, 100,
+      await percentBasedSmoothScroll(testCase.distance, 100, 100,
           GestureSourceType.MOUSE_INPUT, testCase.direction, SPEED_INSTANT);
       await waitForAnimationEndTimeBased(() => { return window.scrollX; });
       await waitForAnimationEndTimeBased(() => { return window.scrollY; });
diff --git a/third_party/blink/web_tests/fast/scroll-behavior/smooth-scroll/mousewheel-scroll.html b/third_party/blink/web_tests/fast/scroll-behavior/smooth-scroll/mousewheel-scroll.html
index f1b928c..b49bffe 100644
--- a/third_party/blink/web_tests/fast/scroll-behavior/smooth-scroll/mousewheel-scroll.html
+++ b/third_party/blink/web_tests/fast/scroll-behavior/smooth-scroll/mousewheel-scroll.html
@@ -11,19 +11,6 @@
 </style>
 
 <script>
-  function checkScrollValues(pixelsToScroll) {
-    const { x: expectedScrollLeft, y: expectedScrollTop} = (
-      calculateExpectedScroll(
-        document.scrollingElement, pixelsToScroll, pixelsToScroll
-      ));
-    assert_approx_equals(
-      document.scrollingElement.scrollTop, expectedScrollTop, 0.001
-    );
-    assert_approx_equals(
-      document.scrollingElement.scrollLeft, expectedScrollLeft, 0.001
-    );
-  }
-
   // Turn on smooth scrolling.
   internals.settings.setScrollAnimatorEnabled(true);
 
@@ -41,10 +28,10 @@
     await mouseMoveTo(x, y);
 
     // Scroll 3 ticks diagonally.
-    await smoothScroll(3 * pixelsPerTick() , x, y, source, 'downright',
+    await percentBasedSmoothScroll(3 * pixelsPerTick() , x, y, source, 'downright',
       SPEED_INSTANT);
     // Undo 2 ticks in each direction.
-    await smoothScroll(2 * pixelsPerTick(), x, y, source, 'upleft',
+    await percentBasedSmoothScroll(2 * pixelsPerTick(), x, y, source, 'upleft',
       SPEED_INSTANT);
 
     await waitForAnimationEndTimeBased(() => { return scroller.scrollTop; });
@@ -52,11 +39,12 @@
 
     // total scroll distance after scrolling actions above
     const pixelsToScroll = pixelsPerTick();
-    checkScrollValues(pixelsToScroll);
+    assert_approx_equals(scroller.scrollTop, pixelsToScroll, 0.4);
+    assert_approx_equals(scroller.scrollLeft, pixelsToScroll, 0.4);
 
     // Undo the last tick in each direction to reset the scroll state before
     // starting the second test.
-    await smoothScroll(1 * pixelsPerTick(), x, y, source, 'upleft',
+    await percentBasedSmoothScroll(1 * pixelsPerTick(), x, y, source, 'upleft',
       SPEED_INSTANT);
 
     await waitForAnimationEndTimeBased(() => { return scroller.scrollTop; });
@@ -73,19 +61,19 @@
     await mouseMoveTo(x, y);
 
     // Scroll down 3 ticks.
-    await smoothScroll(1 * pixelsPerTick(), x, y, source, 'down',
+    await percentBasedSmoothScroll(1 * pixelsPerTick(), x, y, source, 'down',
         SPEED_INSTANT);
-    await smoothScroll(2 * pixelsPerTick(), x, y, source, 'down',
+    await percentBasedSmoothScroll(2 * pixelsPerTick(), x, y, source, 'down',
         SPEED_INSTANT);
     // Scroll right 3 ticks.
-    await smoothScroll(1 * pixelsPerTick(), x, y, source, 'right',
+    await percentBasedSmoothScroll(1 * pixelsPerTick(), x, y, source, 'right',
         SPEED_INSTANT);
-    await smoothScroll(2 * pixelsPerTick(), x, y, source, 'right',
+    await percentBasedSmoothScroll(2 * pixelsPerTick(), x, y, source, 'right',
         SPEED_INSTANT);
     // Undo 1 tick in each direction.
-    await smoothScroll(1 * pixelsPerTick(), x, y, source, 'up',
+    await percentBasedSmoothScroll(1 * pixelsPerTick(), x, y, source, 'up',
         SPEED_INSTANT);
-    await smoothScroll(1 * pixelsPerTick(), x, y, source, 'left',
+    await percentBasedSmoothScroll(1 * pixelsPerTick(), x, y, source, 'left',
         SPEED_INSTANT);
 
     await waitForAnimationEndTimeBased(() => { return scroller.scrollTop; });
@@ -93,7 +81,8 @@
 
     // total scroll distance after scrolling actions above
     const pixelsToScroll = 2 * pixelsPerTick();
-    checkScrollValues(pixelsToScroll);
+    assert_approx_equals(scroller.scrollTop, pixelsToScroll, 0.6);
+    assert_approx_equals(scroller.scrollLeft, pixelsToScroll, 0.6);
   }, "This test ensures that consecutive mouse wheel ticks vertically or " +
       "horizontally scroll to the right offset. The main purpose of this " +
       "test is to ensure that smooth scrolling on the compositor works as " +
diff --git a/third_party/blink/web_tests/fast/scroll-behavior/smooth-scroll/ongoing-smooth-scroll-anchors.html b/third_party/blink/web_tests/fast/scroll-behavior/smooth-scroll/ongoing-smooth-scroll-anchors.html
index be57f8c..b5a19c4 100644
--- a/third_party/blink/web_tests/fast/scroll-behavior/smooth-scroll/ongoing-smooth-scroll-anchors.html
+++ b/third_party/blink/web_tests/fast/scroll-behavior/smooth-scroll/ongoing-smooth-scroll-anchors.html
@@ -22,7 +22,7 @@
 <div id="anchor"></div>
 
 <script>
-  var asyncTest = async_test("Verify smooth scroll interaction with scroll anchroing");
+  var asyncTest = async_test("Verify smooth scroll interaction with scroll anchoring");
 
   // The element that will change in height.
   var ch;
@@ -30,9 +30,7 @@
   // Initital scroll position.
   var initialY = 10;
   // Amount to smooth scroll by.
-  const { y: userScrollY } = calculatePixelsToScroll(
-    document.scrollingElement, 0, 205
-  );
+  const userScrollY = 205;
   // Amount to change the height of the element above the viewport.
   var changerY =  100;
   // End position: height of ch + userScroll.
@@ -55,7 +53,7 @@
     document.addEventListener("scroll", scrollListener);
 
     // Smooth scroll.
-    smoothScroll(userScrollY, 100, 100, source, 'down', SPEED_INSTANT);
+    percentBasedSmoothScroll(userScrollY, 100, 100, source, 'down', SPEED_INSTANT);
     await raf();
     document.getElementById('anchor').scrollIntoView();
   }
diff --git a/third_party/blink/web_tests/fast/scroll-behavior/smooth-scroll/ongoing-smooth-scroll-vertical-rl-anchors.html b/third_party/blink/web_tests/fast/scroll-behavior/smooth-scroll/ongoing-smooth-scroll-vertical-rl-anchors.html
index a021f000..d13c9b6 100644
--- a/third_party/blink/web_tests/fast/scroll-behavior/smooth-scroll/ongoing-smooth-scroll-vertical-rl-anchors.html
+++ b/third_party/blink/web_tests/fast/scroll-behavior/smooth-scroll/ongoing-smooth-scroll-vertical-rl-anchors.html
@@ -33,9 +33,7 @@
   var initialX = -10;
   var initialY = 0;
   // Amount to smooth scroll by.
-  const { x: userScrollX } = calculatePixelsToScroll(
-    document.scrollingElement, -205, 0
-  );
+  const userScrollX = -205;
   // Amount to change the height of the element above the viewport.
   var changerX = 100;
   // End position: height of ch + userScroll.
@@ -58,7 +56,7 @@
     document.addEventListener("scroll", scrollListener);
 
     // Smooth scroll.
-    smoothScroll(userScrollX, 100, 100, source, 'right', SPEED_INSTANT);
+    percentBasedSmoothScroll(userScrollX, 100, 100, source, 'right', SPEED_INSTANT);
     await raf();
     window.scrollTo(initialX, initialY);
   }
diff --git a/third_party/blink/web_tests/fast/scroll-behavior/smooth-scroll/scroll-during-selection.html b/third_party/blink/web_tests/fast/scroll-behavior/smooth-scroll/scroll-during-selection.html
index 69c8184..1da0fc65 100644
--- a/third_party/blink/web_tests/fast/scroll-behavior/smooth-scroll/scroll-during-selection.html
+++ b/third_party/blink/web_tests/fast/scroll-behavior/smooth-scroll/scroll-during-selection.html
@@ -29,17 +29,14 @@
       await mouseMoveTo(40, 20);
 
       // Scroll before releasing the mouse.
-      const expectedScrollY = 40;
-      const { y: pixelsToScrollY } = calculatePixelsToScroll(
-        document.scrollingElement, 0, expectedScrollY
-      );
-      await smoothScroll(
+      const pixelsToScrollY = 40;
+      await percentBasedSmoothScroll(
         pixelsToScrollY, 40, 20, GestureSourceType.MOUSE_INPUT, 'down',
         SPEED_INSTANT
       );
       await waitForAnimationEndTimeBased(() => { return window.scrollY; });
       // Windows and Linux values appear to be slightly different
-      assert_approx_equals(window.scrollY, expectedScrollY, 0.5);
+      assert_approx_equals(window.scrollY, pixelsToScrollY, 0.5);
   }, "This test verifies that text selection does not prevent smooth " +
     "scrolls running on the main thread.");
 </script>
diff --git a/third_party/blink/web_tests/fast/scrolling/scroll-in-scaled-page-with-overflow-hidden.html b/third_party/blink/web_tests/fast/scrolling/scroll-in-scaled-page-with-overflow-hidden.html
index d9d5560..ada5234 100644
--- a/third_party/blink/web_tests/fast/scrolling/scroll-in-scaled-page-with-overflow-hidden.html
+++ b/third_party/blink/web_tests/fast/scrolling/scroll-in-scaled-page-with-overflow-hidden.html
@@ -37,11 +37,8 @@
       // be scrolled programmatically).
       assert_equals(window.visualViewport.pageTop, 0);
 
-      const { y: pixelsToScrollY } = calculatePixelsToScroll(
-        document.scrollingElement, 0, 700
-      );
-      await smoothScroll(
-        pixelsToScrollY, 100, 100, MOUSE_INPUT, 'down', SPEED_INSTANT
+      await percentBasedSmoothScroll(
+        700, 100, 100, MOUSE_INPUT, 'down', SPEED_INSTANT
       );
       await waitForAnimationEndTimeBased(() => {
         return window.visualViewport.pageTop;
diff --git a/third_party/blink/web_tests/platform/generic/virtual/stable/webexposed/feature-policy-features-expected.txt b/third_party/blink/web_tests/platform/generic/virtual/stable/webexposed/feature-policy-features-expected.txt
index aeee2db..de1703fa 100644
--- a/third_party/blink/web_tests/platform/generic/virtual/stable/webexposed/feature-policy-features-expected.txt
+++ b/third_party/blink/web_tests/platform/generic/virtual/stable/webexposed/feature-policy-features-expected.txt
@@ -21,6 +21,7 @@
 ch-ua-platform
 ch-ua-platform-version
 ch-ua-wow64
+ch-viewport-height
 ch-viewport-width
 ch-width
 clipboard-read
diff --git a/third_party/blink/web_tests/virtual/conversions-debug-mode/README.md b/third_party/blink/web_tests/virtual/conversions-debug-mode/README.md
new file mode 100644
index 0000000..6a2bbab
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/conversions-debug-mode/README.md
@@ -0,0 +1,6 @@
+# Tests for [Attribution Reporting
+APIs](https://github.com/WICG/attribution-reporting-api)
+
+This runs the tests for the attribution reporting API. Since the API adds noise
+and delay to the reports, the `conversions-debug-mode` switch needs to be passed
+as command line argument.
diff --git a/third_party/blink/web_tests/wpt_internal/attribution-reporting/README.md b/third_party/blink/web_tests/wpt_internal/attribution-reporting/README.md
new file mode 100644
index 0000000..7f32cd1
--- /dev/null
+++ b/third_party/blink/web_tests/wpt_internal/attribution-reporting/README.md
@@ -0,0 +1,149 @@
+# Attribution Reporting Tests
+
+## Table of Contents
+
+1.  [Overview](#overview)
+2.  [General Guidelines](#general-guidelines)
+3.  [Helper functions](#helper-functions)
+4.  [Sample Test](#sample-test)
+5.  [Running Tests](#running-tests)
+6.  [Server Code](#server-code)
+
+## Overview
+
+The primary objective here is to be able to test the Attribution Reporting APIs.
+This involves testing
+[Event Level Reporting APIs](https://github.com/WICG/conversion-measurement-api/blob/main/EVENT.md)
+and
+[Aggregatable Reporting APIs](https://github.com/WICG/conversion-measurement-api/blob/main/AGGREGATE.md).
+Please refer to the relevant documentation for API details.
+
+## General Guidelines
+
+1.  These tests are currently internal only. They must be added to
+    `wpt_internal/attribution-reporting/` directory. Please note that if tests
+    are added to a different directory, they could run in parallel, This could
+    cause issues with shared stash. For this reason, **add all Attribution
+    Reporting tests to this directory only**.
+2.  The tests rely on using server-side stash for storing cross-request data.
+    Each test is expected to clean up after itself. Specific helper methods are
+    provided to achieve this (Described below).
+3.  All tests must be
+    [WPT JavaScript Tests](https://web-platform-tests.org/writing-tests/testharness.html)
+4.  The test files should end with `.sub.https.html` to access information like
+    `host`, etc. Please refer to the
+    [Server Features](https://web-platform-tests.org/writing-tests/server-features.html#tests-involving-multiple-origins)
+    section in the WPT docs for more details.
+
+## Helper functions
+
+A set of helper functions are available in `resources/helpers.js` for your
+convenience. They can be included in tests by adding the following line to your
+test. The examples are for Event-Levelt Reporting but the same can be applied
+for Aggregatable Reporting.
+
+```html
+<script src="resources/helpers.js"></script>
+```
+
+### resetEventLevelReports and resetAggregatableReports
+
+Removes any data from the stash. This is recommended to run at the beginning of
+your test to ensure you have a fresh stash for your test. You can use this as
+
+```javascript
+// then syntax
+resetEventLevelReports().then(callback...);
+
+// await syntax
+await resetEventLevelReports();
+```
+
+### registerAttributionSrc
+
+This works to register a source or a trigger. You just need to pass in the name
+of the header and the body of the header.
+
+```javascript
+const sourceEvent = {...};
+registerAttributionSrc('Attribution-Reporting-Register-Source', sourceEvent);
+```
+
+The
+[wptserve Pipes](https://web-platform-tests.org/writing-tests/server-pipes.html)
+are used behind the scenes. Pipes are functions that may be used when serving
+files to alter parts of the response.
+
+### delay
+
+A simple delay function that takes time in ms and waits for that long.
+
+```javascript
+// then syntax
+delay(100).then(callback...);
+
+// await syntax
+await delay(100);
+```
+
+### pollEventLevelReports and pollAggregatableReports
+
+Polls the server for Event-Level Reports or Aggregatable Reports respectively.
+An interval is accepted which tells the function how much time (in ms) to wait
+in between requests. The method keeps polling until at least 1 report is
+returned by the server or the test times out. Please note that receiving reports
+from the server is a destructive operation on the server-side. This would
+essentially clear the server of all the reports.
+
+```javascript
+// then syntax
+pollEventLevelReports(100).then(callback...);
+
+// await syntax
+await pollEventLevelReports(100);
+```
+
+## Sample test
+
+Please refer to the `simple-event-level-report-test.sub.https.html` test. It is
+a basic test that utilizes helpers for Event-Level reports.
+
+## Running Tests
+
+Attribution Reporting APIs add noise to the report content and delay to report
+delivery. In order for the tests to run without this noise and delay, chrome
+must run with command-line switch `--conversions-debug-mode`. For this reason,
+all Attribution Reporting tests are virtual tests. You can run the tests by
+
+```shell
+# Build WPT
+autoninja -C out/Default blink_tests
+
+# Run all Attribution Reporting tests
+third_party/blink/tools/run_web_tests.py -t Default virtual/conversions-debug-mode/wpt_internal/attribution-reporting
+
+# Run a single test
+third_party/blink/tools/run_web_tests.py -t Default virtual/conversions-debug-mode/wpt_internal/attribution-reporting/<test-name>.sub.https.html
+```
+
+## Server Code
+
+All the server code lives in `external/wpt/.well-known/attribution-reporting/`
+directory. This is due to the fact that the browser POSTs the reports to
+`/.well-known/...` endpoint. The files of interest are
+
+```shell
+/.well-known/attribution-reporting/report-event-attribution
+/.well-known/attribution-reporting/report-aggregate-attribution
+```
+
+These files don't have a `.py` extension but must be treated as python files.
+They are registered as python scripts with the WPT server router. You can check
+the
+[tools/serve/serve.py](https://github.com/web-platform-tests/wpt/blob/master/tools/serve/serve.py#L573-L574)
+file for details.
+
+This code handles receiving the reports from the browser and the returning the
+reports when requested. The server utilizes
+[WPT Python Handlers](https://web-platform-tests.org/writing-tests/python-handlers/index.html)
+to achieve this.
diff --git a/third_party/blink/web_tests/wpt_internal/attribution-reporting/resources/blank.html b/third_party/blink/web_tests/wpt_internal/attribution-reporting/resources/blank.html
new file mode 100644
index 0000000..a3c3a46
--- /dev/null
+++ b/third_party/blink/web_tests/wpt_internal/attribution-reporting/resources/blank.html
@@ -0,0 +1,2 @@
+<!DOCTYPE html>
+<title>Empty doc</title>
diff --git a/third_party/blink/web_tests/wpt_internal/attribution-reporting/resources/helpers.js b/third_party/blink/web_tests/wpt_internal/attribution-reporting/resources/helpers.js
new file mode 100644
index 0000000..ce96c6aa
--- /dev/null
+++ b/third_party/blink/web_tests/wpt_internal/attribution-reporting/resources/helpers.js
@@ -0,0 +1,65 @@
+/**
+ * Helper functions for attribution reporting API tests.
+ */
+
+const eventLevelReportsUrl =
+    '/.well-known/attribution-reporting/report-event-attribution';
+const aggregatableReportsUrl =
+    '/.well-known/attribution-reporting/report-aggregate-attribution';
+
+/**
+ * Method to clear the stash. Takes the URL as parameter. This could be for
+ * event-level or aggregatable reports.
+ */
+const resetAttributionReports = url => {
+  url = `${url}?clear_stash=true`;
+  const options = {
+    method: 'POST',
+  };
+  return fetch(url, options);
+};
+
+const resetEventLevelReports = () =>
+    resetAttributionReports(eventLevelReportsUrl);
+const resetAggregatableReports = () =>
+    resetAttributionReports(aggregatableReportsUrl);
+
+const pipeHeaderPattern = /[,)]/g;
+
+/**
+ * Registers either a source or trigger.
+ */
+const registerAttributionSrc = (header, body) => {
+  const url = new URL('resources/blank.html', window.location);
+  // , and ) in header values must be escaped with \
+  url.searchParams.set(
+      'pipe',
+      `header(${header},${
+          JSON.stringify(body).replace(pipeHeaderPattern, '\\$&')})`);
+  const image = document.createElement('img');
+  image.setAttribute('attributionsrc', url);
+};
+
+/**
+ * Delay method that waits for prescribed number of milliseconds.
+ */
+const delay = ms => new Promise(resolve => step_timeout(resolve, ms));
+
+/**
+ * Method that polls a particular URL every interval for reports. Once reports
+ * are received, returns the payload as promise.
+ */
+const pollAttributionReports = async (url, interval) => {
+  const resp = await fetch(url);
+  const payload = await resp.json();
+  if (payload.reports.length === 0) {
+    await delay(interval);
+    return pollAttributionReports(url, interval);
+  }
+  return new Promise(resolve => resolve(payload));
+};
+
+const pollEventLevelReports = interval =>
+    pollAttributionReports(eventLevelReportsUrl, interval);
+const pollAggregatableReports = interval =>
+    pollAttributionReports(aggregatableReportsUrl, interval);
diff --git a/third_party/blink/web_tests/wpt_internal/attribution-reporting/simple-event-level-report-test.sub.https.html b/third_party/blink/web_tests/wpt_internal/attribution-reporting/simple-event-level-report-test.sub.https.html
new file mode 100644
index 0000000..cb9f5485
--- /dev/null
+++ b/third_party/blink/web_tests/wpt_internal/attribution-reporting/simple-event-level-report-test.sub.https.html
@@ -0,0 +1,37 @@
+<!doctype html>
+<meta charset=utf-8>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/helpers.js"></script>
+<script>
+promise_test(async t => {
+  const interval = 100;
+  const sourceEvent = {
+    source_event_id: '9153495207648170476',
+    destination: `https://{{host}}`,
+  };
+  const trigger = {event_trigger_data: [{'trigger_data': '2'}]};
+  await resetEventLevelReports();
+  registerAttributionSrc('Attribution-Reporting-Register-Source', sourceEvent);
+  // Adds delay to reduce chance of race condition between source registration and
+  // trigger registration.
+  await delay(interval);
+  registerAttributionSrc(
+      'Attribution-Reporting-Register-Trigger', trigger);
+  const payload = await pollEventLevelReports(interval);
+  assert_equals(payload.reports.length, 1);
+  const report = JSON.parse(payload.reports[0]);
+  // The trigger data is sanitized to "0" because event sources are limited to 1
+  // bit.
+  assert_equals(report.trigger_data, '0');
+  assert_equals(report.source_event_id, sourceEvent.source_event_id);
+  assert_equals(report.source_type, 'event');
+  assert_equals(report.attribution_destination, sourceEvent.destination);
+  assert_greater_than(report.randomized_trigger_rate, 0.0);
+  assert_less_than(report.randomized_trigger_rate, 1.0);
+  assert_own_property(report, 'report_id');
+  assert_equals(typeof report.report_id, 'string');
+  assert_not_own_property(report, 'source_debug_key');
+  assert_not_own_property(report, 'trigger_debug_key');
+}, 'Ensure attribution report is received.');
+</script>
diff --git a/third_party/metrics_proto/BUILD.gn b/third_party/metrics_proto/BUILD.gn
index 1192e875..d038d7a 100644
--- a/third_party/metrics_proto/BUILD.gn
+++ b/third_party/metrics_proto/BUILD.gn
@@ -15,6 +15,7 @@
   "chrome_os_app_list_launch_event.proto",
   "chrome_searchbox_stats.proto",
   "chrome_user_metrics_extension.proto",
+  "custom_tab_session.proto",
   "execution_context.proto",
   "extension_install.proto",
   "histogram_event.proto",
diff --git a/third_party/metrics_proto/README.chromium b/third_party/metrics_proto/README.chromium
index 9cd20b90..74b67c4 100644
--- a/third_party/metrics_proto/README.chromium
+++ b/third_party/metrics_proto/README.chromium
@@ -1,8 +1,8 @@
 Name: Metrics Protos
 Short Name: metrics_proto
 URL: This is the canonical public repository
-Version: 451237368
-Date: 2022/05/26 UTC
+Version: 452791832
+Date: 2022/06/03 UTC
 License: BSD
 Security Critical: Yes
 
diff --git a/third_party/metrics_proto/chrome_user_metrics_extension.proto b/third_party/metrics_proto/chrome_user_metrics_extension.proto
index 68f05dcc..62dbc59 100644
--- a/third_party/metrics_proto/chrome_user_metrics_extension.proto
+++ b/third_party/metrics_proto/chrome_user_metrics_extension.proto
@@ -14,6 +14,7 @@
 import "cast_logs.proto";
 import "cast_assistant_logs.proto";
 import "chrome_os_app_list_launch_event.proto";
+import "custom_tab_session.proto";
 import "histogram_event.proto";
 import "memory_leak_report.proto";
 import "omnibox_event.proto";
@@ -28,7 +29,7 @@
 import "user_action_event.proto";
 import "user_demographics.proto";
 
-// Next tag: 27
+// Next tag: 28
 message ChromeUserMetricsExtension {
   // The product (i.e. end user application) for a given UMA log.
   enum Product {
@@ -167,4 +168,11 @@
   // session. This field is uploaded as independent logs, which contain only
   // session id and core system profile fields, apart from this field.
   repeated TraceLog trace_log = 19;
+
+  // Information about a Custom Tabs session, recorded in the log when the
+  // a CCT session ended. If custom tabs are opened and closed multiple times
+  // within the same log session, only the last one will be recorded. This is
+  // used to identify applications that use Custom Tabs in an abusive way. This
+  // is specific to Android.
+  optional CustomTabSessionProto custom_tab_session = 27;
 }
diff --git a/third_party/metrics_proto/custom_tab_session.proto b/third_party/metrics_proto/custom_tab_session.proto
new file mode 100644
index 0000000..114fbee
--- /dev/null
+++ b/third_party/metrics_proto/custom_tab_session.proto
@@ -0,0 +1,42 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+syntax = "proto2";
+
+option optimize_for = LITE_RUNTIME;
+option java_package = "org.chromium.components.metrics";
+
+option java_outer_classname = "CustomTabSessionProtos";
+
+package metrics;
+
+// Stores information about a Custom Tabs session. Custom Tabs are a Chrome on
+// Android feature that allow a Chrome tab to be launched as part of another
+// application. The launching app has the ability to close the Custom Tab. This
+// proto will let us detect apps that open a Custom Tab (loading a given URL
+// with the user's cookie jar) and then kill it without the user interacting.
+// Next tag: 7
+message CustomTabSessionProto {
+  // The time the recording was taken. Recorded as seconds since epoch.
+  optional int64 time_sec = 1;
+
+  // The package name of the app that launched the Custom Tab.
+  optional string package_name = 2;
+
+  // How long the Custom Tab was open for. It is recorded in seconds with a 10%
+  // fuzzing (real numbers will be fuzzed uniformity in the range of -10% to 10%
+  // before recording). It is capped at 300 seconds (exclusive).
+  optional int32 session_duration_sec = 3;
+
+  // Did the user interact with the page displayed by the Custom Tab?
+  optional bool did_user_interact = 4;
+
+  // True if the Custom Tab was closed through a user action (for example
+  // hitting the Close Button or the Android Back button).
+  optional bool was_user_closed = 5;
+
+  // Was the Custom Tab launched as a Partial Custom Tab
+  // (go/prd-partial-custom-tabs)
+  optional bool is_partial = 6;
+}
\ No newline at end of file
diff --git a/third_party/metrics_proto/system_profile.proto b/third_party/metrics_proto/system_profile.proto
index b6d9378..ed3ee3e 100644
--- a/third_party/metrics_proto/system_profile.proto
+++ b/third_party/metrics_proto/system_profile.proto
@@ -435,10 +435,11 @@
     // The drive that the current user data directory was loaded from.
     optional Drive user_data_drive = 17;
 
-    // Type of TPM on the device. This field is only filled in on ChromeOS. This
-    // includes Chromebooks with TPM1.2 or GSC (cr50 and ti50), and also
-    // includes flex devices (ChromeOS installed on devices with other OS),
-    // which has TPM type "runtime selection".
+    // Type of TPM on the device. This field is only filled in on ChromeOS
+    // devices (both CrOS and LaCrOS platforms). This includes Chromebooks with
+    // TPM1.2 or GSC (cr50 and ti50), and also includes flex devices (ChromeOS
+    // installed on devices with other OS), which has TPM type "runtime
+    // selection".
     enum TpmType {
       TPM_TYPE_UNKNOWN = 0;
       TPM_TYPE_1 = 1;
diff --git a/third_party/mockito/proguard.flags b/third_party/mockito/proguard.flags
index 592aea1a..1333a47 100644
--- a/third_party/mockito/proguard.flags
+++ b/third_party/mockito/proguard.flags
@@ -9,3 +9,8 @@
 -keep class org.mockito.** {
   *;
 }
+
+# Packages that Mockito depends on:
+-keep class com.android.dx.**, net.bytebuddy.** {
+  *;
+}
diff --git a/tools/clang/pylib/clang/PRESUBMIT.py b/tools/clang/pylib/clang/PRESUBMIT.py
index 6315b5af..a2b798e 100644
--- a/tools/clang/pylib/clang/PRESUBMIT.py
+++ b/tools/clang/pylib/clang/PRESUBMIT.py
@@ -14,6 +14,7 @@
       input_api.canned_checks.RunUnitTestsInDirectory(input_api,
                                                       output_api,
                                                       '.', [r'^.+_test\.py$'],
+                                                      run_on_python2=False,
                                                       skip_shebang_check=True))
 
   return results
diff --git a/tools/mb/mb.py b/tools/mb/mb.py
index 0cacdac..2096a3f 100755
--- a/tools/mb/mb.py
+++ b/tools/mb/mb.py
@@ -1723,8 +1723,10 @@
       cmdline += [
           vpython_exe, '../../build/android/test_wrapper/logdog_wrapper.py',
           '--target', target, '--logdog-bin-cmd',
-          '../../.task_template_packages/logdog_butler', '--store-tombstones'
+          '../../.task_template_packages/logdog_butler'
       ]
+      if test_type != 'junit_test':
+        cmdline += ['--store-tombstones']
       if clang_coverage or java_coverage:
         cmdline += ['--coverage-dir', '${ISOLATED_OUTDIR}']
     elif is_fuchsia and test_type != 'script':
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml
index 7d830757..8b2910f 100644
--- a/tools/metrics/histograms/enums.xml
+++ b/tools/metrics/histograms/enums.xml
@@ -40831,6 +40831,16 @@
   <int value="2" label="CONTENT_DISMISSED"/>
 </enum>
 
+<enum name="FencedFrameCreationOutcome">
+  <int value="0" label="Success (default mode)"/>
+  <int value="1" label="Success (opaque ads mode)"/>
+  <int value="2" label="Required sandbox flags not set"/>
+  <int value="3" label="Mode incompatible with parent's mode"/>
+  <int value="4" label="Load in an insecure context"/>
+  <int value="5" label="URL is incompatible with mode=default"/>
+  <int value="6" label="URL is incompatible with mode=opaque ads"/>
+</enum>
+
 <enum name="FetchFailedReasonOrResourceRequestBlockedReason">
   <int value="0" label="kRedirectToDataUrlWithImpermissibleFetchMode"/>
   <int value="1" label="kContentSecurityPolicyViolation"/>
@@ -56938,6 +56948,7 @@
   <int value="-642346675" label="DesktopPWAsAttentionBadgingCrOS:enabled"/>
   <int value="-641846393" label="WebViewLegacyTlsSupport:enabled"/>
   <int value="-641820371" label="EnableCustomMacPaperSizes:enabled"/>
+  <int value="-641777884" label="SharedHighlightingManager:enabled"/>
   <int value="-641719457" label="disable-compositor-touch-hit-testing"/>
   <int value="-641423897" label="CompositeAfterPaint:disabled"/>
   <int value="-640191786" label="DesktopPWAsWithoutExtensions:enabled"/>
@@ -59457,6 +59468,7 @@
   <int value="1037038569" label="OriginAgentClusterDefaultEnable:enabled"/>
   <int value="1037961753" label="CCTIncognito:disabled"/>
   <int value="1038264914" label="PerDeskShelf:enabled"/>
+  <int value="1038906720" label="SharedHighlightingManager:disabled"/>
   <int value="1041376342" label="EnableTouchpadsInDiagnosticsApp:enabled"/>
   <int value="1042202617" label="RemoteCopyReceiver:enabled"/>
   <int value="1043291220" label="OverviewSwipeToClose:disabled"/>
diff --git a/tools/metrics/histograms/metadata/ash/histograms.xml b/tools/metrics/histograms/metadata/ash/histograms.xml
index defa7c6..028270b4 100644
--- a/tools/metrics/histograms/metadata/ash/histograms.xml
+++ b/tools/metrics/histograms/metadata/ash/histograms.xml
@@ -204,6 +204,17 @@
   <token key="Theme" variants="AmbientModeThemes"/>
 </histogram>
 
+<histogram name="Ash.AmbientMode.LottieAnimationSmoothness.{Theme}" units="%"
+    expires_after="2022-12-01">
+  <owner>esum@google.com</owner>
+  <owner>xiaohuic@chromium.org</owner>
+  <summary>
+    Emitted periodically while {Theme} is rendering in ambient mode. 100%
+    represents ideal smoothness.
+  </summary>
+  <token key="Theme" variants="AmbientModeThemes"/>
+</histogram>
+
 <histogram name="Ash.AmbientMode.PhotoSource" enum="AmbientModePhotoSource"
     expires_after="2022-12-01">
   <owner>cowmoo@google.com</owner>
@@ -652,6 +663,7 @@
     expires_after="2022-11-10">
   <owner>rtinkoff@google.com</owner>
   <owner>jiamingc@google.com</owner>
+  <owner>cros-status-area-eng@google.com</owner>
   <summary>
     Recorded when a calendar fetch returns. The duration of the fetch request.
   </summary>
@@ -659,11 +671,16 @@
 
 <histogram name="Ash.Calendar.FetchEvents.MaxDistanceBrowsed" units="months"
     expires_after="2023-04-10">
+  <obsolete>
+    This metric was originally recording something slightly different from what
+    the name suggests. Replaced with Ash.Calendar.MaxDistanceBrowsed.
+  </obsolete>
   <owner>rtinkoff@google.com</owner>
   <owner>jiamingc@google.com</owner>
+  <owner>cros-status-area-eng@google.com</owner>
   <summary>
     Recorded when the calendar is closed. The furthest 'distance' (in months)
-    from today's date to which the user browsed while the calendar was open.
+    from today's date to which a users fetch request succeeded.
   </summary>
 </histogram>
 
@@ -674,6 +691,7 @@
   </obsolete>
   <owner>rtinkoff@google.com</owner>
   <owner>jiamingc@google.com</owner>
+  <owner>cros-status-area-eng@google.com</owner>
   <summary>
     Recorded whenever calendar events are requested, and we can't do the fetch
     because a a resource we need doesn't exist or is not available.
@@ -684,6 +702,7 @@
     expires_after="2022-11-10">
   <owner>rtinkoff@google.com</owner>
   <owner>jiamingc@google.com</owner>
+  <owner>cros-status-area-eng@google.com</owner>
   <summary>
     Recorded whenever calendar events are requested, and no fetch is required
     because the fetch has already returned.
@@ -694,6 +713,7 @@
     enum="CalendarEventFetchApiError" expires_after="2022-11-10">
   <owner>rtinkoff@google.com</owner>
   <owner>jiamingc@google.com</owner>
+  <owner>cros-status-area-eng@google.com</owner>
   <summary>
     Recorded whenever calendar events are fetched. The recorded enum value
     indicates the return code of the fetch request.
@@ -704,6 +724,7 @@
     expires_after="2023-04-14">
   <owner>rtinkoff@google.com</owner>
   <owner>jiamingc@google.com</owner>
+  <owner>cros-status-area-eng@google.com</owner>
   <summary>
     Recorded when a calendar fetch returns. The total size of the month's
     events.
@@ -714,6 +735,7 @@
     expires_after="2022-11-10">
   <owner>rtinkoff@google.com</owner>
   <owner>jiamingc@google.com</owner>
+  <owner>cros-status-area-eng@google.com</owner>
   <summary>
     Recorded whenever calendar events are requested, and the request went too
     long without a response.
@@ -724,12 +746,23 @@
     expires_after="2023-04-13">
   <owner>rtinkoff@google.com</owner>
   <owner>jiamingc@google.com</owner>
+  <owner>cros-status-area-eng@google.com</owner>
   <summary>
     Recorded when the calendar is closed. The total number of months cached for
     calendar events while it was open.
   </summary>
 </histogram>
 
+<histogram name="Ash.Calendar.MaxDistanceBrowsed" units="months"
+    expires_after="2023-02-16">
+  <owner>newcomer@chromium.org</owner>
+  <owner>cros-status-area-eng@google.com</owner>
+  <summary>
+    Records the maximum absolute distance from today that a user has browsed in
+    the calendar. Recorded when the calendar is closed.
+  </summary>
+</histogram>
+
 <histogram name="Ash.Calendar.MonthDwellTime" units="ms"
     expires_after="2023-02-16">
   <owner>kradtke@chromium.org</owner>
@@ -783,6 +816,7 @@
   <owner>newcomer@google.com</owner>
   <owner>jiamingc@google.com</owner>
   <owner>cros-status-area-eng@google.com</owner>
+  <owner>cros-status-area-eng@google.com</owner>
   <summary>Recorded when the close event list animation is finished.</summary>
 </histogram>
 
@@ -2304,7 +2338,7 @@
 
 <histogram
     name="Ash.HotseatWidgetAnimation.AnimationSmoothness{HotseatTransitionType}"
-    units="%" expires_after="2022-07-14">
+    units="%" expires_after="2023-05-31">
   <owner>anasalazar@chromium.org</owner>
   <owner>newcomer@chromium.org</owner>
   <summary>
@@ -2318,7 +2352,7 @@
 
 <histogram
     name="Ash.HotseatWidgetAnimation.{HotseatWidgetElement}AnimationSmoothness"
-    units="%" expires_after="2022-07-14">
+    units="%" expires_after="2023-05-31">
   <owner>anasalazar@chromium.org</owner>
   <owner>newcomer@chromium.org</owner>
   <summary>
@@ -2356,7 +2390,7 @@
 </histogram>
 
 <histogram name="Ash.InteractiveWindowResize.TimeToPresent" units="ms"
-    expires_after="2022-04-17">
+    expires_after="2023-06-01">
   <owner>oshima@chromium.org</owner>
   <owner>mukai@chromium.org</owner>
   <summary>
@@ -2367,7 +2401,7 @@
 </histogram>
 
 <histogram name="Ash.InteractiveWindowResize.TimeToPresent.MaxLatency"
-    units="ms" expires_after="2021-12-10">
+    units="ms" expires_after="2023-06-01">
   <owner>oshima@chromium.org</owner>
   <owner>mukai@chromium.org</owner>
   <summary>
@@ -2560,7 +2594,7 @@
 </histogram>
 
 <histogram base="true" name="Ash.LoginAnimation.Duration" units="ms"
-    expires_after="2022-04-17">
+    expires_after="2023-06-02">
   <owner>oshima@chromium.org</owner>
   <owner>chromeos-wmp@google.com</owner>
 <!-- Name completed by histogram_suffixes
@@ -2587,7 +2621,7 @@
 </histogram>
 
 <histogram base="true" name="Ash.LoginAnimation.Smoothness" units="%"
-    expires_after="2022-04-24">
+    expires_after="2023-06-02">
   <owner>oshima@chromium.org</owner>
   <owner>chromeos-wmp@google.com</owner>
 <!-- Name completed by histogram_suffixes
@@ -3460,7 +3494,7 @@
 </histogram>
 
 <histogram name="Ash.Rotation.AnimationSmoothness" units="%"
-    expires_after="2022-04-10">
+    expires_after="2023-06-01">
   <owner>oshima@chromium.org</owner>
   <owner>wutao@chromium.org</owner>
   <summary>
diff --git a/tools/metrics/histograms/metadata/blink/histograms.xml b/tools/metrics/histograms/metadata/blink/histograms.xml
index 12081da..2ba6e8fe 100644
--- a/tools/metrics/histograms/metadata/blink/histograms.xml
+++ b/tools/metrics/histograms/metadata/blink/histograms.xml
@@ -941,6 +941,47 @@
   </summary>
 </histogram>
 
+<histogram name="Blink.FencedFrame.CreationOrNavigationOutcome"
+    enum="FencedFrameCreationOutcome" expires_after="2023-06-01">
+  <owner>shivanisha@chromium.org</owner>
+  <owner>dom@chromium.org</owner>
+  <owner>lbrady@google.com</owner>
+  <summary>
+    Records creation and navigation outcomes for a fenced frame. This is written
+    either when a fenced frame navigates successfully, or when some issue causes
+    either the creation or navigation of a fenced frame to stop.
+  </summary>
+</histogram>
+
+<histogram name="Blink.FencedFrame.IsFrameResizedAfterSizeFrozen"
+    enum="BooleanYesNo" expires_after="2023-06-01">
+  <owner>shivanisha@chromium.org</owner>
+  <owner>dom@chromium.org</owner>
+  <owner>lbrady@google.com</owner>
+  <summary>
+    Records if a fenced frame's size is set after the size had been frozen. The
+    size of a fenced frame is frozen once it navigates to a page, and results in
+    the inner contents being scaled to fit the new dimensions without telling it
+    the frame's new size. This is only logged up to one time throughout a fenced
+    frame's life, and will not be logged if the size is never set after being
+    frozen.
+  </summary>
+</histogram>
+
+<histogram name="Blink.FencedFrame.IsOpaqueFrameSizeCoerced"
+    enum="BooleanChanged" expires_after="2023-06-01">
+  <owner>shivanisha@chromium.org</owner>
+  <owner>dom@chromium.org</owner>
+  <owner>lbrady@google.com</owner>
+  <summary>
+    Records whether an opaque-ads fenced frame's size was changed because the
+    provided size was not in the list of allowed sizes. This will not log
+    anything for a default mode fenced frame. This will log when an opaque-ads
+    fenced frame has its size frozen (which is triggered when it navigates to
+    its first page).
+  </summary>
+</histogram>
+
 <histogram name="Blink.Fetch.ReducedUserAgent" enum="Boolean"
     expires_after="M110">
   <owner>miketaylr@chromium.org</owner>
diff --git a/tools/metrics/histograms/metadata/cryptohome/histograms.xml b/tools/metrics/histograms/metadata/cryptohome/histograms.xml
index 58cff8e..f784f62d 100644
--- a/tools/metrics/histograms/metadata/cryptohome/histograms.xml
+++ b/tools/metrics/histograms/metadata/cryptohome/histograms.xml
@@ -373,14 +373,34 @@
   </summary>
 </histogram>
 
+<histogram name="Cryptohome.FreeDiskSpaceDuringLoginTotalFreedInMb" units="MiB"
+    expires_after="2023-02-01">
+  <owner>vsavu@chromium.org</owner>
+  <owner>gwendal@chromium.org</owner>
+  <owner>sarthakkukreti@chromium.org</owner>
+  <summary>
+    Background: In critically low disk space scenarios, cryptohome performs
+    cleanup before allowing login to proceed. If enough space is available, this
+    operation is skipped and nothing is reported. This cleanup is related to the
+    periodic cleanup cryptohome performes.
+
+    This histogram records how many Mebibytes of space was freed in total every
+    time login cleanup is executed.
+  </summary>
+</histogram>
+
 <histogram name="Cryptohome.FreeDiskSpaceTotalFreedInMb" units="MiB"
     expires_after="2023-02-01">
   <owner>vsavu@chromium.org</owner>
-  <owner>dlunev@chromium.org</owner>
+  <owner>gwendal@chromium.org</owner>
+  <owner>sarthakkukreti@chromium.org</owner>
   <summary>
-    In low disk space scenarios, Cryptohome performs a lot of disk space
-    operations to erase data. This is the amount of space that was released by
-    the automatic cleanup.
+    Background: In low disk space scenarios, cryptohome performs cleanup
+    periodically. If enough space is available, this operation is skipped and
+    nothing is reported.
+
+    This histogram records how many Mebibytes of space was freed in total every
+    time cleanup is executed.
   </summary>
 </histogram>
 
diff --git a/tools/metrics/histograms/metadata/others/histograms.xml b/tools/metrics/histograms/metadata/others/histograms.xml
index d4794aa..3e8fc9bf 100644
--- a/tools/metrics/histograms/metadata/others/histograms.xml
+++ b/tools/metrics/histograms/metadata/others/histograms.xml
@@ -12212,7 +12212,7 @@
 </histogram>
 
 <histogram name="Skia.SubmitRenderPasses" units="renderpasses"
-    expires_after="2022-07-09">
+    expires_after="2023-01-09">
   <owner>egdaniel@google.com</owner>
   <owner>bsalomon@google.com</owner>
   <summary>
@@ -14613,7 +14613,7 @@
 </histogram>
 
 <histogram name="WebUI.WebUIURLLoaderFactory.URLRequestLoadTime" units="ms"
-    expires_after="2022-06-01">
+    expires_after="2022-10-30">
   <owner>dpapad@chromium.org</owner>
   <owner>etienneb@chromium.org</owner>
   <owner>tluk@chromium.org</owner>
diff --git a/tools/metrics/histograms/metadata/scheduler/histograms.xml b/tools/metrics/histograms/metadata/scheduler/histograms.xml
index 047c16d4..4f93016c3 100644
--- a/tools/metrics/histograms/metadata/scheduler/histograms.xml
+++ b/tools/metrics/histograms/metadata/scheduler/histograms.xml
@@ -138,6 +138,52 @@
   </summary>
 </histogram>
 
+<histogram name="Scheduler.TaskQueueImpl.DelayedIncomingQueueSize"
+    units="tasks" expires_after="2022-09-30">
+  <owner>yafroze@google.com</owner>
+  <owner>pmonette@chromium.org</owner>
+  <owner>catan-team@chromium.org</owner>
+  <summary>
+    The size of the |delayed_incoming_queue|.
+
+    This is emitted pseudorandomly when a delayedtask is enqueued on the
+    SequenceManager's main thread.
+
+    Note: This metric merges from all named threads.
+  </summary>
+</histogram>
+
+<histogram name="Scheduler.TaskQueueImpl.MaxDelayedIncomingQueueSize"
+    units="tasks" expires_after="2022-09-30">
+  <owner>yafroze@google.com</owner>
+  <owner>pmonette@chromium.org</owner>
+  <owner>catan-team@chromium.org</owner>
+  <summary>
+    The max size of the |delayed_incoming_queue| between the time two sequential
+    samples are chosen.
+
+    This is emitted pseudorandomly when a delayedtask is enqueued on the
+    SequenceManager's main thread.
+
+    Note: This metric merges from all named threads.
+  </summary>
+</histogram>
+
+<histogram name="Scheduler.TaskQueueImpl.PostDelayedTaskDelay" units="ms"
+    expires_after="2022-09-30">
+  <owner>yafroze@google.com</owner>
+  <owner>pmonette@chromium.org</owner>
+  <owner>catan-team@chromium.org</owner>
+  <summary>
+    The delay time for the delayed tasks posted to the sequence manager.
+
+    This is emitted pseudorandomly when a delayedtask is enqueued on the
+    SequenceManager's main thread.
+
+    Note: This metric merges from all named threads.
+  </summary>
+</histogram>
+
 <histogram name="Scheduling.BeginImplFrameLatency2" units="microseconds"
     expires_after="M85">
   <owner>stanisc@chromium.org</owner>
diff --git a/tools/metrics/histograms/metadata/search/histograms.xml b/tools/metrics/histograms/metadata/search/histograms.xml
index 0c48e49..b1bc82b 100644
--- a/tools/metrics/histograms/metadata/search/histograms.xml
+++ b/tools/metrics/histograms/metadata/search/histograms.xml
@@ -966,7 +966,10 @@
 </histogram>
 
 <histogram name="Search.ContextualSearchSelectionLengthSuppression"
-    enum="ContextualSearchTapSuppression" expires_after="M72">
+    enum="ContextualSearchTapSuppression" expires_after="M104">
+  <obsolete>
+    Removed as of 06/2022
+  </obsolete>
   <owner>donnd@chromium.org</owner>
   <owner>twellington@chromium.org</owner>
   <summary>
diff --git a/tools/perf/chrome-health-presets.yaml b/tools/perf/chrome-health-presets.yaml
index d108885..87165cb 100644
--- a/tools/perf/chrome-health-presets.yaml
+++ b/tools/perf/chrome-health-presets.yaml
@@ -60,6 +60,19 @@
           - motionmark_ramp_multiply
           - motionmark_ramp_paths
           - motionmark_ramp_suits
+      - benchmark: rendering.desktop.notracing
+        configs:
+          - mac-laptop_low_end-perf
+          - mac-m1_mini_2020-perf
+          - linux-perf
+          - win-10-perf
+        stories:
+          - motionmark_ramp_composite
+      - benchmark: rendering.mobile.notracing
+        configs:
+          - android-pixel4-perf
+        stories:
+          - motionmark_ramp_composite
   speedometer2:
     telemetry_batch_experiment:
       - benchmark: speedometer2-chrome-health
@@ -102,6 +115,19 @@
           - motionmark_ramp_multiply
           - motionmark_ramp_paths
           - motionmark_ramp_suits
+      - benchmark: rendering.desktop.notracing
+        configs:
+          - mac-laptop_low_end-perf
+          - mac-m1_mini_2020-perf
+          - linux-perf
+          - win-10-perf
+        stories:
+          - motionmark_ramp_composite
+      - benchmark: rendering.mobile.notracing
+        configs:
+          - android-pixel4-perf
+        stories:
+          - motionmark_ramp_composite
   cwv-ch:
     telemetry_batch_experiment:
       - benchmark: loading.desktop
diff --git a/tools/perf/core/perf_data_generator.py b/tools/perf/core/perf_data_generator.py
index d507ff67..58b9b5b 100755
--- a/tools/perf/core/perf_data_generator.py
+++ b/tools/perf/core/perf_data_generator.py
@@ -655,33 +655,6 @@
         },
         'perf_trigger': False,
     },
-    'win32-builder-perf': {
-        'additional_compile_targets': ['chromedriver', 'chromium_builder_perf'],
-        'tests': [{
-            'name': 'chrome_sizes',
-            'isolate': 'chrome_sizes',
-            'type': TEST_TYPES.GENERIC,
-            'resultdb': {
-                'has_native_resultdb_integration': True,
-            },
-        }],
-        'dimension': {
-            'cpu': 'x86',
-            'os': 'Windows',
-            'pool': 'chrome.tests',
-        },
-        'perf_trigger':
-        False,
-    },
-    'win32-builder-perf-pgo': {
-        'additional_compile_targets': ['chromium_builder_perf'],
-        'dimension': {
-            'cpu': 'x86',
-            'os': 'Windows',
-            'pool': 'chrome.tests',
-        },
-        'perf_trigger': False,
-    },
     'win64-builder-perf': {
         'additional_compile_targets': ['chromedriver', 'chromium_builder_perf'],
         'tests': [{
diff --git a/tools/perf/core/perf_json_config_validator.py b/tools/perf/core/perf_json_config_validator.py
index 9bb0830f..4c921cd 100644
--- a/tools/perf/core/perf_json_config_validator.py
+++ b/tools/perf/core/perf_json_config_validator.py
@@ -37,7 +37,6 @@
     'linux-builder-perf': {'chrome.tests'},
     'mac-arm-builder-perf': {'chrome.tests'},
     'mac-builder-perf': {'chrome.tests'},
-    'win32-builder-perf': {'chrome.tests'},
     'win64-builder-perf': {'chrome.tests'},
 }
 _VALID_WEBVIEW_BROWSERS = {
diff --git a/tools/run-swarmed.py b/tools/run-swarmed.py
index d10ff2c..fd19b4f 100755
--- a/tools/run-swarmed.py
+++ b/tools/run-swarmed.py
@@ -115,9 +115,10 @@
     # These flags are recognized by our test runners, but do not work
     # when running custom scripts.
     runner_args += [
-        '--test-launcher-summary-output=${ISOLATED_OUTDIR}/output.json',
-        '--system-log-file=${ISOLATED_OUTDIR}/system_log'
+        '--test-launcher-summary-output=${ISOLATED_OUTDIR}/output.json'
     ]
+    if 'junit' not in args.target_name:
+      runner_args += ['--system-log-file=${ISOLATED_OUTDIR}/system_log']
   if args.gtest_filter:
     runner_args.append('--gtest_filter=' + args.gtest_filter)
   if args.gtest_repeat:
diff --git a/ui/base/test/skia_gold_pixel_diff.cc b/ui/base/test/skia_gold_pixel_diff.cc
index 597caf9c..e8e3a54d 100644
--- a/ui/base/test/skia_gold_pixel_diff.cc
+++ b/ui/base/test/skia_gold_pixel_diff.cc
@@ -83,7 +83,7 @@
   argv.insert(argv.begin() + 1, args.begin(), args.end());
 }
 
-void FillInSystemEnvironment(base::Value::DictStorage& ds) {
+void FillInSystemEnvironment(base::Value::Dict& ds) {
   std::string processor = "unknown";
 #if defined(ARCH_CPU_X86)
   processor = "x86";
@@ -93,8 +93,8 @@
   LOG(WARNING) << "Unknown Processor.";
 #endif
 
-  ds["system"] = base::Value(SkiaGoldPixelDiff::GetPlatform());
-  ds["processor"] = base::Value(processor);
+  ds.Set("system", SkiaGoldPixelDiff::GetPlatform());
+  ds.Set("processor", processor);
 }
 
 // Fill in test environment to the keys_file. The format is json.
@@ -103,7 +103,7 @@
 // should be filled in. Eg: operating system, graphics card, processor
 // architecture, screen resolution, etc.
 bool FillInTestEnvironment(const base::FilePath& keys_file) {
-  base::Value::DictStorage ds;
+  base::Value::Dict ds;
   FillInSystemEnvironment(ds);
   base::Value root(std::move(ds));
   std::string content;
diff --git a/ui/base/x/BUILD.gn b/ui/base/x/BUILD.gn
index e508dbaf..0815385d 100644
--- a/ui/base/x/BUILD.gn
+++ b/ui/base/x/BUILD.gn
@@ -90,6 +90,7 @@
     "//ui/gfx/geometry",
     "//ui/gfx/x",
     "//ui/platform_window/common",
+    "//ui/strings:ui_strings_grit",
   ]
 
   if (is_linux || is_chromeos) {
diff --git a/ui/base/x/x11_display_util.cc b/ui/base/x/x11_display_util.cc
index b0c10088..b703af7 100644
--- a/ui/base/x/x11_display_util.cc
+++ b/ui/base/x/x11_display_util.cc
@@ -12,6 +12,7 @@
 #include "base/compiler_specific.h"
 #include "base/logging.h"
 #include "base/strings/string_util.h"
+#include "ui/base/l10n/l10n_util.h"
 #include "ui/base/x/x11_util.h"
 #include "ui/display/util/display_util.h"
 #include "ui/display/util/edid_parser.h"
@@ -22,6 +23,7 @@
 #include "ui/gfx/x/randr.h"
 #include "ui/gfx/x/x11_atom_cache.h"
 #include "ui/gfx/x/xproto_util.h"
+#include "ui/strings/grit/ui_strings.h"
 
 namespace ui {
 
@@ -286,8 +288,14 @@
       explicit_primary_display_index = displays.size();
 
     const std::string name(output_info->name.begin(), output_info->name.end());
-    if (base::StartsWith(name, "eDP") || base::StartsWith(name, "LVDS"))
+    if (base::StartsWith(name, "eDP") || base::StartsWith(name, "LVDS")) {
       display::SetInternalDisplayIds({display_id});
+      // Use localized variant of "Built-in display" for internal displays.
+      // This follows the ozone DRM behavior (i.e. ChromeOS).
+      display.set_label(l10n_util::GetStringUTF8(IDS_DISPLAY_NAME_INTERNAL));
+    } else {
+      display.set_label(edid_parser.display_name());
+    }
 
     auto monitor_iter =
         output_to_monitor.find(static_cast<x11::RandR::Output>(output_id));
diff --git a/ui/gl/delegated_ink_point_renderer_gpu_unittest.cc b/ui/gl/delegated_ink_point_renderer_gpu_unittest.cc
index a1eaa516..12d4fe0 100644
--- a/ui/gl/delegated_ink_point_renderer_gpu_unittest.cc
+++ b/ui/gl/delegated_ink_point_renderer_gpu_unittest.cc
@@ -97,8 +97,8 @@
  protected:
   void SetUp() override {
     // Without this, the following check always fails.
-    gl::init::InitializeGLNoExtensionsOneOff(/*init_bindings=*/true,
-                                             /*system_device_id=*/0);
+    display_ = gl::init::InitializeGLNoExtensionsOneOff(/*init_bindings=*/true,
+                                                        /*system_device_id=*/0);
     if (!gl::DirectCompositionSurfaceWin::GetDirectCompositionDevice()) {
       LOG(WARNING)
           << "GL implementation not using DirectComposition, skipping test.";
@@ -124,7 +124,7 @@
     context_ = nullptr;
     if (surface_)
       DestroySurface(std::move(surface_));
-    gl::init::ShutdownGL(false);
+    gl::init::ShutdownGL(display_, false);
   }
 
  private:
@@ -164,6 +164,7 @@
   HWND parent_window_;
   scoped_refptr<DirectCompositionSurfaceWin> surface_;
   scoped_refptr<GLContext> context_;
+  GLDisplay* display_ = nullptr;
 
   // Used as a reference when making DelegatedInkMetadatas based on previously
   // sent points.
diff --git a/ui/gl/direct_composition_surface_win.cc b/ui/gl/direct_composition_surface_win.cc
index 6c524088..480c380 100644
--- a/ui/gl/direct_composition_surface_win.cc
+++ b/ui/gl/direct_composition_surface_win.cc
@@ -410,7 +410,7 @@
 }
 
 // static
-void DirectCompositionSurfaceWin::InitializeOneOff() {
+void DirectCompositionSurfaceWin::InitializeOneOff(GLDisplayEGL* display) {
   DCHECK(!g_dcomp_device);
 
   if (base::CommandLine::ForCurrentProcess()->HasSwitch(
@@ -431,7 +431,7 @@
 
   // EGL_KHR_no_config_context surface compatibility is required to be able to
   // MakeCurrent with the default pbuffer surface.
-  if (!GLSurfaceEGL::GetGLDisplayEGL()->IsEGLNoConfigContextSupported()) {
+  if (!display->IsEGLNoConfigContextSupported()) {
     DLOG(ERROR) << "EGL_KHR_no_config_context not supported";
     return;
   }
diff --git a/ui/gl/direct_composition_surface_win.h b/ui/gl/direct_composition_surface_win.h
index beb4172..0e2a094 100644
--- a/ui/gl/direct_composition_surface_win.h
+++ b/ui/gl/direct_composition_surface_win.h
@@ -60,7 +60,7 @@
   DirectCompositionSurfaceWin& operator=(const DirectCompositionSurfaceWin&) =
       delete;
 
-  static void InitializeOneOff();
+  static void InitializeOneOff(GLDisplayEGL* display);
   static void ShutdownOneOff();
 
   static const Microsoft::WRL::ComPtr<IDCompositionDevice2>&
diff --git a/ui/gl/direct_composition_surface_win_unittest.cc b/ui/gl/direct_composition_surface_win_unittest.cc
index 67ede44..a7b6ec9 100644
--- a/ui/gl/direct_composition_surface_win_unittest.cc
+++ b/ui/gl/direct_composition_surface_win_unittest.cc
@@ -123,8 +123,8 @@
     fake_power_monitor_source_.SetOnBatteryPower(true);
 
     // Without this, the following check always fails.
-    gl::init::InitializeGLNoExtensionsOneOff(/*init_bindings=*/true,
-                                             /*system_device_id=*/0);
+    display_ = gl::init::InitializeGLNoExtensionsOneOff(
+        /*init_bindings=*/true, /*system_device_id=*/0);
     if (!DirectCompositionSurfaceWin::GetDirectCompositionDevice()) {
       LOG(WARNING) << "DirectComposition not supported, skipping test.";
       return;
@@ -142,7 +142,7 @@
     context_ = nullptr;
     if (surface_)
       DestroySurface(std::move(surface_));
-    gl::init::ShutdownGL(false);
+    gl::init::ShutdownGL(display_, false);
   }
 
   scoped_refptr<DirectCompositionSurfaceWin>
@@ -176,6 +176,7 @@
   scoped_refptr<DirectCompositionSurfaceWin> surface_;
   scoped_refptr<GLContext> context_;
   base::test::ScopedPowerMonitorTestSource fake_power_monitor_source_;
+  GLDisplay* display_ = nullptr;
 };
 
 TEST_F(DirectCompositionSurfaceTest, TestMakeCurrent) {
diff --git a/ui/gl/egl_api_unittest.cc b/ui/gl/egl_api_unittest.cc
index ffd94d0..d6b8588 100644
--- a/ui/gl/egl_api_unittest.cc
+++ b/ui/gl/egl_api_unittest.cc
@@ -18,6 +18,7 @@
   void SetUp() override {
     fake_client_extension_string_ = "";
     fake_extension_string_ = "";
+    display_ = nullptr;
 
     g_driver_egl.fn.eglInitializeFn = &FakeInitialize;
     g_driver_egl.fn.eglTerminateFn = &FakeTerminate;
@@ -35,7 +36,7 @@
   }
 
   void TearDown() override {
-    init::ShutdownGL(false);
+    init::ShutdownGL(display_, false);
     api_.reset(nullptr);
 
     fake_client_extension_string_ = "";
@@ -50,8 +51,8 @@
       SetDisabledExtensionsEGL(disabled_extensions);
     }
     g_driver_egl.InitializeClientExtensionBindings();
-    GLSurfaceEGL::InitializeDisplay(EGLDisplayPlatform(EGL_DEFAULT_DISPLAY),
-                                    /*system_device_id=*/0);
+    display_ = GLSurfaceEGL::InitializeDisplay(
+        EGLDisplayPlatform(EGL_DEFAULT_DISPLAY), /*system_device_id=*/0);
     g_driver_egl.InitializeExtensionBindings();
   }
 
@@ -108,6 +109,7 @@
   static const char* fake_extension_string_;
   static const char* fake_client_extension_string_;
 
+  GLDisplay* display_ = nullptr;
   std::unique_ptr<RealEGLApi> api_;
 };
 
diff --git a/ui/gl/gl_display_manager.h b/ui/gl/gl_display_manager.h
index 966a871..3225c7d 100644
--- a/ui/gl/gl_display_manager.h
+++ b/ui/gl/gl_display_manager.h
@@ -23,6 +23,8 @@
 class GL_EXPORT GLDisplayManager {
  public:
   // Getter for the singleton. This will return nullptr on failure.
+  // This should only be called inside the ui/gl module. In component build,
+  // calling this outside ui/gl module returns a different instance.
   static GLDisplayManager<GLDisplayPlatform>* GetInstance() {
     static base::NoDestructor<GLDisplayManager<GLDisplayPlatform>> instance;
     return instance.get();
diff --git a/ui/gl/gl_egl_api_implementation.cc b/ui/gl/gl_egl_api_implementation.cc
index 20300b21..0261916c 100644
--- a/ui/gl/gl_egl_api_implementation.cc
+++ b/ui/gl/gl_egl_api_implementation.cc
@@ -134,8 +134,8 @@
   g_current_egl_context->SetDisabledExtensions(disabled_extensions);
 }
 
-bool InitializeExtensionSettingsOneOffEGL() {
-  return GLSurfaceEGL::InitializeExtensionSettingsOneOff();
+bool InitializeExtensionSettingsOneOffEGL(GLDisplayEGL* display) {
+  return GLSurfaceEGL::InitializeExtensionSettingsOneOff(display);
 }
 
 }  // namespace gl
diff --git a/ui/gl/gl_egl_api_implementation.h b/ui/gl/gl_egl_api_implementation.h
index e11d0dd..9dace80 100644
--- a/ui/gl/gl_egl_api_implementation.h
+++ b/ui/gl/gl_egl_api_implementation.h
@@ -11,6 +11,7 @@
 
 #include "base/memory/raw_ptr.h"
 #include "ui/gl/gl_bindings.h"
+#include "ui/gl/gl_display.h"
 #include "ui/gl/gl_export.h"
 
 namespace gl {
@@ -21,7 +22,7 @@
 GL_EXPORT void ClearBindingsEGL();
 GL_EXPORT bool GetGLWindowSystemBindingInfoEGL(GLWindowSystemBindingInfo* info);
 GL_EXPORT void SetDisabledExtensionsEGL(const std::string& disabled_extensions);
-GL_EXPORT bool InitializeExtensionSettingsOneOffEGL();
+GL_EXPORT bool InitializeExtensionSettingsOneOffEGL(GLDisplayEGL* display);
 
 class GL_EXPORT EGLApiBase : public EGLApi {
  public:
diff --git a/ui/gl/gl_surface_egl.cc b/ui/gl/gl_surface_egl.cc
index d3df5378..a70c69a 100644
--- a/ui/gl/gl_surface_egl.cc
+++ b/ui/gl/gl_surface_egl.cc
@@ -185,8 +185,6 @@
 
 namespace gl {
 
-bool GLSurfaceEGL::initialized_ = false;
-
 namespace {
 
 class EGLGpuSwitchingObserver;
@@ -392,8 +390,8 @@
     extra_display_attribs.push_back(EGL_FEATURE_ALL_DISABLED_ANGLE);
     extra_display_attribs.push_back(EGL_TRUE);
   }
-  if (system_device_id != 0 && GLSurfaceEGL::GetGLDisplayEGL()
-                                   ->IsANGLEPlatformANGLEDeviceIdSupported()) {
+  if (system_device_id != 0 &&
+      gl_display->IsANGLEPlatformANGLEDeviceIdSupported()) {
     uint32_t low_part = system_device_id & 0xffffffff;
     extra_display_attribs.push_back(EGL_PLATFORM_ANGLE_DEVICE_ID_LOW_ANGLE);
     extra_display_attribs.push_back(low_part);
@@ -994,35 +992,39 @@
 }
 
 // static
-bool GLSurfaceEGL::InitializeOneOff(EGLDisplayPlatform native_display,
-                                    uint64_t system_device_id) {
-  if (initialized_)
-    return true;
+GLDisplayEGL* GLSurfaceEGL::InitializeOneOff(EGLDisplayPlatform native_display,
+                                             uint64_t system_device_id) {
+  GLDisplayEGL* display =
+      GLDisplayManagerEGL::GetInstance()->GetDisplay(system_device_id);
+  if (display->GetDisplay() == EGL_NO_DISPLAY) {
+    // Must be called before InitializeDisplay().
+    g_driver_egl.InitializeClientExtensionBindings();
 
-  // Must be called before InitializeDisplay().
-  g_driver_egl.InitializeClientExtensionBindings();
+    display = InitializeDisplay(native_display, system_device_id);
+    if (display->GetDisplay() == EGL_NO_DISPLAY)
+      return nullptr;
 
-  GLDisplayEGL* display = InitializeDisplay(native_display, system_device_id);
-  if (display->GetDisplay() == EGL_NO_DISPLAY)
-    return false;
+    // Must be called after InitializeDisplay().
+    g_driver_egl.InitializeExtensionBindings();
 
-  // Must be called after InitializeDisplay().
-  g_driver_egl.InitializeExtensionBindings();
-
-  return InitializeOneOffCommon(display);
+    InitializeOneOffCommon(display);
+  }
+  return display;
 }
 
 // static
-bool GLSurfaceEGL::InitializeOneOffForTesting() {
+GLDisplayEGL* GLSurfaceEGL::InitializeOneOffForTesting() {
   g_driver_egl.InitializeClientExtensionBindings();
-  GLDisplayEGL* display = GetGLDisplayEGL();
+  GLDisplayEGL* display =
+      GLDisplayManagerEGL::GetInstance()->GetDisplay(GpuPreference::kDefault);
   display->SetDisplay(eglGetCurrentDisplay());
   g_driver_egl.InitializeExtensionBindings();
-  return InitializeOneOffCommon(display);
+  InitializeOneOffCommon(display);
+  return display;
 }
 
 // static
-bool GLSurfaceEGL::InitializeOneOffCommon(GLDisplayEGL* display) {
+void GLSurfaceEGL::InitializeOneOffCommon(GLDisplayEGL* display) {
   display->egl_client_extensions =
       eglQueryString(EGL_NO_DISPLAY, EGL_EXTENSIONS);
   display->egl_extensions =
@@ -1161,27 +1163,25 @@
     ui::GpuSwitchingManager::GetInstance()->AddObserver(
         g_egl_gpu_switching_observer);
   }
-
-  initialized_ = true;
-  return true;
 }
 
 // static
-bool GLSurfaceEGL::InitializeExtensionSettingsOneOff() {
-  if (!initialized_)
+bool GLSurfaceEGL::InitializeExtensionSettingsOneOff(GLDisplayEGL* display) {
+  DCHECK(display);
+  if (display->GetDisplay() == EGL_NO_DISPLAY)
     return false;
   g_driver_egl.UpdateConditionalExtensionBindings();
-  GetGLDisplayEGL()->egl_client_extensions =
+  display->egl_client_extensions =
       eglQueryString(EGL_NO_DISPLAY, EGL_EXTENSIONS);
-  GetGLDisplayEGL()->egl_extensions =
-      eglQueryString(GetGLDisplayEGL()->GetDisplay(), EGL_EXTENSIONS);
+  display->egl_extensions =
+      eglQueryString(display->GetDisplay(), EGL_EXTENSIONS);
 
   return true;
 }
 
 // static
-void GLSurfaceEGL::ShutdownOneOff() {
-  if (!initialized_) {
+void GLSurfaceEGL::ShutdownOneOff(GLDisplayEGL* display) {
+  if (!display || display->GetDisplay() == EGL_NO_DISPLAY) {
     return;
   }
 
@@ -1191,13 +1191,10 @@
     delete g_egl_gpu_switching_observer;
     g_egl_gpu_switching_observer = nullptr;
   }
-  GLDisplayEGL* display = GetGLDisplayEGL();
   angle::ResetPlatform(display->GetDisplay());
-  if (display->GetDisplay() != EGL_NO_DISPLAY) {
-    DCHECK(g_driver_egl.fn.eglTerminateFn);
-    eglTerminate(display->GetDisplay());
-    display->SetDisplay(EGL_NO_DISPLAY);
-  }
+  DCHECK(g_driver_egl.fn.eglTerminateFn);
+  eglTerminate(display->GetDisplay());
+  display->SetDisplay(EGL_NO_DISPLAY);
 
   display->egl_client_extensions = nullptr;
   display->egl_extensions = nullptr;
@@ -1214,8 +1211,6 @@
   display->egl_display_texture_share_group_supported = false;
   display->egl_create_context_client_arrays_supported = false;
   display->egl_angle_feature_control_supported = false;
-
-  initialized_ = false;
 }
 
 GLSurfaceEGL::~GLSurfaceEGL() = default;
@@ -1225,7 +1220,8 @@
 // static
 GLDisplayEGL* GLSurfaceEGL::InitializeDisplay(EGLDisplayPlatform native_display,
                                               uint64_t system_device_id) {
-  GLDisplayEGL* gl_display = GetGLDisplayEGL();
+  GLDisplayEGL* gl_display =
+      GLDisplayManagerEGL::GetInstance()->GetDisplay(system_device_id);
   if (gl_display->GetDisplay() != EGL_NO_DISPLAY) {
     return gl_display;
   }
@@ -1312,17 +1308,17 @@
 
   for (size_t disp_index = 0; disp_index < init_displays.size(); ++disp_index) {
     DisplayType display_type = init_displays[disp_index];
-    EGLDisplay display = GetDisplayFromType(
+    EGLDisplay egl_display = GetDisplayFromType(
         display_type, gl_display, enabled_angle_features,
         disabled_angle_features, disable_all_angle_features, system_device_id);
-    if (display == EGL_NO_DISPLAY) {
+    if (egl_display == EGL_NO_DISPLAY) {
       LOG(ERROR) << "EGL display query failed with error "
                  << GetLastEGLErrorString();
     }
 
     // Init ANGLE platform now that we have the global display.
     if (supports_angle) {
-      if (!angle::InitializePlatform(display)) {
+      if (!angle::InitializePlatform(egl_display)) {
         LOG(ERROR) << "ANGLE Platform initialization failed.";
       }
 
@@ -1338,7 +1334,7 @@
                           ->MaybeGetScopedDisplayUnsetForVulkan();
     }
 
-    if (!eglInitialize(display, nullptr, nullptr)) {
+    if (!eglInitialize(egl_display, nullptr, nullptr)) {
       bool is_last = disp_index == init_displays.size() - 1;
 
       LOG(ERROR) << "eglInitialize " << DisplayTypeString(display_type)
@@ -1361,7 +1357,7 @@
 
     UMA_HISTOGRAM_ENUMERATION("GPU.EGLDisplayType", display_type,
                               DISPLAY_TYPE_MAX);
-    gl_display->SetDisplay(display);
+    gl_display->SetDisplay(egl_display);
     gl_display->display_type = display_type;
     break;
   }
@@ -1392,7 +1388,7 @@
   DCHECK(!surface_);
   format_ = format;
 
-  if (!display_->GetDisplay()) {
+  if (display_->GetDisplay() == EGL_NO_DISPLAY) {
     LOG(ERROR) << "Trying to create surface with invalid display.";
     return false;
   }
diff --git a/ui/gl/gl_surface_egl.h b/ui/gl/gl_surface_egl.h
index e53b967..56d276e 100644
--- a/ui/gl/gl_surface_egl.h
+++ b/ui/gl/gl_surface_egl.h
@@ -61,11 +61,15 @@
 
   // |system_device_id| specifies which GPU to use on a multi-GPU system.
   // If its value is 0, use the default GPU of the system.
-  static bool InitializeOneOff(EGLDisplayPlatform native_display,
-                               uint64_t system_device_id);
-  static bool InitializeOneOffForTesting();
-  static bool InitializeExtensionSettingsOneOff();
-  static void ShutdownOneOff();
+  // Calling this functionm a second time on the same |system_device_id|
+  // is a no-op and returns the same GLDisplayEGL.
+  // TODO(https://crbug.com/1251724): This will be called once per display
+  // when Chrome begins to support multi-gpu rendering.
+  static GLDisplayEGL* InitializeOneOff(EGLDisplayPlatform native_display,
+                                        uint64_t system_device_id);
+  static GLDisplayEGL* InitializeOneOffForTesting();
+  static bool InitializeExtensionSettingsOneOff(GLDisplayEGL* display);
+  static void ShutdownOneOff(GLDisplayEGL* display);
   // |system_device_id| specifies which GPU to use on a multi-GPU system.
   // If its value is 0, use the default GPU of the system.
   static GLDisplayEGL* InitializeDisplay(EGLDisplayPlatform native_display,
@@ -79,8 +83,7 @@
   GLDisplayEGL* display_ = nullptr;
 
  private:
-  static bool InitializeOneOffCommon(GLDisplayEGL* display);
-  static bool initialized_;
+  static void InitializeOneOffCommon(GLDisplayEGL* display);
 };
 
 // Encapsulates an EGL surface bound to a view.
diff --git a/ui/gl/gl_surface_egl_unittest.cc b/ui/gl/gl_surface_egl_unittest.cc
index dabd89c..c27bef3 100644
--- a/ui/gl/gl_surface_egl_unittest.cc
+++ b/ui/gl/gl_surface_egl_unittest.cc
@@ -32,15 +32,18 @@
  protected:
   void SetUp() override {
 #if BUILDFLAG(IS_WIN)
-    GLSurfaceTestSupport::InitializeOneOffImplementation(
+    display_ = GLSurfaceTestSupport::InitializeOneOffImplementation(
         GLImplementationParts(kGLImplementationEGLANGLE), true);
 #else
-    GLSurfaceTestSupport::InitializeOneOffImplementation(
+    display_ = GLSurfaceTestSupport::InitializeOneOffImplementation(
         GLImplementationParts(kGLImplementationEGLGLES2), true);
 #endif
   }
 
-  void TearDown() override { gl::init::ShutdownGL(false); }
+  void TearDown() override { GLSurfaceTestSupport::ShutdownGL(display_); }
+
+ private:
+  GLDisplay* display_ = nullptr;
 };
 
 #if !defined(MEMORY_SANITIZER)
diff --git a/ui/gl/gl_utils.cc b/ui/gl/gl_utils.cc
index 2ff24c8..c98a7cd 100644
--- a/ui/gl/gl_utils.cc
+++ b/ui/gl/gl_utils.cc
@@ -11,6 +11,7 @@
 #include "base/logging.h"
 #include "build/build_config.h"
 #include "ui/gl/gl_bindings.h"
+#include "ui/gl/gl_display_manager.h"
 #include "ui/gl/gl_features.h"
 #include "ui/gl/gl_switches.h"
 
@@ -197,6 +198,24 @@
 }
 #endif  // BUILDFLAG(IS_WIN)
 
+#if defined(USE_EGL)
+void SetGpuPreferenceEGL(GpuPreference preference, uint64_t system_device_id) {
+  GLDisplayManagerEGL::GetInstance()->SetGpuPreference(preference,
+                                                       system_device_id);
+}
+
+GLDisplayEGL* GetDefaultDisplayEGL() {
+  return GLDisplayManagerEGL::GetInstance()->GetDisplay(
+      GpuPreference::kDefault);
+}
+#endif  // USE_EGL
+
+#if defined(USE_GLX)
+GLDisplayX11* GetDisplayX11(uint64_t system_device_id) {
+  return GLDisplayManagerX11::GetInstance()->GetDisplay(system_device_id);
+}
+#endif  // USE_GLX
+
 #if BUILDFLAG(IS_MAC)
 
 ScopedEnableTextureRectangleInShaderCompiler::
diff --git a/ui/gl/gl_utils.h b/ui/gl/gl_utils.h
index 9fedd3c..05bd69c 100644
--- a/ui/gl/gl_utils.h
+++ b/ui/gl/gl_utils.h
@@ -10,6 +10,7 @@
 #include "base/command_line.h"
 #include "build/build_config.h"
 #include "ui/gl/gl_export.h"
+#include "ui/gl/gpu_preference.h"
 
 #if BUILDFLAG(IS_WIN)
 #include <dxgi1_6.h>
@@ -21,6 +22,12 @@
 
 namespace gl {
 class GLApi;
+#if defined(USE_EGL)
+class GLDisplayEGL;
+#endif  // USE_EGL
+#if defined(USE_GLX)
+class GLDisplayX11;
+#endif  // USE_GLX
 
 GL_EXPORT void Crash();
 GL_EXPORT void Hang();
@@ -59,6 +66,25 @@
 void LabelSwapChainBuffers(IDXGISwapChain* swap_chain, const char* name_prefix);
 #endif
 
+// The following functions expose functionalities from GLDisplayManagerEGL
+// and GLDisplayManagerX11 for access outside the ui/gl module. This is because
+// the two GLDisplayManager classes are singletons and in component build,
+// calling GetInstance() directly returns different instances in different
+// components.
+#if defined(USE_EGL)
+// Add an entry <preference, system_device_id> to GLDisplayManagerEGL.
+GL_EXPORT void SetGpuPreferenceEGL(GpuPreference preference,
+                                   uint64_t system_device_id);
+
+// Query the default GLDisplayEGL.
+GL_EXPORT GLDisplayEGL* GetDefaultDisplayEGL();
+#endif  // USE_EGL
+
+#if defined(USE_GLX)
+// Query the GLDisplayX11 by |system_device_id|.
+GL_EXPORT GLDisplayX11* GetDisplayX11(uint64_t system_device_id);
+#endif  // USE_GLX
+
 // Temporarily allows compilation of shaders that use the
 // ARB_texture_rectangle/ANGLE_texture_rectangle extension. We don't want to
 // expose the extension to WebGL user shaders but we still need to use it for
diff --git a/ui/gl/gpu_timing_unittest.cc b/ui/gl/gpu_timing_unittest.cc
index 0b33a2ee..096b52e5 100644
--- a/ui/gl/gpu_timing_unittest.cc
+++ b/ui/gl/gpu_timing_unittest.cc
@@ -39,8 +39,8 @@
     context_ = nullptr;
     surface_ = nullptr;
     if (setup_) {
-      MockGLInterface::SetGLInterface(NULL);
-      init::ShutdownGL(false);
+      MockGLInterface::SetGLInterface(nullptr);
+      GLSurfaceTestSupport::ShutdownGL(display_);
     }
     setup_ = false;
     cpu_time_bounded_ = false;
@@ -51,7 +51,7 @@
   void SetupGLContext(const char* gl_version, const char* gl_extensions) {
     ASSERT_FALSE(setup_) << "Cannot setup GL context twice.";
     SetGLGetProcAddressProc(MockGLInterface::GetGLProcAddress);
-    GLSurfaceTestSupport::InitializeOneOffWithMockBindings();
+    display_ = GLSurfaceTestSupport::InitializeOneOffWithMockBindings();
     gl_ = std::make_unique<::testing::StrictMock<MockGLInterface>>();
     MockGLInterface::SetGLInterface(gl_.get());
 
@@ -86,6 +86,7 @@
   scoped_refptr<GLContextStub> context_;
   scoped_refptr<GLSurfaceStub> surface_;
   GPUTimingFake gpu_timing_fake_queries_;
+  GLDisplay* display_ = nullptr;
 };
 
 TEST_F(GPUTimingTest, FakeTimerTest) {
diff --git a/ui/gl/init/gl_factory.cc b/ui/gl/init/gl_factory.cc
index 01b8dd9..70fdfb35 100644
--- a/ui/gl/init/gl_factory.cc
+++ b/ui/gl/init/gl_factory.cc
@@ -15,6 +15,7 @@
 #include "base/trace_event/trace_event.h"
 #include "build/build_config.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
+#include "ui/gl/gl_display_manager.h"
 #include "ui/gl/gl_share_group.h"
 #include "ui/gl/gl_surface.h"
 #include "ui/gl/gl_utils.h"
@@ -131,8 +132,8 @@
   return GLImplementationParts(kGLImplementationNone);
 }
 
-bool InitializeGLOneOffPlatformHelper(bool init_extensions,
-                                      uint64_t system_device_id) {
+GLDisplay* InitializeGLOneOffPlatformHelper(bool init_extensions,
+                                            uint64_t system_device_id) {
   TRACE_EVENT1("gpu,startup", "gl::init::InitializeGLOneOffPlatformHelper",
                "init_extensions", init_extensions);
 
@@ -147,26 +148,30 @@
 
 }  // namespace
 
-bool InitializeGLOneOff(uint64_t system_device_id) {
+GLDisplay* InitializeGLOneOff(uint64_t system_device_id) {
   TRACE_EVENT0("gpu,startup", "gl::init::InitializeOneOff");
 
   if (!InitializeStaticGLBindingsOneOff())
-    return false;
-  if (GetGLImplementation() == kGLImplementationDisabled)
-    return true;
+    return nullptr;
+  if (GetGLImplementation() == kGLImplementationDisabled) {
+    return GLDisplayManagerEGL::GetInstance()->GetDisplay(
+        GpuPreference::kDefault);
+  }
 
   return InitializeGLOneOffPlatformHelper(true, system_device_id);
 }
 
-bool InitializeGLNoExtensionsOneOff(bool init_bindings,
-                                    uint64_t system_device_id) {
+GLDisplay* InitializeGLNoExtensionsOneOff(bool init_bindings,
+                                          uint64_t system_device_id) {
   TRACE_EVENT1("gpu,startup", "gl::init::InitializeNoExtensionsOneOff",
                "init_bindings", init_bindings);
   if (init_bindings) {
     if (!InitializeStaticGLBindingsOneOff())
-      return false;
-    if (GetGLImplementation() == kGLImplementationDisabled)
-      return true;
+      return nullptr;
+    if (GetGLImplementation() == kGLImplementationDisabled) {
+      return GLDisplayManagerEGL::GetInstance()->GetDisplay(
+          GpuPreference::kDefault);
+    }
   }
 
   return InitializeGLOneOffPlatformHelper(false, system_device_id);
@@ -196,48 +201,51 @@
 
   bool initialized = InitializeStaticGLBindings(impl);
   if (!initialized && fallback_to_software_gl) {
-    ShutdownGL(/*due_to_fallback*/ true);
+    ShutdownGL(nullptr, /*due_to_fallback*/ true);
     initialized = InitializeStaticGLBindings(GetSoftwareGLImplementation());
   }
   if (!initialized) {
-    ShutdownGL(/*due_to_fallback*/ false);
+    ShutdownGL(nullptr, /*due_to_fallback*/ false);
     return false;
   }
   return true;
 }
 
-bool InitializeGLOneOffPlatformImplementation(bool fallback_to_software_gl,
-                                              bool disable_gl_drawing,
-                                              bool init_extensions,
-                                              uint64_t system_device_id) {
+GLDisplay* InitializeGLOneOffPlatformImplementation(
+    bool fallback_to_software_gl,
+    bool disable_gl_drawing,
+    bool init_extensions,
+    uint64_t system_device_id) {
   if (IsSoftwareGLImplementation(GetGLImplementationParts()))
     fallback_to_software_gl = false;
 
-  bool initialized = InitializeGLOneOffPlatform(system_device_id);
+  GLDisplay* display = InitializeGLOneOffPlatform(system_device_id);
+  bool initialized = !!display;
   if (!initialized && fallback_to_software_gl) {
-    ShutdownGL(/*due_to_fallback*/ true);
-    initialized = InitializeStaticGLBindings(GetSoftwareGLImplementation()) &&
-                  InitializeGLOneOffPlatform(system_device_id);
+    ShutdownGL(nullptr, /*due_to_fallback=*/true);
+    if (InitializeStaticGLBindings(GetSoftwareGLImplementation())) {
+      display = InitializeGLOneOffPlatform(system_device_id);
+      initialized = !!display;
+    }
   }
   if (initialized && init_extensions) {
-    initialized = InitializeExtensionSettingsOneOffPlatform();
+    initialized = InitializeExtensionSettingsOneOffPlatform(display);
   }
 
-  if (!initialized)
-    ShutdownGL(false);
-
-  if (initialized) {
-    DVLOG(1) << "Using "
-             << GetGLImplementationGLName(GetGLImplementationParts())
-             << " GL implementation.";
-    if (disable_gl_drawing)
-      InitializeNullDrawGLBindings();
+  if (!initialized) {
+    ShutdownGL(display, false);
+    return nullptr;
   }
-  return initialized;
+
+  DVLOG(1) << "Using " << GetGLImplementationGLName(GetGLImplementationParts())
+           << " GL implementation.";
+  if (disable_gl_drawing)
+    InitializeNullDrawGLBindings();
+  return display;
 }
 
-void ShutdownGL(bool due_to_fallback) {
-  ShutdownGLPlatform();
+void ShutdownGL(GLDisplay* display, bool due_to_fallback) {
+  ShutdownGLPlatform(display);
 
   UnloadGLNativeLibraries(due_to_fallback);
   SetGLImplementation(kGLImplementationNone);
diff --git a/ui/gl/init/gl_factory.h b/ui/gl/init/gl_factory.h
index bf6765f..218f4d9 100644
--- a/ui/gl/init/gl_factory.h
+++ b/ui/gl/init/gl_factory.h
@@ -12,6 +12,7 @@
 #include "base/memory/ref_counted.h"
 #include "ui/gfx/geometry/size.h"
 #include "ui/gfx/native_widget_types.h"
+#include "ui/gl/gl_display.h"
 #include "ui/gl/gl_implementation.h"
 #include "ui/gl/gl_surface_format.h"
 #include "ui/gl/gpu_preference.h"
@@ -35,13 +36,14 @@
 // Initializes GL bindings and extension settings.
 // |system_device_id| specifies which GPU to use on a multi-GPU system.
 // If its value is 0, use the default GPU of the system.
-GL_INIT_EXPORT bool InitializeGLOneOff(uint64_t system_device_id);
+GL_INIT_EXPORT GLDisplay* InitializeGLOneOff(uint64_t system_device_id);
 
 // Initializes GL bindings without initializing extension settings.
 // |system_device_id| specifies which GPU to use on a multi-GPU system.
 // If its value is 0, use the default GPU of the system.
-GL_INIT_EXPORT bool InitializeGLNoExtensionsOneOff(bool init_bindings,
-                                                   uint64_t system_device_id);
+GL_INIT_EXPORT GLDisplay* InitializeGLNoExtensionsOneOff(
+    bool init_bindings,
+    uint64_t system_device_id);
 
 // Initializes GL bindings - load dlls and get proc address according to gl
 // command line switch.
@@ -49,7 +51,8 @@
 
 // Initialize plaiform dependent extension settings, including bindings,
 // capabilities, etc.
-GL_INIT_EXPORT bool InitializeExtensionSettingsOneOffPlatform();
+GL_INIT_EXPORT bool InitializeExtensionSettingsOneOffPlatform(
+    GLDisplay* display);
 
 // Initializes GL bindings using the provided parameters. This might be required
 // for use in tests.
@@ -62,14 +65,15 @@
 // successfully.
 // |system_device_id| specifies which GPU to use on a multi-GPU system.
 // If its value is 0, use the default GPU of the system.
-GL_INIT_EXPORT bool InitializeGLOneOffPlatformImplementation(
+GL_INIT_EXPORT GLDisplay* InitializeGLOneOffPlatformImplementation(
     bool fallback_to_software_gl,
     bool disable_gl_drawing,
     bool init_extensions,
     uint64_t system_device_id);
 
 // Clears GL bindings and resets GL implementation.
-GL_INIT_EXPORT void ShutdownGL(bool due_to_fallback);
+// Calling this function a second time on the same |display| is a no-op.
+GL_INIT_EXPORT void ShutdownGL(GLDisplay* display, bool due_to_fallback);
 
 // Return information about the GL window system binding implementation (e.g.,
 // EGL, GLX, WGL). Returns true if the information was retrieved successfully.
diff --git a/ui/gl/init/gl_factory_android.cc b/ui/gl/init/gl_factory_android.cc
index 222e612..c508a04e 100644
--- a/ui/gl/init/gl_factory_android.cc
+++ b/ui/gl/init/gl_factory_android.cc
@@ -176,13 +176,14 @@
   }
 }
 
-bool InitializeExtensionSettingsOneOffPlatform() {
+bool InitializeExtensionSettingsOneOffPlatform(GLDisplay* display) {
   GLImplementation implementation = GetGLImplementation();
   DCHECK_NE(kGLImplementationNone, implementation);
   switch (implementation) {
     case kGLImplementationEGLGLES2:
     case kGLImplementationEGLANGLE:
-      return InitializeExtensionSettingsOneOffEGL();
+      return InitializeExtensionSettingsOneOffEGL(
+          static_cast<GLDisplayEGL*>(display));
     case kGLImplementationMockGL:
     case kGLImplementationStubGL:
       return true;
diff --git a/ui/gl/init/gl_factory_mac.cc b/ui/gl/init/gl_factory_mac.cc
index a70f8631..4de2742 100644
--- a/ui/gl/init/gl_factory_mac.cc
+++ b/ui/gl/init/gl_factory_mac.cc
@@ -166,7 +166,7 @@
   // TODO(zmo): Implement this if needs arise.
 }
 
-bool InitializeExtensionSettingsOneOffPlatform() {
+bool InitializeExtensionSettingsOneOffPlatform(GLDisplay* display) {
   GLImplementation implementation = GetGLImplementation();
   DCHECK_NE(kGLImplementationNone, implementation);
   // TODO(zmo): Implement this if needs arise.
diff --git a/ui/gl/init/gl_factory_ozone.cc b/ui/gl/init/gl_factory_ozone.cc
index 7259e7b..57b4c97 100644
--- a/ui/gl/init/gl_factory_ozone.cc
+++ b/ui/gl/init/gl_factory_ozone.cc
@@ -120,9 +120,9 @@
   }
 }
 
-bool InitializeExtensionSettingsOneOffPlatform() {
+bool InitializeExtensionSettingsOneOffPlatform(GLDisplay* display) {
   if (HasGLOzone())
-    return GetGLOzone()->InitializeExtensionSettingsOneOffPlatform();
+    return GetGLOzone()->InitializeExtensionSettingsOneOffPlatform(display);
 
   switch (GetGLImplementation()) {
     case kGLImplementationMockGL:
diff --git a/ui/gl/init/gl_factory_win.cc b/ui/gl/init/gl_factory_win.cc
index 3aa35bb..f61749a 100644
--- a/ui/gl/init/gl_factory_win.cc
+++ b/ui/gl/init/gl_factory_win.cc
@@ -115,12 +115,13 @@
   }
 }
 
-bool InitializeExtensionSettingsOneOffPlatform() {
+bool InitializeExtensionSettingsOneOffPlatform(GLDisplay* display) {
   GLImplementation implementation = GetGLImplementation();
   DCHECK_NE(kGLImplementationNone, implementation);
   switch (implementation) {
     case kGLImplementationEGLANGLE:
-      return InitializeExtensionSettingsOneOffEGL();
+      return InitializeExtensionSettingsOneOffEGL(
+          static_cast<GLDisplayEGL*>(display));
     case kGLImplementationMockGL:
     case kGLImplementationStubGL:
       return true;
diff --git a/ui/gl/init/gl_initializer.h b/ui/gl/init/gl_initializer.h
index 06445b3..12c30d4 100644
--- a/ui/gl/init/gl_initializer.h
+++ b/ui/gl/init/gl_initializer.h
@@ -6,6 +6,7 @@
 #define UI_GL_INIT_GL_INITIALIZER_H_
 
 #include "ui/gl/buildflags.h"
+#include "ui/gl/gl_display.h"
 #include "ui/gl/gl_implementation.h"
 
 namespace gl {
@@ -20,7 +21,7 @@
 // InitializeGLOneOffPlatformImplementation() instead.
 // |system_device_id| specifies which GPU to use on a multi-GPU system.
 // If its value is 0, use the default GPU of the system.
-bool InitializeGLOneOffPlatform(uint64_t system_device_id);
+GLDisplay* InitializeGLOneOffPlatform(uint64_t system_device_id);
 
 // Initializes a particular GL implementation.
 bool InitializeStaticGLBindings(GLImplementationParts implementation);
@@ -30,7 +31,8 @@
 #endif  // BUILDFLAG(USE_STATIC_ANGLE)
 
 // Clears GL bindings for all implementations supported by platform.
-void ShutdownGLPlatform();
+// Calling this function a second time on the same |display| is a no-op.
+void ShutdownGLPlatform(GLDisplay* display);
 
 }  // namespace init
 }  // namespace gl
diff --git a/ui/gl/init/gl_initializer_android.cc b/ui/gl/init/gl_initializer_android.cc
index d26c601..5abd2d9 100644
--- a/ui/gl/init/gl_initializer_android.cc
+++ b/ui/gl/init/gl_initializer_android.cc
@@ -10,6 +10,7 @@
 #include "base/logging.h"
 #include "base/native_library.h"
 #include "ui/gl/gl_bindings.h"
+#include "ui/gl/gl_display_manager.h"
 #include "ui/gl/gl_egl_api_implementation.h"
 #include "ui/gl/gl_gl_api_implementation.h"
 #include "ui/gl/gl_surface_egl.h"
@@ -76,18 +77,20 @@
 
 }  // namespace
 
-bool InitializeGLOneOffPlatform(uint64_t system_device_id) {
+GLDisplay* InitializeGLOneOffPlatform(uint64_t system_device_id) {
   switch (GetGLImplementation()) {
     case kGLImplementationEGLGLES2:
-    case kGLImplementationEGLANGLE:
-      if (!GLSurfaceEGL::InitializeOneOff(
-              EGLDisplayPlatform(EGL_DEFAULT_DISPLAY), system_device_id)) {
+    case kGLImplementationEGLANGLE: {
+      GLDisplay* display = GLSurfaceEGL::InitializeOneOff(
+          EGLDisplayPlatform(EGL_DEFAULT_DISPLAY), system_device_id);
+      if (!display) {
         LOG(ERROR) << "GLSurfaceEGL::InitializeOneOff failed.";
-        return false;
+        return nullptr;
       }
-      return true;
+      return display;
+    }
     default:
-      return true;
+      return GLDisplayManagerEGL::GetInstance()->GetDisplay(system_device_id);
   }
 }
 
@@ -113,8 +116,8 @@
   return false;
 }
 
-void ShutdownGLPlatform() {
-  GLSurfaceEGL::ShutdownOneOff();
+void ShutdownGLPlatform(GLDisplay* display) {
+  GLSurfaceEGL::ShutdownOneOff(static_cast<GLDisplayEGL*>(display));
   ClearBindingsEGL();
   ClearBindingsGL();
 }
diff --git a/ui/gl/init/gl_initializer_mac.cc b/ui/gl/init/gl_initializer_mac.cc
index b1fbd1f..fa9771391 100644
--- a/ui/gl/init/gl_initializer_mac.cc
+++ b/ui/gl/init/gl_initializer_mac.cc
@@ -18,6 +18,7 @@
 #include "base/threading/thread_restrictions.h"
 #include "ui/gl/gl_bindings.h"
 #include "ui/gl/gl_context.h"
+#include "ui/gl/gl_display_manager.h"
 #include "ui/gl/gl_gl_api_implementation.h"
 #include "ui/gl/gl_implementation.h"
 #include "ui/gl/gl_surface.h"
@@ -170,28 +171,29 @@
 
 }  // namespace
 
-bool InitializeGLOneOffPlatform(uint64_t system_device_id) {
+GLDisplay* InitializeGLOneOffPlatform(uint64_t system_device_id) {
   switch (GetGLImplementation()) {
     case kGLImplementationDesktopGL:
     case kGLImplementationDesktopGLCoreProfile:
     case kGLImplementationAppleGL:
       if (!InitializeOneOffForSandbox()) {
         LOG(ERROR) << "GLSurfaceCGL::InitializeOneOff failed.";
-        return false;
       }
-      return true;
+      return GLDisplayManagerEGL::GetInstance()->GetDisplay(system_device_id);
 #if defined(USE_EGL)
     case kGLImplementationEGLGLES2:
-    case kGLImplementationEGLANGLE:
-      if (!GLSurfaceEGL::InitializeOneOff(EGLDisplayPlatform(0),
-                                          system_device_id)) {
+    case kGLImplementationEGLANGLE: {
+      GLDisplay* display = GLSurfaceEGL::InitializeOneOff(EGLDisplayPlatform(0),
+                                                          system_device_id);
+      if (!display) {
         LOG(ERROR) << "GLSurfaceEGL::InitializeOneOff failed.";
-        return false;
+        return nullptr;
       }
-      return true;
+      return display;
+    }
 #endif  // defined(USE_EGL)
     default:
-      return true;
+      return GLDisplayManagerEGL::GetInstance()->GetDisplay(system_device_id);
   }
 }
 
@@ -229,10 +231,10 @@
   return false;
 }
 
-void ShutdownGLPlatform() {
+void ShutdownGLPlatform(GLDisplay* display) {
   ClearBindingsGL();
 #if defined(USE_EGL)
-  GLSurfaceEGL::ShutdownOneOff();
+  GLSurfaceEGL::ShutdownOneOff(static_cast<GLDisplayEGL*>(display));
   ClearBindingsEGL();
 #endif  // defined(USE_EGL)
 }
diff --git a/ui/gl/init/gl_initializer_ozone.cc b/ui/gl/init/gl_initializer_ozone.cc
index 36aef21..36c73023 100644
--- a/ui/gl/init/gl_initializer_ozone.cc
+++ b/ui/gl/init/gl_initializer_ozone.cc
@@ -7,6 +7,7 @@
 #include "base/check_op.h"
 #include "base/notreached.h"
 #include "ui/gl/gl_bindings.h"
+#include "ui/gl/gl_display_manager.h"
 #include "ui/gl/gl_gl_api_implementation.h"
 #include "ui/gl/gl_surface.h"
 
@@ -19,20 +20,20 @@
 namespace gl {
 namespace init {
 
-bool InitializeGLOneOffPlatform(uint64_t) {
+GLDisplay* InitializeGLOneOffPlatform(uint64_t system_device_id) {
   if (HasGLOzone()) {
     gl::GLDisplayEglUtil::SetInstance(gl::GLDisplayEglUtilOzone::GetInstance());
-    return GetGLOzone()->InitializeGLOneOffPlatform();
+    return GetGLOzone()->InitializeGLOneOffPlatform(system_device_id);
   }
 
   switch (GetGLImplementation()) {
     case kGLImplementationMockGL:
     case kGLImplementationStubGL:
-      return true;
+      return GLDisplayManagerEGL::GetInstance()->GetDisplay(system_device_id);
     default:
       NOTREACHED();
   }
-  return false;
+  return nullptr;
 }
 
 bool InitializeStaticGLBindings(GLImplementationParts implementation) {
@@ -58,9 +59,9 @@
   return false;
 }
 
-void ShutdownGLPlatform() {
+void ShutdownGLPlatform(GLDisplay* display) {
   if (HasGLOzone()) {
-    GetGLOzone()->ShutdownGL();
+    GetGLOzone()->ShutdownGL(display);
     return;
   }
 
diff --git a/ui/gl/init/gl_initializer_win.cc b/ui/gl/init/gl_initializer_win.cc
index 06798da..960065c 100644
--- a/ui/gl/init/gl_initializer_win.cc
+++ b/ui/gl/init/gl_initializer_win.cc
@@ -18,6 +18,7 @@
 #include "base/trace_event/trace_event.h"
 #include "base/win/windows_version.h"
 #include "ui/gl/gl_bindings.h"
+#include "ui/gl/gl_display_manager.h"
 #include "ui/gl/gl_egl_api_implementation.h"
 #include "ui/gl/gl_gl_api_implementation.h"
 #include "ui/gl/gl_surface_egl.h"
@@ -122,25 +123,27 @@
 
 }  // namespace
 
-bool InitializeGLOneOffPlatform(uint64_t system_device_id) {
+GLDisplay* InitializeGLOneOffPlatform(uint64_t system_device_id) {
   VSyncProviderWin::InitializeOneOff();
 
   switch (GetGLImplementation()) {
-    case kGLImplementationEGLANGLE:
-      if (!GLSurfaceEGL::InitializeOneOff(EGLDisplayPlatform(GetDC(nullptr)),
-                                          system_device_id)) {
+    case kGLImplementationEGLANGLE: {
+      GLDisplayEGL* display = GLSurfaceEGL::InitializeOneOff(
+          EGLDisplayPlatform(GetDC(nullptr)), system_device_id);
+      if (!display) {
         LOG(ERROR) << "GLSurfaceEGL::InitializeOneOff failed.";
-        return false;
+        return nullptr;
       }
-      DirectCompositionSurfaceWin::InitializeOneOff();
-      break;
+      DirectCompositionSurfaceWin::InitializeOneOff(display);
+      return display;
+    }
     case kGLImplementationMockGL:
     case kGLImplementationStubGL:
       break;
     default:
       NOTREACHED();
   }
-  return true;
+  return GLDisplayManagerEGL::GetInstance()->GetDisplay(system_device_id);
 }
 
 bool InitializeStaticGLBindings(GLImplementationParts implementation) {
@@ -170,9 +173,9 @@
   return false;
 }
 
-void ShutdownGLPlatform() {
+void ShutdownGLPlatform(GLDisplay* display) {
   DirectCompositionSurfaceWin::ShutdownOneOff();
-  GLSurfaceEGL::ShutdownOneOff();
+  GLSurfaceEGL::ShutdownOneOff(static_cast<GLDisplayEGL*>(display));
   ClearBindingsEGL();
   ClearBindingsGL();
 }
diff --git a/ui/gl/test/gl_image_test_support.cc b/ui/gl/test/gl_image_test_support.cc
index c3d5bb4..81b85cf4 100644
--- a/ui/gl/test/gl_image_test_support.cc
+++ b/ui/gl/test/gl_image_test_support.cc
@@ -33,6 +33,8 @@
 }
 }  // namespace
 
+GLDisplay* GLImageTestSupport::display_ = nullptr;
+
 // static
 void GLImageTestSupport::InitializeGL(
     absl::optional<GLImplementationParts> prefered_impl) {
@@ -50,7 +52,7 @@
       prefered_impl ? *prefered_impl : allowed_impls[0];
   DCHECK(impl.IsAllowed(allowed_impls));
 
-  GLSurfaceTestSupport::InitializeOneOffImplementation(impl, true);
+  display_ = GLSurfaceTestSupport::InitializeOneOffImplementation(impl, true);
 #if defined(USE_OZONE)
   // Make sure all the tasks posted to the current task runner by the
   // initialization functions are run before running the tests.
@@ -60,7 +62,7 @@
 
 // static
 void GLImageTestSupport::CleanupGL() {
-  init::ShutdownGL(false);
+  GLSurfaceTestSupport::ShutdownGL(display_);
 }
 
 // static
diff --git a/ui/gl/test/gl_image_test_support.h b/ui/gl/test/gl_image_test_support.h
index e427a6b..393872f 100644
--- a/ui/gl/test/gl_image_test_support.h
+++ b/ui/gl/test/gl_image_test_support.h
@@ -9,6 +9,7 @@
 
 #include "third_party/abseil-cpp/absl/types/optional.h"
 #include "ui/gfx/buffer_types.h"
+#include "ui/gl/gl_display.h"
 #include "ui/gl/gl_implementation.h"
 
 namespace gl {
@@ -31,6 +32,9 @@
                                    gfx::BufferFormat format,
                                    const uint8_t color[4],
                                    uint8_t* data);
+
+ private:
+  static GLDisplay* display_;
 };
 
 }  // namespace gl
diff --git a/ui/gl/test/gl_surface_test_support.cc b/ui/gl/test/gl_surface_test_support.cc
index 22ee46e..1afadab 100644
--- a/ui/gl/test/gl_surface_test_support.cc
+++ b/ui/gl/test/gl_surface_test_support.cc
@@ -25,7 +25,8 @@
 namespace gl {
 
 namespace {
-void InitializeOneOffHelper(bool init_extensions) {
+
+GLDisplay* InitializeOneOffHelper(bool init_extensions) {
   DCHECK_EQ(kGLImplementationNone, GetGLImplementation());
 
 #if defined(USE_OZONE)
@@ -69,70 +70,70 @@
 
   CHECK(gl::init::InitializeStaticGLBindingsImplementation(
       impl, fallback_to_software_gl));
-  CHECK(gl::init::InitializeGLOneOffPlatformImplementation(
+  GLDisplay* display = gl::init::InitializeGLOneOffPlatformImplementation(
       fallback_to_software_gl, disable_gl_drawing, init_extensions,
-      /*system_device_id=*/0));
+      /*system_device_id=*/0);
+  CHECK(display);
+  return display;
 }
 }  // namespace
 
 // static
-void GLSurfaceTestSupport::InitializeOneOff() {
-  InitializeOneOffHelper(true);
+GLDisplay* GLSurfaceTestSupport::InitializeOneOff() {
+  return InitializeOneOffHelper(true);
 }
 
 // static
-void GLSurfaceTestSupport::InitializeNoExtensionsOneOff() {
-  InitializeOneOffHelper(false);
+GLDisplay* GLSurfaceTestSupport::InitializeNoExtensionsOneOff() {
+  return InitializeOneOffHelper(false);
 }
 
 // static
-void GLSurfaceTestSupport::InitializeOneOffImplementation(
+GLDisplay* GLSurfaceTestSupport::InitializeOneOffImplementation(
     GLImplementationParts impl,
     bool fallback_to_software_gl) {
   DCHECK(!base::CommandLine::ForCurrentProcess()->HasSwitch(switches::kUseGL))
       << "kUseGL has not effect in tests";
 
-  // This method may be called multiple times in the same process to set up
-  // bindings in different ways.
-  init::ShutdownGL(false);
-
   bool disable_gl_drawing = false;
   bool init_extensions = true;
 
   CHECK(gl::init::InitializeStaticGLBindingsImplementation(
       impl, fallback_to_software_gl));
-  CHECK(gl::init::InitializeGLOneOffPlatformImplementation(
+  GLDisplay* display = gl::init::InitializeGLOneOffPlatformImplementation(
       fallback_to_software_gl, disable_gl_drawing, init_extensions,
-      /*system_device_id=*/0));
+      /*system_device_id=*/0);
+  CHECK(display);
+  return display;
 }
 
 // static
-void GLSurfaceTestSupport::InitializeOneOffWithMockBindings() {
+GLDisplay* GLSurfaceTestSupport::InitializeOneOffWithMockBindings() {
 #if defined(USE_OZONE)
   ui::OzonePlatform::InitParams params;
   params.single_process = true;
   ui::OzonePlatform::InitializeForGPU(params);
 #endif
 
-  InitializeOneOffImplementation(GLImplementationParts(kGLImplementationMockGL),
-                                 false);
+  return InitializeOneOffImplementation(
+      GLImplementationParts(kGLImplementationMockGL), false);
 }
 
 // static
-void GLSurfaceTestSupport::InitializeOneOffWithStubBindings() {
+GLDisplay* GLSurfaceTestSupport::InitializeOneOffWithStubBindings() {
 #if defined(USE_OZONE)
   ui::OzonePlatform::InitParams params;
   params.single_process = true;
   ui::OzonePlatform::InitializeForGPU(params);
 #endif
 
-  InitializeOneOffImplementation(GLImplementationParts(kGLImplementationStubGL),
-                                 false);
+  return InitializeOneOffImplementation(
+      GLImplementationParts(kGLImplementationStubGL), false);
 }
 
 // static
-void GLSurfaceTestSupport::ShutdownGL() {
-  init::ShutdownGL(false);
+void GLSurfaceTestSupport::ShutdownGL(GLDisplay* display) {
+  init::ShutdownGL(display, false);
 }
 
 }  // namespace gl
diff --git a/ui/gl/test/gl_surface_test_support.h b/ui/gl/test/gl_surface_test_support.h
index 89c662cf..ac0db72f 100644
--- a/ui/gl/test/gl_surface_test_support.h
+++ b/ui/gl/test/gl_surface_test_support.h
@@ -5,19 +5,21 @@
 #ifndef UI_GL_TEST_GL_SURFACE_TEST_SUPPORT_H_
 #define UI_GL_TEST_GL_SURFACE_TEST_SUPPORT_H_
 
+#include "ui/gl/gl_display.h"
 #include "ui/gl/gl_implementation.h"
 
 namespace gl {
 
 class GLSurfaceTestSupport {
  public:
-  static void InitializeOneOff();
-  static void InitializeNoExtensionsOneOff();
-  static void InitializeOneOffImplementation(GLImplementationParts impl,
-                                             bool fallback_to_swiftshader);
-  static void InitializeOneOffWithMockBindings();
-  static void InitializeOneOffWithStubBindings();
-  static void ShutdownGL();
+  static GLDisplay* InitializeOneOff();
+  static GLDisplay* InitializeNoExtensionsOneOff();
+  static GLDisplay* InitializeOneOffImplementation(
+      GLImplementationParts impl,
+      bool fallback_to_swiftshader);
+  static GLDisplay* InitializeOneOffWithMockBindings();
+  static GLDisplay* InitializeOneOffWithStubBindings();
+  static void ShutdownGL(GLDisplay* display);
 };
 
 }  // namespace gl
diff --git a/ui/ozone/common/gl_ozone_egl.cc b/ui/ozone/common/gl_ozone_egl.cc
index 2034d0a..c7e3707 100644
--- a/ui/ozone/common/gl_ozone_egl.cc
+++ b/ui/ozone/common/gl_ozone_egl.cc
@@ -15,13 +15,15 @@
 
 namespace ui {
 
-bool GLOzoneEGL::InitializeGLOneOffPlatform() {
-  if (!gl::GLSurfaceEGL::InitializeOneOff(GetNativeDisplay(),
-                                          /*system_device_id=*/0)) {
+gl::GLDisplay* GLOzoneEGL::InitializeGLOneOffPlatform(
+    uint64_t system_device_id) {
+  gl::GLDisplay* display =
+      gl::GLSurfaceEGL::InitializeOneOff(GetNativeDisplay(), system_device_id);
+  if (!display) {
     LOG(ERROR) << "GLSurfaceEGL::InitializeOneOff failed.";
-    return false;
+    return nullptr;
   }
-  return true;
+  return display;
 }
 
 bool GLOzoneEGL::InitializeStaticGLBindings(
@@ -41,12 +43,14 @@
   gl::SetDisabledExtensionsEGL(disabled_extensions);
 }
 
-bool GLOzoneEGL::InitializeExtensionSettingsOneOffPlatform() {
-  return gl::InitializeExtensionSettingsOneOffEGL();
+bool GLOzoneEGL::InitializeExtensionSettingsOneOffPlatform(
+    gl::GLDisplay* display) {
+  return gl::InitializeExtensionSettingsOneOffEGL(
+      static_cast<gl::GLDisplayEGL*>(display));
 }
 
-void GLOzoneEGL::ShutdownGL() {
-  gl::GLSurfaceEGL::ShutdownOneOff();
+void GLOzoneEGL::ShutdownGL(gl::GLDisplay* display) {
+  gl::GLSurfaceEGL::ShutdownOneOff(static_cast<gl::GLDisplayEGL*>(display));
   gl::ClearBindingsGL();
   gl::ClearBindingsEGL();
 }
diff --git a/ui/ozone/common/gl_ozone_egl.h b/ui/ozone/common/gl_ozone_egl.h
index 11b44340..29d0aa5 100644
--- a/ui/ozone/common/gl_ozone_egl.h
+++ b/ui/ozone/common/gl_ozone_egl.h
@@ -24,13 +24,14 @@
   ~GLOzoneEGL() override {}
 
   // GLOzone:
-  bool InitializeGLOneOffPlatform() override;
+  gl::GLDisplay* InitializeGLOneOffPlatform(uint64_t system_device_id) override;
   bool InitializeStaticGLBindings(
       const gl::GLImplementationParts& implementation) override;
   void SetDisabledExtensionsPlatform(
       const std::string& disabled_extensions) override;
-  bool InitializeExtensionSettingsOneOffPlatform() override;
-  void ShutdownGL() override;
+  bool InitializeExtensionSettingsOneOffPlatform(
+      gl::GLDisplay* display) override;
+  void ShutdownGL(gl::GLDisplay* display) override;
   bool GetGLWindowSystemBindingInfo(
       const gl::GLVersionInfo& gl_info,
       gl::GLWindowSystemBindingInfo* info) override;
diff --git a/ui/ozone/platform/x11/gl_ozone_glx.cc b/ui/ozone/platform/x11/gl_ozone_glx.cc
index b40b3c5..6f3bd25 100644
--- a/ui/ozone/platform/x11/gl_ozone_glx.cc
+++ b/ui/ozone/platform/x11/gl_ozone_glx.cc
@@ -11,6 +11,7 @@
 #include "ui/gl/gl_gl_api_implementation.h"
 #include "ui/gl/gl_glx_api_implementation.h"
 #include "ui/gl/gl_surface_glx_x11.h"
+#include "ui/gl/gl_utils.h"
 
 namespace ui {
 
@@ -24,12 +25,15 @@
 
 }  // namespace
 
-bool GLOzoneGLX::InitializeGLOneOffPlatform() {
+gl::GLDisplay* GLOzoneGLX::InitializeGLOneOffPlatform(
+    uint64_t system_device_id) {
+  // TODO(https://crbug.com/1251724): GLSurfaceGLX::InitializeOneOff()
+  // should take |system_device_id| and return a GLDisplayX11.
   if (!gl::GLSurfaceGLX::InitializeOneOff()) {
     LOG(ERROR) << "GLSurfaceGLX::InitializeOneOff failed.";
-    return false;
+    return nullptr;
   }
-  return true;
+  return gl::GetDisplayX11(system_device_id);
 }
 
 bool GLOzoneGLX::InitializeStaticGLBindings(
@@ -73,11 +77,12 @@
   gl::SetDisabledExtensionsGLX(disabled_extensions);
 }
 
-bool GLOzoneGLX::InitializeExtensionSettingsOneOffPlatform() {
+bool GLOzoneGLX::InitializeExtensionSettingsOneOffPlatform(
+    gl::GLDisplay* display) {
   return gl::InitializeExtensionSettingsOneOffGLX();
 }
 
-void GLOzoneGLX::ShutdownGL() {
+void GLOzoneGLX::ShutdownGL(gl::GLDisplay* display) {
   gl::ClearBindingsGL();
   gl::ClearBindingsGLX();
 }
diff --git a/ui/ozone/platform/x11/gl_ozone_glx.h b/ui/ozone/platform/x11/gl_ozone_glx.h
index 61d51d5e..1fb05ff 100644
--- a/ui/ozone/platform/x11/gl_ozone_glx.h
+++ b/ui/ozone/platform/x11/gl_ozone_glx.h
@@ -19,13 +19,14 @@
 
   ~GLOzoneGLX() override {}
 
-  bool InitializeGLOneOffPlatform() override;
+  gl::GLDisplay* InitializeGLOneOffPlatform(uint64_t system_device_id) override;
   bool InitializeStaticGLBindings(
       const gl::GLImplementationParts& implementation) override;
   void SetDisabledExtensionsPlatform(
       const std::string& disabled_extensions) override;
-  bool InitializeExtensionSettingsOneOffPlatform() override;
-  void ShutdownGL() override;
+  bool InitializeExtensionSettingsOneOffPlatform(
+      gl::GLDisplay* display) override;
+  void ShutdownGL(gl::GLDisplay* display) override;
   bool GetGLWindowSystemBindingInfo(
       const gl::GLVersionInfo& gl_info,
       gl::GLWindowSystemBindingInfo* info) override;
diff --git a/ui/ozone/public/gl_ozone.h b/ui/ozone/public/gl_ozone.h
index ccfa309b..ef4ec09e 100644
--- a/ui/ozone/public/gl_ozone.h
+++ b/ui/ozone/public/gl_ozone.h
@@ -11,6 +11,7 @@
 #include "base/memory/ref_counted.h"
 #include "ui/gfx/geometry/size.h"
 #include "ui/gfx/native_widget_types.h"
+#include "ui/gl/gl_display.h"
 #include "ui/gl/gl_implementation.h"
 #include "ui/gl/gpu_preference.h"
 
@@ -37,7 +38,8 @@
       const gl::GLImplementationParts& implementation) = 0;
 
   // Performs any one off initialization for GL implementation.
-  virtual bool InitializeGLOneOffPlatform() = 0;
+  virtual gl::GLDisplay* InitializeGLOneOffPlatform(
+      uint64_t system_device_id) = 0;
 
   // Disables the specified extensions in the window system bindings,
   // e.g., GLX, EGL, etc. This is part of the GPU driver bug workarounds
@@ -48,10 +50,11 @@
   // Initializes extension related settings for window system bindings that
   // will be affected by SetDisabledExtensionsPlatform(). This function is
   // called after SetDisabledExtensionsPlatform() to finalize the bindings.
-  virtual bool InitializeExtensionSettingsOneOffPlatform() = 0;
+  virtual bool InitializeExtensionSettingsOneOffPlatform(
+      gl::GLDisplay* display) = 0;
 
   // Clears static GL bindings.
-  virtual void ShutdownGL() = 0;
+  virtual void ShutdownGL(gl::GLDisplay* display) = 0;
 
   // Returns information about the GL window system binding implementation (eg.
   // EGL, GLX, WGL). Returns true if the information was retrieved successfully.