diff --git a/DEPS b/DEPS
index c20ef23..12a141f0 100644
--- a/DEPS
+++ b/DEPS
@@ -209,11 +209,11 @@
   # 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': '00a199282e4b184fa4fde2edee073ae989b5db67',
+  'skia_revision': '5276ba274b38826e3415326f08607bf6cdc08238',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling V8
   # and whatever else without interference from each other.
-  'v8_revision': '6e085af6bbdd3d117fefc046c5a5b70858a2d6a4',
+  'v8_revision': 'e278eae60f9a9136604506876c74bd273f374d92',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling swarming_client
   # and whatever else without interference from each other.
@@ -221,7 +221,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling ANGLE
   # and whatever else without interference from each other.
-  'angle_revision': '0656787010a6ce708704916481adab2cd7b08688',
+  'angle_revision': '19b7815d8dffd2e1b7dd7f40511c999cacb42edc',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling SwiftShader
   # and whatever else without interference from each other.
@@ -260,7 +260,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling freetype
   # and whatever else without interference from each other.
-  'freetype_revision': 'f631542dae1aaf9101135ed660cd3ff1d08ed93c',
+  'freetype_revision': '4e1c6a12e5f285d57e66818177013cce7efbd7b0',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling freetype
   # and whatever else without interference from each other.
@@ -280,7 +280,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': 'ed0183e21a8642343f61c8302ee21550246e5b65',
+  'catapult_revision': '00b6ebab8b4ec3aa2376e3f2dd51a4547d31c492',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling libFuzzer
   # and whatever else without interference from each other.
@@ -288,7 +288,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling devtools-frontend
   # and whatever else without interference from each other.
-  'devtools_frontend_revision': '69a53e842fef2729160345def9047cb7bf3ccbcd',
+  'devtools_frontend_revision': 'b2687c0b37e97ede7106473fcffff495591e7d5b',
   # 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.
@@ -332,7 +332,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.
-  'quiche_revision': '9f23586cf08918d9b56b1c2f7468fbabb7354e55',
+  'quiche_revision': 'ffae80c46d83ad299b72dc9badc95bff5d777b3a',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling ios_webkit
   # and whatever else without interference from each other.
@@ -356,7 +356,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling nearby
   # and whatever else without interference from each other.
-  'nearby_revision': '857958c87fa5994490948608c625c9407f118fe3',
+  'nearby_revision': 'e1b1c671416858c85bbac732e1ae2482c659f834',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling securemessage
   # and whatever else without interference from each other.
@@ -384,7 +384,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.
-  'libjxl_revision': 'a124844519310785445d0b6efcd536c5398e6a20',
+  'libjxl_revision': 'e5ce94456581d43f8a52c8100c726a0d079f65e7',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
@@ -730,7 +730,7 @@
     'packages': [
       {
           'package': 'chromium/third_party/androidx',
-          'version': 'OlL9OGf4wTT0pjm2vwmttRgPxRRGKMLtgw5ITuLShmIC',
+          'version': '2mGrDboR-mGbHGKWk3iBfNYHRVx3OPQ6WxiKxMeYiFkC',
       },
     ],
     'condition': 'checkout_android',
@@ -946,7 +946,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' + '@' + '8bfbc1211c43853af17e3f301e1c1c858540909a',
+      'url': Var('chromium_git') + '/chromiumos/chromite.git' + '@' + '0828e9e93022c7a57616fc42e546a077a241806d',
       'condition': 'checkout_chromeos',
   },
 
@@ -966,7 +966,7 @@
   },
 
   'src/third_party/depot_tools':
-    Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + '0d1afc97295c538cf179214fd98ec3c2cca85c8f',
+    Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + '06450df7e61d5d698eafda002e5e48ac46a847c2',
 
   'src/third_party/devtools-frontend/src':
     Var('chromium_git') + '/devtools/devtools-frontend' + '@' + Var('devtools_frontend_revision'),
@@ -1338,7 +1338,7 @@
   },
 
   'src/third_party/perfetto':
-    Var('android_git') + '/platform/external/perfetto.git' + '@' + '21d255cb2580c7d9b2a9a554b35a7faed6b2605a',
+    Var('android_git') + '/platform/external/perfetto.git' + '@' + '9df4ba9884c2ee6a4645c11de99bf18b5198fae2',
 
   'src/third_party/perl': {
       'url': Var('chromium_git') + '/chromium/deps/perl.git' + '@' + '6f3e5028eb65d0b4c5fdd792106ac4c84eee1eb3',
@@ -1552,7 +1552,7 @@
     Var('chromium_git') + '/external/github.com/gpuweb/cts.git' + '@' + '3692cbbd3224f9d7e2ae138ef26bd584855249a1',
 
   'src/third_party/webrtc':
-    Var('webrtc_git') + '/src.git' + '@' + 'da83f65db342df78e55ee98a3532883a32ae9385',
+    Var('webrtc_git') + '/src.git' + '@' + '924a2751e019b15e22d53acace10d57fffea4319',
 
   'src/third_party/libgifcodec':
      Var('skia_git') + '/libgifcodec' + '@'+  Var('libgifcodec_revision'),
diff --git a/ash/accelerators/accelerator_controller_unittest.cc b/ash/accelerators/accelerator_controller_unittest.cc
index 7bb82f1..cf4fbfb3 100644
--- a/ash/accelerators/accelerator_controller_unittest.cc
+++ b/ash/accelerators/accelerator_controller_unittest.cc
@@ -1281,9 +1281,9 @@
   base::RunLoop().RunUntilIdle();
   GetAppListTestHelper()->CheckVisibility(true);
   GetAppListTestHelper()->CheckState(AppListViewState::kFullscreenAllApps);
-  histogram_tester.ExpectTotalCount(kAppListToggleMethodHistogram,
+  histogram_tester.ExpectTotalCount("Apps.AppListShowSource",
                                     ++toggle_count_total);
-  histogram_tester.ExpectBucketCount(kAppListToggleMethodHistogram,
+  histogram_tester.ExpectBucketCount("Apps.AppListShowSource",
                                      kSearchKeyFullscreen,
                                      ++toggle_count_fullscreen);
 
@@ -1299,9 +1299,9 @@
   base::RunLoop().RunUntilIdle();
   GetAppListTestHelper()->CheckVisibility(true);
   GetAppListTestHelper()->CheckState(AppListViewState::kPeeking);
-  histogram_tester.ExpectTotalCount(kAppListToggleMethodHistogram,
+  histogram_tester.ExpectTotalCount("Apps.AppListShowSource",
                                     ++toggle_count_total);
-  histogram_tester.ExpectBucketCount(kAppListToggleMethodHistogram, kSearchKey,
+  histogram_tester.ExpectBucketCount("Apps.AppListShowSource", kSearchKey,
                                      ++toggle_count_regular);
 
   EXPECT_TRUE(ProcessInController(
@@ -1309,9 +1309,9 @@
   base::RunLoop().RunUntilIdle();
   GetAppListTestHelper()->CheckVisibility(true);
   GetAppListTestHelper()->CheckState(AppListViewState::kFullscreenAllApps);
-  histogram_tester.ExpectTotalCount(kAppListToggleMethodHistogram,
+  histogram_tester.ExpectTotalCount("Apps.AppListShowSource",
                                     ++toggle_count_total);
-  histogram_tester.ExpectBucketCount(kAppListToggleMethodHistogram,
+  histogram_tester.ExpectBucketCount("Apps.AppListShowSource",
                                      kSearchKeyFullscreen,
                                      ++toggle_count_fullscreen);
   // VKEY_BROWSER_SEARCH (no shift) should not return to peeking, but close the
@@ -1327,9 +1327,9 @@
   base::RunLoop().RunUntilIdle();
   GetAppListTestHelper()->CheckVisibility(true);
   GetAppListTestHelper()->CheckState(AppListViewState::kPeeking);
-  histogram_tester.ExpectTotalCount(kAppListToggleMethodHistogram,
+  histogram_tester.ExpectTotalCount("Apps.AppListShowSource",
                                     ++toggle_count_total);
-  histogram_tester.ExpectBucketCount(kAppListToggleMethodHistogram, kSearchKey,
+  histogram_tester.ExpectBucketCount("Apps.AppListShowSource", kSearchKey,
                                      ++toggle_count_regular);
   ui::test::EventGenerator* generator = GetEventGenerator();
   generator->PressKey(ui::VKEY_0, ui::EF_NONE);
@@ -1343,9 +1343,9 @@
   base::RunLoop().RunUntilIdle();
   GetAppListTestHelper()->CheckVisibility(true);
   GetAppListTestHelper()->CheckState(AppListViewState::kFullscreenSearch);
-  histogram_tester.ExpectTotalCount(kAppListToggleMethodHistogram,
+  histogram_tester.ExpectTotalCount("Apps.AppListShowSource",
                                     ++toggle_count_total);
-  histogram_tester.ExpectBucketCount(kAppListToggleMethodHistogram,
+  histogram_tester.ExpectBucketCount("Apps.AppListShowSource",
                                      kSearchKeyFullscreen,
                                      ++toggle_count_fullscreen);
 
diff --git a/ash/app_list/app_list_controller_impl.cc b/ash/app_list/app_list_controller_impl.cc
index 953f5da..2b94580 100644
--- a/ash/app_list/app_list_controller_impl.cc
+++ b/ash/app_list/app_list_controller_impl.cc
@@ -239,7 +239,7 @@
 }
 
 void LogAppListShowSource(AppListShowSource show_source) {
-  UMA_HISTOGRAM_ENUMERATION(kAppListToggleMethodHistogram, show_source);
+  UMA_HISTOGRAM_ENUMERATION("Apps.AppListShowSource", show_source);
 }
 
 base::Optional<TabletModeAnimationTransition>
@@ -1318,7 +1318,7 @@
     }
   }
 
-  UMA_HISTOGRAM_ENUMERATION(kSearchResultOpenDisplayTypeHistogram,
+  UMA_HISTOGRAM_ENUMERATION("Apps.AppListSearchResultOpenDisplayType",
                             result->display_type(),
                             SearchResultDisplayType::kLast);
 
@@ -1327,12 +1327,13 @@
   if (launched_from != AppListLaunchedFrom::kLaunchedFromSuggestionChip) {
     base::RecordAction(base::UserMetricsAction("AppList_OpenSearchResult"));
 
-    UMA_HISTOGRAM_COUNTS_100(kSearchQueryLength, GetLastQueryLength());
+    UMA_HISTOGRAM_COUNTS_100("Apps.AppListSearchQueryLength",
+                             GetLastQueryLength());
     if (IsTabletMode()) {
-      UMA_HISTOGRAM_COUNTS_100(kSearchQueryLengthInTablet,
+      UMA_HISTOGRAM_COUNTS_100("Apps.AppListSearchQueryLength.TabletMode",
                                GetLastQueryLength());
     } else {
-      UMA_HISTOGRAM_COUNTS_100(kSearchQueryLengthInClamshell,
+      UMA_HISTOGRAM_COUNTS_100("Apps.AppListSearchQueryLength.ClamshellMode",
                                GetLastQueryLength());
     }
   }
diff --git a/ash/app_list/app_list_metrics.cc b/ash/app_list/app_list_metrics.cc
index 574d220..9a29144 100644
--- a/ash/app_list/app_list_metrics.cc
+++ b/ash/app_list/app_list_metrics.cc
@@ -17,6 +17,9 @@
 
 namespace ash {
 
+const char kAppListPeekingToFullscreenHistogram[] =
+    "Apps.AppListPeekingToFullscreenSource";
+
 // The UMA histogram that logs smoothness of pagination animation.
 constexpr char kPaginationTransitionAnimationSmoothness[] =
     "Apps.PaginationTransition.AnimationSmoothness";
@@ -107,13 +110,13 @@
 
 void RecordPageSwitcherSource(AppListPageSwitcherSource source,
                               bool is_tablet_mode) {
-  UMA_HISTOGRAM_ENUMERATION(kAppListPageSwitcherSourceHistogram, source,
+  UMA_HISTOGRAM_ENUMERATION("Apps.AppListPageSwitcherSource", source,
                             kMaxAppListPageSwitcherSource);
   if (is_tablet_mode) {
-    UMA_HISTOGRAM_ENUMERATION(kAppListPageSwitcherSourceHistogramInTablet,
+    UMA_HISTOGRAM_ENUMERATION("Apps.AppListPageSwitcherSource.TabletMode",
                               source, kMaxAppListPageSwitcherSource);
   } else {
-    UMA_HISTOGRAM_ENUMERATION(kAppListPageSwitcherSourceHistogramInClamshell,
+    UMA_HISTOGRAM_ENUMERATION("Apps.AppListPageSwitcherSource.ClamshellMode",
                               source, kMaxAppListPageSwitcherSource);
   }
 }
diff --git a/ash/app_list/app_list_metrics.h b/ash/app_list/app_list_metrics.h
index c8b48fc..398f6d8d 100644
--- a/ash/app_list/app_list_metrics.h
+++ b/ash/app_list/app_list_metrics.h
@@ -16,98 +16,9 @@
 class SearchModel;
 class SearchResult;
 
-// The UMA histogram that logs the input latency from input event to the
-// representation time of the shown launcher UI.
-constexpr char kAppListShowInputLatencyHistogram[] =
-    "Apps.AppListShow.InputLatency";
-
-// The UMA histogram that logs the input latency from input event to the
-// representation time of the dismissed launcher UI.
-constexpr char kAppListHideInputLatencyHistogram[] =
-    "Apps.AppListHide.InputLatency";
-
-// The UMA histogram that logs different ways to move an app in app list's apps
-// grid.
-constexpr char kAppListAppMovingType[] = "Apps.AppListAppMovingType";
-
-// The UMA histogram that logs the creation time of the AppListView.
-constexpr char kAppListCreationTimeHistogram[] = "Apps.AppListCreationTime";
-
-// The UMA histogram that logs usage of state transitions in the new
-// app list UI.
-constexpr char kAppListStateTransitionSourceHistogram[] =
-    "Apps.AppListStateTransitionSource";
-
-// The UMA histogram that logs the source of root app grid page switcher usage
-// in the app list.
-constexpr char kAppListPageSwitcherSourceHistogram[] =
-    "Apps.AppListPageSwitcherSource";
-
-// The UMA histogram that logs the source of root app grid page switcher usage
-// in the app list in tablet mode.
-constexpr char kAppListPageSwitcherSourceHistogramInTablet[] =
-    "Apps.AppListPageSwitcherSource.TabletMode";
-
-// The UMA histogram that logs the source of root app grid page switcher usage
-// in the app list in clamshell mode.
-constexpr char kAppListPageSwitcherSourceHistogramInClamshell[] =
-    "Apps.AppListPageSwitcherSource.ClamshellMode";
-
-// The UMA histogram that logs usage of the original and redesigned folders.
-constexpr char kAppListFolderOpenedHistogram[] = "Apps.AppListFolderOpened";
-
 // The UMA histogram that logs how the app list transitions from peeking to
-// fullscreen.
-constexpr char kAppListPeekingToFullscreenHistogram[] =
-    "Apps.AppListPeekingToFullscreenSource";
-
-// The UMA histogram that logs how the app list is shown.
-constexpr char kAppListToggleMethodHistogram[] = "Apps.AppListShowSource";
-
-// The UMA histogram that logs the presence or absence of Drive QuickAccess
-// search results in the zero-state results list. Differentiates between results
-// existing in the model's results list, but not being displayed in the view.
-constexpr char kDriveQuickAccessResultPresence[] =
-    "Apps.AppListDriveQuickAccessProvider.ResultPresence";
-
-// The UMA histogram that logs smoothness of folder show/hide animation.
-constexpr char kFolderShowHideAnimationSmoothness[] =
-    "Apps.AppListFolder.ShowHide.AnimationSmoothness";
-
-// The UMA histogram that logs which page gets opened by the user.
-constexpr char kPageOpenedHistogram[] = "Apps.AppListPageOpened";
-
-// The UMA histogram that logs how many apps users have in folders.
-constexpr char kNumberOfAppsInFoldersHistogram[] =
-    "Apps.AppsInFolders.FullscreenAppListEnabled";
-
-// The UMA histogram that logs how many folders users have.
-constexpr char kNumberOfFoldersHistogram[] = "Apps.NumberOfFolders";
-
-// The UMA histogram that logs how many pages users have in top level apps grid.
-constexpr char kNumberOfPagesHistogram[] = "Apps.NumberOfPages";
-
-// The UMA histogram that logs how many pages with empty slots users have in top
-// level apps grid.
-constexpr char kNumberOfPagesNotFullHistogram[] = "Apps.NumberOfPagesNotFull";
-
-// The UMA histogram that logs the type of search result opened.
-constexpr char kSearchResultOpenDisplayTypeHistogram[] =
-    "Apps.AppListSearchResultOpenDisplayType";
-
-// The UMA histogram that logs how long the search query was when a result was
-// opened.
-constexpr char kSearchQueryLength[] = "Apps.AppListSearchQueryLength";
-
-// The UMA histogram that logs how long the search query was when a result was
-// opened in clamshell mode.
-constexpr char kSearchQueryLengthInClamshell[] =
-    "Apps.AppListSearchQueryLength.ClamshellMode";
-
-// The UMA histogram that logs how long the search query was when a result was
-// opened in tablet mode.
-constexpr char kSearchQueryLengthInTablet[] =
-    "Apps.AppListSearchQueryLength.TabletMode";
+// fullscreen. Exposed in this header because it is recorded in multiple files.
+ASH_EXPORT extern const char kAppListPeekingToFullscreenHistogram[];
 
 // The different ways to create a new page in the apps grid. These values are
 // written to logs. New enum values can be added, but existing enums must never
diff --git a/ash/app_list/app_list_metrics_unittest.cc b/ash/app_list/app_list_metrics_unittest.cc
index c9e5216a..d579612 100644
--- a/ash/app_list/app_list_metrics_unittest.cc
+++ b/ash/app_list/app_list_metrics_unittest.cc
@@ -545,10 +545,10 @@
 
   ClickHomeButton();
   histogram_tester.ExpectBucketCount(
-      kAppListToggleMethodHistogram, kShelfButton,
+      "Apps.AppListShowSource", kShelfButton,
       1 /* Number of times app list is shown with a shelf button */);
   histogram_tester.ExpectBucketCount(
-      kAppListToggleMethodHistogram, kTabletMode,
+      "Apps.AppListShowSource", kTabletMode,
       0 /* Number of times app list is shown by tablet mode transition */);
 
   GetAppListTestHelper()->CheckVisibility(true);
@@ -557,9 +557,9 @@
   // showing the app list.
   ClickHomeButton();
   histogram_tester.ExpectBucketCount(
-      kAppListToggleMethodHistogram, kShelfButton,
+      "Apps.AppListShowSource", kShelfButton,
       1 /* Number of times app list shown with a shelf button */);
-  histogram_tester.ExpectTotalCount(kAppListToggleMethodHistogram, 1);
+  histogram_tester.ExpectTotalCount("Apps.AppListShowSource", 1);
 }
 
 // Ensure that app list is not recorded as shown when going to tablet mode with
@@ -572,7 +572,7 @@
   GetAppListTestHelper()->CheckVisibility(false);
 
   // Ensure that no AppListShowSource metric was recoreded.
-  histogram_tester.ExpectTotalCount(kAppListToggleMethodHistogram, 0);
+  histogram_tester.ExpectTotalCount("Apps.AppListShowSource", 0);
 }
 
 // Ensure that app list is recorded as shown when going to tablet mode with no
@@ -584,7 +584,7 @@
   GetAppListTestHelper()->CheckVisibility(true);
 
   histogram_tester.ExpectBucketCount(
-      kAppListToggleMethodHistogram, kTabletMode,
+      "Apps.AppListShowSource", kTabletMode,
       1 /* Number of times app list shown after entering tablet mode */);
 }
 
diff --git a/ash/app_list/app_list_presenter_delegate_unittest.cc b/ash/app_list/app_list_presenter_delegate_unittest.cc
index 6fb3b39..838ebce 100644
--- a/ash/app_list/app_list_presenter_delegate_unittest.cc
+++ b/ash/app_list/app_list_presenter_delegate_unittest.cc
@@ -873,7 +873,7 @@
   event_generator->DragMouseTo(target_point);
   event_generator->ReleaseLeftButton();
   histogram_tester.ExpectUniqueSample(
-      kAppListPageSwitcherSourceHistogramInClamshell,
+      "Apps.AppListPageSwitcherSource.ClamshellMode",
       AppListPageSwitcherSource::kMouseDrag, 1);
 }
 
diff --git a/ash/app_list/app_list_presenter_impl.cc b/ash/app_list/app_list_presenter_impl.cc
index 61ef552..41d60fca 100644
--- a/ash/app_list/app_list_presenter_impl.cc
+++ b/ash/app_list/app_list_presenter_impl.cc
@@ -59,9 +59,9 @@
   }
   const base::TimeDelta input_latency = present_time - event_time_stamp;
   if (is_showing) {
-    UMA_HISTOGRAM_TIMES(kAppListShowInputLatencyHistogram, input_latency);
+    UMA_HISTOGRAM_TIMES("Apps.AppListShow.InputLatency", input_latency);
   } else {
-    UMA_HISTOGRAM_TIMES(kAppListHideInputLatencyHistogram, input_latency);
+    UMA_HISTOGRAM_TIMES("Apps.AppListHide.InputLatency", input_latency);
   }
 }
 
diff --git a/ash/app_list/views/app_list_folder_view.cc b/ash/app_list/views/app_list_folder_view.cc
index a309a8e7..ac541fb 100644
--- a/ash/app_list/views/app_list_folder_view.cc
+++ b/ash/app_list/views/app_list_folder_view.cc
@@ -515,8 +515,8 @@
       GetWidget()->GetCompositor()->RequestNewThroughputTracker();
   show_hide_metrics_tracker_->Start(
       metrics_util::ForSmoothness(base::BindRepeating([](int smoothness) {
-        UMA_HISTOGRAM_PERCENTAGE(kFolderShowHideAnimationSmoothness,
-                                 smoothness);
+        UMA_HISTOGRAM_PERCENTAGE(
+            "Apps.AppListFolder.ShowHide.AnimationSmoothness", smoothness);
       })));
 
   hide_for_reparent_ = hide_for_reparent;
diff --git a/ash/app_list/views/app_list_main_view.cc b/ash/app_list/views/app_list_main_view.cc
index e0b5acb..c9a9faa 100644
--- a/ash/app_list/views/app_list_main_view.cc
+++ b/ash/app_list/views/app_list_main_view.cc
@@ -144,7 +144,7 @@
   // TODO(jennyz): Activate the folder via AppListModel notification.
   if (item->GetItemType() == AppListFolderItem::kItemType) {
     contents_view_->ShowFolderContent(static_cast<AppListFolderItem*>(item));
-    UMA_HISTOGRAM_ENUMERATION(kAppListFolderOpenedHistogram,
+    UMA_HISTOGRAM_ENUMERATION("Apps.AppListFolderOpened",
                               kFullscreenAppListFolders, kMaxFolderOpened);
   } else {
     base::RecordAction(base::UserMetricsAction("AppList_ClickOnApp"));
diff --git a/ash/app_list/views/app_list_view.cc b/ash/app_list/views/app_list_view.cc
index cd99d69..de72e79 100644
--- a/ash/app_list/views/app_list_view.cc
+++ b/ash/app_list/views/app_list_view.cc
@@ -10,6 +10,7 @@
 #include <utility>
 #include <vector>
 
+#include "ash/app_list/app_list_metrics.h"
 #include "ash/app_list/app_list_util.h"
 #include "ash/app_list/model/app_list_model.h"
 #include "ash/app_list/views/app_list_folder_view.h"
@@ -807,7 +808,7 @@
   OnTabletModeChanged(delegate_->IsInTabletMode());
   app_list_main_view_->ShowAppListWhenReady();
 
-  UMA_HISTOGRAM_TIMES(kAppListCreationTimeHistogram,
+  UMA_HISTOGRAM_TIMES("Apps.AppListCreationTime",
                       base::Time::Now() - time_shown_.value());
   time_shown_ = base::nullopt;
   RecordFolderMetrics();
@@ -1264,7 +1265,7 @@
   if (transition == kMaxAppListStateTransition)
     return;
 
-  UMA_HISTOGRAM_ENUMERATION(kAppListStateTransitionSourceHistogram, transition,
+  UMA_HISTOGRAM_ENUMERATION("Apps.AppListStateTransitionSource", transition,
                             kMaxAppListStateTransition);
 
   switch (transition) {
@@ -2374,6 +2375,7 @@
 void AppListView::RecordFolderMetrics() {
   int number_of_apps_in_folders = 0;
   int number_of_folders = 0;
+  int non_system_folders = 0;
   AppListItemList* item_list =
       app_list_main_view_->model()->top_level_item_list();
   for (size_t i = 0; i < item_list->item_count(); ++i) {
@@ -2385,9 +2387,14 @@
     if (folder->folder_type() == AppListFolderItem::FOLDER_TYPE_OEM)
       continue;  // Don't count items in OEM folders.
     number_of_apps_in_folders += folder->item_list()->item_count();
+    if (folder->id() == kCrostiniFolderId)
+      continue;
+    // Folders that are not the OEM folder and not "Linux apps".
+    ++non_system_folders;
   }
-  UMA_HISTOGRAM_COUNTS_100(kNumberOfFoldersHistogram, number_of_folders);
-  UMA_HISTOGRAM_COUNTS_100(kNumberOfAppsInFoldersHistogram,
+  UMA_HISTOGRAM_COUNTS_100("Apps.NumberOfFolders", number_of_folders);
+  UMA_HISTOGRAM_COUNTS_100("Apps.NumberOfNonSystemFolders", non_system_folders);
+  UMA_HISTOGRAM_COUNTS_100("Apps.AppsInFolders.FullscreenAppListEnabled",
                            number_of_apps_in_folders);
 }
 
diff --git a/ash/app_list/views/app_list_view_unittest.cc b/ash/app_list/views/app_list_view_unittest.cc
index c6b427c..454b6f95 100644
--- a/ash/app_list/views/app_list_view_unittest.cc
+++ b/ash/app_list/views/app_list_view_unittest.cc
@@ -13,6 +13,7 @@
 #include <utility>
 #include <vector>
 
+#include "ash/app_list/app_list_metrics.h"
 #include "ash/app_list/app_list_test_view_delegate.h"
 #include "ash/app_list/model/app_list_test_model.h"
 #include "ash/app_list/model/search/search_box_model.h"
@@ -41,6 +42,7 @@
 #include "ash/keyboard/ui/keyboard_ui_controller.h"
 #include "ash/public/cpp/app_list/app_list_config.h"
 #include "ash/public/cpp/app_list/app_list_features.h"
+#include "ash/public/cpp/app_list/app_list_types.h"
 #include "ash/public/cpp/ash_features.h"
 #include "ash/public/cpp/pagination/pagination_model.h"
 #include "ash/public/cpp/presentation_time_recorder.h"
@@ -1665,6 +1667,65 @@
   ASSERT_EQ(ash::AppListViewState::kPeeking, view_->app_list_state());
 }
 
+TEST_F(AppListViewTest, RecordFolderMetrics_ZeroFolders) {
+  base::HistogramTester histogram;
+  Initialize(/*is_tablet_mode=*/false);
+  delegate_->GetTestModel()->PopulateApps(2);
+  Show();
+
+  // 1 sample in the 0 folders bucket.
+  EXPECT_EQ(1, histogram.GetBucketCount("Apps.NumberOfFolders", 0));
+  EXPECT_EQ(1, histogram.GetBucketCount("Apps.NumberOfNonSystemFolders", 0));
+  // 1 sample in the 0 apps bucket.
+  EXPECT_EQ(1, histogram.GetBucketCount(
+                   "Apps.AppsInFolders.FullscreenAppListEnabled", 0));
+}
+
+TEST_F(AppListViewTest, RecordFolderMetrics_OneRegularFolder) {
+  base::HistogramTester histogram;
+  Initialize(/*is_tablet_mode=*/false);
+  delegate_->GetTestModel()->CreateAndPopulateFolderWithApps(2);
+  Show();
+
+  // 1 sample in the 1 folder bucket.
+  EXPECT_EQ(1, histogram.GetBucketCount("Apps.NumberOfFolders", 1));
+  EXPECT_EQ(1, histogram.GetBucketCount("Apps.NumberOfNonSystemFolders", 1));
+  // 1 sample in the 2 apps bucket.
+  EXPECT_EQ(1, histogram.GetBucketCount(
+                   "Apps.AppsInFolders.FullscreenAppListEnabled", 2));
+}
+
+TEST_F(AppListViewTest, RecordFolderMetrics_OemFolder) {
+  base::HistogramTester histogram;
+  Initialize(/*is_tablet_mode=*/false);
+  delegate_->GetTestModel()->CreateSingleItemFolder(kOemFolderId, "item_id");
+  Show();
+
+  // 1 sample in the 1 folder bucket.
+  EXPECT_EQ(1, histogram.GetBucketCount("Apps.NumberOfFolders", 1));
+  // 1 sample in the 0 folders bucket, because OEM folder is a system folder.
+  EXPECT_EQ(1, histogram.GetBucketCount("Apps.NumberOfNonSystemFolders", 0));
+  // 1 sample in the 0 apps bucket, because OEM apps don't count.
+  EXPECT_EQ(1, histogram.GetBucketCount(
+                   "Apps.AppsInFolders.FullscreenAppListEnabled", 0));
+}
+
+TEST_F(AppListViewTest, RecordFolderMetrics_LinuxAppsFolder) {
+  base::HistogramTester histogram;
+  Initialize(/*is_tablet_mode=*/false);
+  delegate_->GetTestModel()->CreateSingleItemFolder(kCrostiniFolderId,
+                                                    "item_id");
+  Show();
+
+  // 1 sample in the 1 folder bucket.
+  EXPECT_EQ(1, histogram.GetBucketCount("Apps.NumberOfFolders", 1));
+  // 1 sample in the 0 folders bucket, because "Linux apps" is a system folder.
+  EXPECT_EQ(1, histogram.GetBucketCount("Apps.NumberOfNonSystemFolders", 0));
+  // 1 sample in the 1 app bucket, because Linux apps do count.
+  EXPECT_EQ(1, histogram.GetBucketCount(
+                   "Apps.AppsInFolders.FullscreenAppListEnabled", 1));
+}
+
 // Tests that in side shelf mode, the app list opens in fullscreen by default
 // and verifies that the top rounded corners of the app list background are
 // hidden (see https://crbug.com/920082).
diff --git a/ash/app_list/views/apps_grid_view.cc b/ash/app_list/views/apps_grid_view.cc
index c9cfd7c..0492291 100644
--- a/ash/app_list/views/apps_grid_view.cc
+++ b/ash/app_list/views/apps_grid_view.cc
@@ -3714,7 +3714,7 @@
 
 void AppsGridView::RecordPageMetrics() {
   DCHECK(!folder_delegate_);
-  UMA_HISTOGRAM_COUNTS_100(kNumberOfPagesHistogram,
+  UMA_HISTOGRAM_COUNTS_100("Apps.NumberOfPages",
                            pagination_model_.total_pages());
 
   // Calculate the number of pages that have empty slots.
@@ -3732,11 +3732,11 @@
     if (item_num % TilesPerPage() > 0)
       page_count = 1;
   }
-  UMA_HISTOGRAM_COUNTS_100(kNumberOfPagesNotFullHistogram, page_count);
+  UMA_HISTOGRAM_COUNTS_100("Apps.NumberOfPagesNotFull", page_count);
 }
 
 void AppsGridView::RecordAppMovingTypeMetrics(AppListAppMovingType type) {
-  UMA_HISTOGRAM_ENUMERATION(kAppListAppMovingType, type,
+  UMA_HISTOGRAM_ENUMERATION("Apps.AppListAppMovingType", type,
                             kMaxAppListAppMovingType);
 }
 
diff --git a/ash/app_list/views/apps_grid_view_unittest.cc b/ash/app_list/views/apps_grid_view_unittest.cc
index ace49fb..fbc40bc 100644
--- a/ash/app_list/views/apps_grid_view_unittest.cc
+++ b/ash/app_list/views/apps_grid_view_unittest.cc
@@ -2120,7 +2120,7 @@
   EXPECT_EQ(2u, folder_item->ChildItemCount());
   EXPECT_TRUE(folder_item->FindChildItem(first_item_id));
   EXPECT_TRUE(folder_item->FindChildItem(second_item_id));
-  histogram_tester.ExpectBucketCount(kAppListAppMovingType,
+  histogram_tester.ExpectBucketCount("Apps.AppListAppMovingType",
                                      kMoveByKeyboardIntoFolder, 1);
 
   // Test that, when a folder is selected, control+shift+arrow does nothing.
@@ -2128,7 +2128,7 @@
 
   EXPECT_TRUE(apps_grid_view_->IsSelectedView(new_folder));
   EXPECT_EQ(2u, folder_item->ChildItemCount());
-  histogram_tester.ExpectBucketCount(kAppListAppMovingType,
+  histogram_tester.ExpectBucketCount("Apps.AppListAppMovingType",
                                      kMoveByKeyboardIntoFolder, 1);
 
   // Move selection to the item to the right of the folder and put it in the
@@ -2140,7 +2140,7 @@
 
   EXPECT_TRUE(apps_grid_view_->IsSelectedView(new_folder));
   EXPECT_EQ(3u, folder_item->ChildItemCount());
-  histogram_tester.ExpectBucketCount(kAppListAppMovingType,
+  histogram_tester.ExpectBucketCount("Apps.AppListAppMovingType",
                                      kMoveByKeyboardIntoFolder, 2);
 
   // Move selection to the item below the folder and put it in the folder.
@@ -2149,7 +2149,7 @@
 
   EXPECT_TRUE(apps_grid_view_->IsSelectedView(new_folder));
   EXPECT_EQ(4u, folder_item->ChildItemCount());
-  histogram_tester.ExpectBucketCount(kAppListAppMovingType,
+  histogram_tester.ExpectBucketCount("Apps.AppListAppMovingType",
                                      kMoveByKeyboardIntoFolder, 3);
 
   // Move the folder to the second row, then put the item above the folder in
@@ -2160,7 +2160,7 @@
 
   EXPECT_TRUE(apps_grid_view_->IsSelectedView(new_folder));
   EXPECT_EQ(5u, folder_item->ChildItemCount());
-  histogram_tester.ExpectBucketCount(kAppListAppMovingType,
+  histogram_tester.ExpectBucketCount("Apps.AppListAppMovingType",
                                      kMoveByKeyboardIntoFolder, 4);
 }
 
diff --git a/ash/app_list/views/expand_arrow_view.cc b/ash/app_list/views/expand_arrow_view.cc
index d917796..45cf824 100644
--- a/ash/app_list/views/expand_arrow_view.cc
+++ b/ash/app_list/views/expand_arrow_view.cc
@@ -376,7 +376,7 @@
 }
 
 void ExpandArrowView::TransitToFullscreenAllAppsState() {
-  UMA_HISTOGRAM_ENUMERATION(kPageOpenedHistogram, AppListState::kStateApps,
+  UMA_HISTOGRAM_ENUMERATION("Apps.AppListPageOpened", AppListState::kStateApps,
                             AppListState::kStateLast);
   UMA_HISTOGRAM_ENUMERATION(kAppListPeekingToFullscreenHistogram, kExpandArrow,
                             kMaxPeekingToFullscreen);
diff --git a/ash/app_list/views/search_box_view.cc b/ash/app_list/views/search_box_view.cc
index 985042e8..2e70afb 100644
--- a/ash/app_list/views/search_box_view.cc
+++ b/ash/app_list/views/search_box_view.cc
@@ -89,9 +89,7 @@
                              AppListView* app_list_view)
     : SearchBoxViewBase(delegate),
       view_delegate_(view_delegate),
-      app_list_view_(app_list_view),
-      is_app_list_search_autocomplete_enabled_(
-          app_list_features::IsAppListSearchAutocompleteEnabled()) {}
+      app_list_view_(app_list_view) {}
 
 SearchBoxView::~SearchBoxView() {
   search_model_->search_box()->RemoveObserver(this);
@@ -805,8 +803,7 @@
 bool SearchBoxView::ShouldProcessAutocomplete() {
   // IME sets composition text while the user is typing, so avoid handle
   // autocomplete in this case to avoid conflicts.
-  return is_app_list_search_autocomplete_enabled_ &&
-         !(search_box()->IsIMEComposing() && highlight_range_.is_empty());
+  return !(search_box()->IsIMEComposing() && highlight_range_.is_empty());
 }
 
 void SearchBoxView::ResetHighlightRange() {
diff --git a/ash/app_list/views/search_box_view.h b/ash/app_list/views/search_box_view.h
index affc8ecc..8d75f14 100644
--- a/ash/app_list/views/search_box_view.h
+++ b/ash/app_list/views/search_box_view.h
@@ -176,9 +176,6 @@
   AppListView* app_list_view_;
   ContentsView* contents_view_ = nullptr;
 
-  // True if app list search autocomplete is enabled.
-  const bool is_app_list_search_autocomplete_enabled_;
-
   // Whether tablet mode is active.
   bool is_tablet_mode_ = false;
 
diff --git a/ash/app_list/views/search_box_view_unittest.cc b/ash/app_list/views/search_box_view_unittest.cc
index f4f90bb..0566137 100644
--- a/ash/app_list/views/search_box_view_unittest.cc
+++ b/ash/app_list/views/search_box_view_unittest.cc
@@ -25,7 +25,6 @@
 #include "base/macros.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/test/metrics/user_action_tester.h"
-#include "base/test/scoped_feature_list.h"
 #include "ui/base/ime/composition_text.h"
 #include "ui/events/base_event_utils.h"
 #include "ui/gfx/image/image_skia.h"
@@ -841,13 +840,6 @@
   SearchBoxViewAutocompleteTest() = default;
   ~SearchBoxViewAutocompleteTest() override = default;
 
-  // Overridden from testing::Test
-  void SetUp() override {
-    scoped_feature_list_.InitWithFeatures(
-        {app_list_features::kEnableAppListSearchAutocomplete}, {});
-    SearchBoxViewTest::SetUp();
-  }
-
   // Expect the entire autocomplete suggestion if |should_autocomplete| is true,
   // expect only typed characters otherwise.
   void ExpectAutocompleteSuggestion(bool should_autocomplete) {
@@ -952,8 +944,6 @@
   }
 
  private:
-  base::test::ScopedFeatureList scoped_feature_list_;
-
   DISALLOW_COPY_AND_ASSIGN(SearchBoxViewAutocompleteTest);
 };
 
diff --git a/ash/content/shimless_rma/resources/BUILD.gn b/ash/content/shimless_rma/resources/BUILD.gn
index 5ce38d2..ec3842e 100644
--- a/ash/content/shimless_rma/resources/BUILD.gn
+++ b/ash/content/shimless_rma/resources/BUILD.gn
@@ -37,6 +37,7 @@
 js_library("shimless_rma") {
   deps = [
     "//third_party/polymer/v3_0/components-chromium/polymer:polymer_bundled",
+    "//ui/webui/resources/cr_elements/cr_button:cr_button.m",
   ]
 }
 
diff --git a/ash/content/shimless_rma/resources/index.html b/ash/content/shimless_rma/resources/index.html
index 5bb94ed6b..52597ab3 100644
--- a/ash/content/shimless_rma/resources/index.html
+++ b/ash/content/shimless_rma/resources/index.html
@@ -10,8 +10,8 @@
   <body>
     <shimless-rma></shimless-rma>
 
+    <link rel="stylesheet" src="shimless_rma_shared_css.js">
     <script type="module" src="shimless_rma.js"></script>
-    <link rel="stylesheet" src="shimless_rma_shared_css.js"></script>
     <!-- Below mojo script required to run browser tests -->
     <script src="chrome://resources/mojo/mojo/public/js/mojo_bindings_lite.js">
     </script>
diff --git a/ash/content/shimless_rma/resources/shimless_rma.html b/ash/content/shimless_rma/resources/shimless_rma.html
index 8eafab3d..7d340c5 100644
--- a/ash/content/shimless_rma/resources/shimless_rma.html
+++ b/ash/content/shimless_rma/resources/shimless_rma.html
@@ -1,2 +1,28 @@
-<div id="header"></div>
-<button on-click="close_">close</button>
\ No newline at end of file
+<style include="cr-shared-style shimless-rma-shared">
+</style>
+<div id="shimlessRMAContainer">
+	<div class="shimlessHeader">
+		<cr-button 
+			id="back" on-click="onBackBtnClicked_"
+			disabled="[[isBtnDisabled_(currentPage_.btnBack)]]"
+			hidden$="[[isBtnHidden_(currentPage_.btnBack)]]">
+		  Back
+		</cr-button>
+	</div>
+	<!-- TODO(joonbug): i18n button labels -->
+	<div id="contentContainer"></div>
+	<div class="shimlessFooter">
+		<cr-button
+			id="cancel" on-click="onCancelBtnClicked_"
+			disabled="[[isBtnDisabled_(currentPage_.btnCancel)]]"
+			hidden$="[[isBtnHidden_(currentPage_.btnCancel)]]">
+		  Cancel
+		</cr-button>
+		<cr-button
+			id="next" on-click="onNextBtnClicked_"
+			disabled="[[isBtnDisabled_(currentPage_.btnNext)]]"
+			hidden$="[[isBtnHidden_(currentPage_.btnNext)]]">
+		  Next
+		</cr-button>
+	</div>
+</div>
\ No newline at end of file
diff --git a/ash/content/shimless_rma/resources/shimless_rma.js b/ash/content/shimless_rma/resources/shimless_rma.js
index fdcc171..cb74fd98 100644
--- a/ash/content/shimless_rma/resources/shimless_rma.js
+++ b/ash/content/shimless_rma/resources/shimless_rma.js
@@ -2,9 +2,32 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+import 'chrome://resources/cr_elements/cr_button/cr_button.m.js';
+import './shimless_rma_shared_css.js';
+
 import {html, PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 
 /**
+ * @typedef {{
+ *  componentIs: string,
+ *  btnNext: string,
+ *  btnNextLabel: string,
+ *  btnCancel: string,
+ *  btnBack: string,
+ * }}
+ */
+let PageInfo;
+
+/**
+ * // TODO(joonbug): update type to <RmaState, PageInfo> using a Map.
+ * @type {!Object<number, !PageInfo>}
+ */
+const StateComponentMapping = {
+  // TODO(joonbug): Update with enum and actual componentId.
+  0: {componentIs: 'component1'},
+};
+
+/**
  * @fileoverview
  * 'shimless-rma' is the main page for the shimless rma process modal dialog.
  */
@@ -17,14 +40,65 @@
     return html`{__html_template__}`;
   }
 
+  static get properties() {
+    return {
+      /**
+       * Current PageInfo based on current state
+       * @protected
+       * @type {PageInfo}
+       */
+      currentPage_: {
+        type: Object,
+        value: {},
+      },
+    };
+  }
+
   /** @override */
   ready() {
     super.ready();
-    this.$.header.textContent = 'hello world';  // remove
+    this.fetchState_().then((state) => this.loadState_(state));
   }
 
-  close_() {
-    window.close();
+  /** @private */
+  fetchState_() {
+    // TODO(joonbug): fetch from fake
+    return Promise.resolve(0);
+  }
+
+  /** @private */
+  loadState_(state) {
+    const pageInfo = StateComponentMapping[state];
+    this.currentPage_ = pageInfo;
+    // TODO(joonbug): Load component
+  }
+
+  /** @protected */
+  isBtnHidden_(btn) {
+    return btn === 'hidden';
+  }
+
+  /** @protected */
+  isBtnDisabled_(btn) {
+    return btn === 'disabled';
+  }
+
+  /** @protected */
+  onBackBtnClicked_() {
+    // TODO(joonbug): fill with action
+    return;
+  }
+
+  /** @protected */
+  onNextBtnClicked_() {
+    // TODO(joonbug): fill with action
+    return;
+  }
+
+  /** @protected */
+  onCancelBtnClicked_() {
+    // TODO(joonbug): fill with action
+    return;
   }
 };
 
diff --git a/ash/public/cpp/app_list/app_list_features.cc b/ash/public/cpp/app_list/app_list_features.cc
index 665cd2d1..c6f062c 100644
--- a/ash/public/cpp/app_list/app_list_features.cc
+++ b/ash/public/cpp/app_list/app_list_features.cc
@@ -12,8 +12,6 @@
 
 const base::Feature kEnableAppDataSearch{"EnableAppDataSearch",
                                          base::FEATURE_DISABLED_BY_DEFAULT};
-const base::Feature kEnableAppListSearchAutocomplete{
-    "EnableAppListSearchAutocomplete", base::FEATURE_ENABLED_BY_DEFAULT};
 const base::Feature kEnableAppRanker{"EnableAppRanker",
                                      base::FEATURE_ENABLED_BY_DEFAULT};
 const base::Feature kEnableZeroStateAppsRanker{
@@ -60,10 +58,6 @@
   return base::FeatureList::IsEnabled(kEnableAppDataSearch);
 }
 
-bool IsAppListSearchAutocompleteEnabled() {
-  return base::FeatureList::IsEnabled(kEnableAppListSearchAutocomplete);
-}
-
 bool IsAppRankerEnabled() {
   return base::FeatureList::IsEnabled(kEnableAppRanker);
 }
diff --git a/ash/public/cpp/app_list/app_list_features.h b/ash/public/cpp/app_list/app_list_features.h
index 84ba2fb..65c2953 100644
--- a/ash/public/cpp/app_list/app_list_features.h
+++ b/ash/public/cpp/app_list/app_list_features.h
@@ -21,9 +21,6 @@
 // Enables in-app data search.
 ASH_PUBLIC_EXPORT extern const base::Feature kEnableAppDataSearch;
 
-// Enables the feature to autocomplete text typed in the AppList search box.
-ASH_PUBLIC_EXPORT extern const base::Feature kEnableAppListSearchAutocomplete;
-
 // Enable app ranking models.
 ASH_PUBLIC_EXPORT extern const base::Feature kEnableAppRanker;
 
@@ -86,7 +83,6 @@
 ASH_PUBLIC_EXPORT extern const base::Feature kLauncherQueryHighlighting;
 
 bool ASH_PUBLIC_EXPORT IsAppDataSearchEnabled();
-bool ASH_PUBLIC_EXPORT IsAppListSearchAutocompleteEnabled();
 bool ASH_PUBLIC_EXPORT IsAppRankerEnabled();
 bool ASH_PUBLIC_EXPORT IsZeroStateAppsRankerEnabled();
 bool ASH_PUBLIC_EXPORT IsQueryBasedMixedTypesRankerEnabled();
diff --git a/ash/public/cpp/app_list/app_list_types.cc b/ash/public/cpp/app_list/app_list_types.cc
index 60b1df9..41f1f08 100644
--- a/ash/public/cpp/app_list/app_list_types.cc
+++ b/ash/public/cpp/app_list/app_list_types.cc
@@ -8,6 +8,10 @@
 
 const char kOemFolderId[] = "ddb1da55-d478-4243-8642-56d3041f0263";
 
+// In order to be compatible with sync folder id must match standard.
+// Generated using crx_file::id_util::GenerateId("LinuxAppsFolder")
+const char kCrostiniFolderId[] = "ddolnhmblagmcagkedkbfejapapdimlk";
+
 ////////////////////////////////////////////////////////////////////////////////
 // AppListItemMetadata:
 
diff --git a/ash/public/cpp/app_list/app_list_types.h b/ash/public/cpp/app_list/app_list_types.h
index 5b6f98a..a25c107 100644
--- a/ash/public/cpp/app_list/app_list_types.h
+++ b/ash/public/cpp/app_list/app_list_types.h
@@ -34,6 +34,9 @@
 // Id of OEM folder in app list.
 ASH_PUBLIC_EXPORT extern const char kOemFolderId[];
 
+// The AppListItem ID of the "Linux apps" folder.
+ASH_PUBLIC_EXPORT extern const char kCrostiniFolderId[];
+
 // App list config types supported by AppListConfig.
 enum class AppListConfigType {
   // Config used on large screens when app_list_features::ScalableAppList
diff --git a/base/task/single_thread_task_executor_unittest.cc b/base/task/single_thread_task_executor_unittest.cc
index 0164a25..0e37d36 100644
--- a/base/task/single_thread_task_executor_unittest.cc
+++ b/base/task/single_thread_task_executor_unittest.cc
@@ -37,6 +37,7 @@
 #include "base/threading/sequence_local_storage_slot.h"
 #include "base/threading/thread.h"
 #include "base/threading/thread_task_runner_handle.h"
+#include "base/time/time.h"
 #include "build/build_config.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -386,13 +387,29 @@
 
 #endif  // defined(OS_WIN)
 
-void PostNTasksThenQuit(int posts_remaining) {
-  if (posts_remaining > 1) {
-    ThreadTaskRunnerHandle::Get()->PostTask(
-        FROM_HERE, BindOnce(&PostNTasksThenQuit, posts_remaining - 1));
-  } else {
-    RunLoop::QuitCurrentWhenIdleDeprecated();
+void PostNTasksThenQuit(TimeTicks begin_ticks,
+                        TimeTicks last_post_ticks,
+                        int posts_remaining,
+                        OnceClosure on_done) {
+  // Tasks should be running on a decent heart beat. Some platforms/bots however
+  // have a hard time posting+running *all* tasks before test timeout, allow
+  // those platforms to pass anyways so long as they kept a decent heartbeat up
+  // to that point.
+  const auto now = TimeTicks::Now();
+  EXPECT_LT(now - last_post_ticks, TestTimeouts::tiny_timeout());
+  if (posts_remaining == 0 ||
+      now - begin_ticks >= TestTimeouts::action_max_timeout()) {
+    if (posts_remaining > 0) {
+      LOG(ERROR) << "Slow test environment, bailing out early with "
+                 << posts_remaining << " tasks to go.";
+    }
+    std::move(on_done).Run();
+    return;
   }
+
+  ThreadTaskRunnerHandle::Get()->PostTask(
+      FROM_HERE, BindOnce(&PostNTasksThenQuit, begin_ticks, now,
+                          posts_remaining - 1, std::move(on_done)));
 }
 
 #if defined(OS_WIN)
@@ -1253,27 +1270,28 @@
   EXPECT_EQ(static_cast<size_t>(task_index), order.Size());
 }
 
-// There was a bug in the MessagePumpGLib where posting tasks recursively
-// caused the message loop to hang, due to the buffer of the internal pipe
-// becoming full. Test all SingleThreadTaskExecutor types to ensure this issue
-// does not exist in other MessagePumps.
+// Regression test for crbug.com/170904 where posting tasks recursively caused
+// the message loop to hang in MessagePumpGLib, due to the buffer of the
+// internal pipe becoming full. Test all SingleThreadTaskExecutor types to
+// ensure this issue does not exist in other MessagePumps.
 //
-// On Linux, the pipe buffer size is 64KiB by default. The bug caused one
-// byte accumulated in the pipe per two posts, so we should repeat 128K
-// times to reproduce the bug.
-#if defined(OS_FUCHSIA) || defined(OS_CHROMEOS)
-// TODO(crbug.com/810077): This is flaky on Fuchsia.
-// TODO(crbug.com/1188497): Also flaky on Chrome OS.
-#define MAYBE_RecursivePosts DISABLED_RecursivePosts
-#else
-#define MAYBE_RecursivePosts RecursivePosts
-#endif
-TEST_P(SingleThreadTaskExecutorTypedTest, MAYBE_RecursivePosts) {
+// On Linux, the pipe buffer size is 64KiB by default. The bug caused one byte
+// accumulated in the pipe per two posts, so we should repeat 128K times to
+// reproduce the bug.
+//
+// Some platforms+bots are flakily unable to run that many tasks before test
+// timeout (see crbug.com/810077 and crbug.com/1188497). This test will bail
+// early and pass in those situations as long as it was able to keep a decent
+// heartbeat between tasks.
+TEST_P(SingleThreadTaskExecutorTypedTest, RecursivePostsDoNotFloodPipe) {
   const int kNumTimes = 1 << 17;
   SingleThreadTaskExecutor executor(GetParam());
-  executor.task_runner()->PostTask(FROM_HERE,
-                                   BindOnce(&PostNTasksThenQuit, kNumTimes));
-  RunLoop().Run();
+  const auto begin_ticks = TimeTicks::Now();
+  RunLoop run_loop;
+  executor.task_runner()->PostTask(
+      FROM_HERE, BindOnce(&PostNTasksThenQuit, begin_ticks, begin_ticks,
+                          kNumTimes - 1, run_loop.QuitClosure()));
+  run_loop.Run();
 }
 
 TEST_P(SingleThreadTaskExecutorTypedTest, NestableTasksAllowedAtTopLevel) {
diff --git a/build/android/pylib/local/device/local_device_instrumentation_test_run.py b/build/android/pylib/local/device/local_device_instrumentation_test_run.py
index 52195e9..8541cd73 100644
--- a/build/android/pylib/local/device/local_device_instrumentation_test_run.py
+++ b/build/android/pylib/local/device/local_device_instrumentation_test_run.py
@@ -888,17 +888,20 @@
           tests_to_rerun.append(t)
 
     # If we have a crash that isn't recognized as a crash in a batch, the tests
-    # will be marked as unknown. When they're reran unbatched and pass,
+    # will be marked as unknown. Sometimes a test failure causes a crash, but
+    # the crash isn't recorded because the failure was detected first.
+    # When the UNKNOWN tests are reran while unbatched and pass,
     # they'll have an UNKNOWN, PASS status, so will be improperly marked as
     # flaky, so change status to NOTRUN and don't try rerunning. They will
     # get rerun individually at the local_device_test_run/environment level.
     # as the "Batch" annotation was removed.
-    found_crash = False
+    found_crash_or_fail = False
     for r in results:
-      if r.GetType() == base_test_result.ResultType.CRASH:
-        found_crash = True
+      if (r.GetType() == base_test_result.ResultType.CRASH
+          or r.GetType() == base_test_result.ResultType.FAIL):
+        found_crash_or_fail = True
         break
-    if not found_crash:
+    if not found_crash_or_fail:
       # Don't bother rerunning since the unrecognized crashes in
       # the batch will keep failing.
       tests_to_rerun = None
diff --git a/build/fuchsia/linux.sdk.sha1 b/build/fuchsia/linux.sdk.sha1
index 374eb05e..7cb92c4 100644
--- a/build/fuchsia/linux.sdk.sha1
+++ b/build/fuchsia/linux.sdk.sha1
@@ -1 +1 @@
-4.20210430.0.1
+4.20210430.1.1
diff --git a/build/fuchsia/mac.sdk.sha1 b/build/fuchsia/mac.sdk.sha1
index 374eb05e..7cb92c4 100644
--- a/build/fuchsia/mac.sdk.sha1
+++ b/build/fuchsia/mac.sdk.sha1
@@ -1 +1 @@
-4.20210430.0.1
+4.20210430.1.1
diff --git a/chrome/android/chrome_java_resources.gni b/chrome/android/chrome_java_resources.gni
index 4e92370d..af75699 100644
--- a/chrome/android/chrome_java_resources.gni
+++ b/chrome/android/chrome_java_resources.gni
@@ -643,6 +643,7 @@
   "java/res/drawable/popup_bg_bottom_tinted.xml",
   "java/res/drawable/qr_code.xml",
   "java/res/drawable/screenshot.xml",
+  "java/res/drawable/search_sogou.xml",
   "java/res/drawable/send_tab.xml",
   "java/res/drawable/shared_clipboard_zero_state_dark.xml",
   "java/res/drawable/shared_clipboard_zero_state_light.xml",
diff --git a/chrome/android/chrome_java_sources.gni b/chrome/android/chrome_java_sources.gni
index 376e478..ef76501 100644
--- a/chrome/android/chrome_java_sources.gni
+++ b/chrome/android/chrome_java_sources.gni
@@ -736,9 +736,12 @@
   "java/src/org/chromium/chrome/browser/javascript/WebContextFetcher.java",
   "java/src/org/chromium/chrome/browser/language/LanguageAskPrompt.java",
   "java/src/org/chromium/chrome/browser/lens/LensPolicyUtils.java",
+  "java/src/org/chromium/chrome/browser/locale/DefaultSearchEngineDialogHelper.java",
   "java/src/org/chromium/chrome/browser/locale/DefaultSearchEnginePromoDialog.java",
+  "java/src/org/chromium/chrome/browser/locale/LocaleChangedBroadcastReceiver.java",
   "java/src/org/chromium/chrome/browser/locale/LocaleManager.java",
   "java/src/org/chromium/chrome/browser/locale/LocaleTemplateUrlLoader.java",
+  "java/src/org/chromium/chrome/browser/locale/SogouPromoDialog.java",
   "java/src/org/chromium/chrome/browser/login/ChromeHttpAuthHandler.java",
   "java/src/org/chromium/chrome/browser/media/MediaCaptureDevicesDispatcherAndroid.java",
   "java/src/org/chromium/chrome/browser/media/MediaCaptureNotificationServiceImpl.java",
@@ -801,7 +804,6 @@
   "java/src/org/chromium/chrome/browser/notifications/WebPlatformNotificationMetrics.java",
   "java/src/org/chromium/chrome/browser/notifications/channels/ChannelsUpdater.java",
   "java/src/org/chromium/chrome/browser/notifications/channels/ChromeChannelDefinitions.java",
-  "java/src/org/chromium/chrome/browser/notifications/channels/LocaleChangedBroadcastReceiver.java",
   "java/src/org/chromium/chrome/browser/notifications/channels/SiteChannelsManager.java",
   "java/src/org/chromium/chrome/browser/notifications/scheduler/DisplayAgent.java",
   "java/src/org/chromium/chrome/browser/notifications/scheduler/NotificationSchedulerTask.java",
diff --git a/chrome/android/chrome_test_java_sources.gni b/chrome/android/chrome_test_java_sources.gni
index 84ad903..50fb0bab 100644
--- a/chrome/android/chrome_test_java_sources.gni
+++ b/chrome/android/chrome_test_java_sources.gni
@@ -261,6 +261,9 @@
   "javatests/src/org/chromium/chrome/browser/jsdialog/JavascriptTabModalDialogTest.java",
   "javatests/src/org/chromium/chrome/browser/language/AppLocaleUtilsTest.java",
   "javatests/src/org/chromium/chrome/browser/language/settings/LanguageSettingsTest.java",
+  "javatests/src/org/chromium/chrome/browser/locale/DefaultSearchEngineDialogHelperTest.java",
+  "javatests/src/org/chromium/chrome/browser/locale/DefaultSearchEngineDialogHelperUtils.java",
+  "javatests/src/org/chromium/chrome/browser/locale/DefaultSearchEnginePromoDialogTest.java",
   "javatests/src/org/chromium/chrome/browser/locale/LocaleManagerReferralTest.java",
   "javatests/src/org/chromium/chrome/browser/locale/LocaleManagerTest.java",
   "javatests/src/org/chromium/chrome/browser/login/ChromeHttpAuthHandlerTest.java",
@@ -274,7 +277,6 @@
   "javatests/src/org/chromium/chrome/browser/metrics/MainIntentBehaviorMetricsIntegrationTest.java",
   "javatests/src/org/chromium/chrome/browser/metrics/PageLoadMetricsTest.java",
   "javatests/src/org/chromium/chrome/browser/metrics/StartupLoadingMetricsTest.java",
-  "javatests/src/org/chromium/chrome/browser/metrics/StartupPermissionsMetricsTest.java",
   "javatests/src/org/chromium/chrome/browser/modaldialog/ChromeTabModalPresenterTest.java",
   "javatests/src/org/chromium/chrome/browser/modaldialog/ModalDialogViewRenderTest.java",
   "javatests/src/org/chromium/chrome/browser/multiwindow/MultiWindowIntegrationTest.java",
@@ -493,9 +495,6 @@
   "javatests/src/org/chromium/chrome/browser/query_tiles/ViewActions.java",
   "javatests/src/org/chromium/chrome/browser/read_later/ReadLaterContextMenuTest.java",
   "javatests/src/org/chromium/chrome/browser/reengagement/ReengagementNotificationControllerIntegrationTest.java",
-  "javatests/src/org/chromium/chrome/browser/search_engines/DefaultSearchEngineDialogHelperTest.java",
-  "javatests/src/org/chromium/chrome/browser/search_engines/DefaultSearchEngineDialogHelperUtils.java",
-  "javatests/src/org/chromium/chrome/browser/search_engines/DefaultSearchEnginePromoDialogTest.java",
   "javatests/src/org/chromium/chrome/browser/search_engines/TemplateUrlServiceTest.java",
   "javatests/src/org/chromium/chrome/browser/search_engines/settings/SearchEngineSettingsTest.java",
   "javatests/src/org/chromium/chrome/browser/searchwidget/SearchActivityTest.java",
diff --git a/chrome/android/expectations/monochrome_public_bundle.AndroidManifest.expected b/chrome/android/expectations/monochrome_public_bundle.AndroidManifest.expected
index 2fbf0153..cbcac9b 100644
--- a/chrome/android/expectations/monochrome_public_bundle.AndroidManifest.expected
+++ b/chrome/android/expectations/monochrome_public_bundle.AndroidManifest.expected
@@ -971,6 +971,13 @@
         android:name="org.chromium.chrome.browser.customtabs.CustomTabsShareBroadcastReceiver"
         android:exported="false">
     </receiver>  # DIFF-ANCHOR: fd245bee
+    <receiver  # DIFF-ANCHOR: de591772
+        android:name="org.chromium.chrome.browser.locale.LocaleChangedBroadcastReceiver"
+        android:exported="false">
+      <intent-filter>  # DIFF-ANCHOR: 5458f2a2
+        <action android:name="android.intent.action.LOCALE_CHANGED"/>
+      </intent-filter>  # DIFF-ANCHOR: 5458f2a2
+    </receiver>  # DIFF-ANCHOR: de591772
     <receiver  # DIFF-ANCHOR: 2b9c19aa
         android:name="org.chromium.chrome.browser.notifications.NotificationIntentInterceptor$Receiver"
         android:exported="false">
@@ -987,13 +994,6 @@
         <action android:name="org.chromium.chrome.browser.notifications.CLOSE_NOTIFICATION"/>
       </intent-filter>  # DIFF-ANCHOR: 1c1c5ed8
     </receiver>  # DIFF-ANCHOR: df76237f
-    <receiver  # DIFF-ANCHOR: a9b53579
-        android:name="org.chromium.chrome.browser.notifications.channels.LocaleChangedBroadcastReceiver"
-        android:exported="false">
-      <intent-filter>  # DIFF-ANCHOR: 5458f2a2
-        <action android:name="android.intent.action.LOCALE_CHANGED"/>
-      </intent-filter>  # DIFF-ANCHOR: 5458f2a2
-    </receiver>  # DIFF-ANCHOR: a9b53579
     <receiver  # DIFF-ANCHOR: 0cefd906
         android:name="org.chromium.chrome.browser.notifications.scheduler.DisplayAgent$Receiver"
         android:exported="false">
diff --git a/chrome/android/expectations/trichrome_chrome_bundle.AndroidManifest.expected b/chrome/android/expectations/trichrome_chrome_bundle.AndroidManifest.expected
index 42ca7da..4ab32acc 100644
--- a/chrome/android/expectations/trichrome_chrome_bundle.AndroidManifest.expected
+++ b/chrome/android/expectations/trichrome_chrome_bundle.AndroidManifest.expected
@@ -909,6 +909,13 @@
         android:name="org.chromium.chrome.browser.customtabs.CustomTabsShareBroadcastReceiver"
         android:exported="false">
     </receiver>  # DIFF-ANCHOR: fd245bee
+    <receiver  # DIFF-ANCHOR: de591772
+        android:name="org.chromium.chrome.browser.locale.LocaleChangedBroadcastReceiver"
+        android:exported="false">
+      <intent-filter>  # DIFF-ANCHOR: 5458f2a2
+        <action android:name="android.intent.action.LOCALE_CHANGED"/>
+      </intent-filter>  # DIFF-ANCHOR: 5458f2a2
+    </receiver>  # DIFF-ANCHOR: de591772
     <receiver  # DIFF-ANCHOR: 2b9c19aa
         android:name="org.chromium.chrome.browser.notifications.NotificationIntentInterceptor$Receiver"
         android:exported="false">
@@ -925,13 +932,6 @@
         <action android:name="org.chromium.chrome.browser.notifications.CLOSE_NOTIFICATION"/>
       </intent-filter>  # DIFF-ANCHOR: 1c1c5ed8
     </receiver>  # DIFF-ANCHOR: df76237f
-    <receiver  # DIFF-ANCHOR: a9b53579
-        android:name="org.chromium.chrome.browser.notifications.channels.LocaleChangedBroadcastReceiver"
-        android:exported="false">
-      <intent-filter>  # DIFF-ANCHOR: 5458f2a2
-        <action android:name="android.intent.action.LOCALE_CHANGED"/>
-      </intent-filter>  # DIFF-ANCHOR: 5458f2a2
-    </receiver>  # DIFF-ANCHOR: a9b53579
     <receiver  # DIFF-ANCHOR: 0cefd906
         android:name="org.chromium.chrome.browser.notifications.scheduler.DisplayAgent$Receiver"
         android:exported="false">
diff --git a/chrome/android/features/autofill_assistant/javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantChromeTabIntegrationTest.java b/chrome/android/features/autofill_assistant/javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantChromeTabIntegrationTest.java
index 88f2d117..5b4d042 100644
--- a/chrome/android/features/autofill_assistant/javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantChromeTabIntegrationTest.java
+++ b/chrome/android/features/autofill_assistant/javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantChromeTabIntegrationTest.java
@@ -6,8 +6,6 @@
 
 import static androidx.test.espresso.Espresso.onView;
 import static androidx.test.espresso.action.ViewActions.click;
-import static androidx.test.espresso.action.ViewActions.pressImeActionButton;
-import static androidx.test.espresso.action.ViewActions.typeText;
 import static androidx.test.espresso.assertion.ViewAssertions.doesNotExist;
 import static androidx.test.espresso.assertion.ViewAssertions.matches;
 import static androidx.test.espresso.matcher.ViewMatchers.isCompletelyDisplayed;
@@ -30,6 +28,7 @@
 import static org.chromium.chrome.browser.autofill_assistant.AutofillAssistantUiTestUtil.waitUntilViewMatchesCondition;
 
 import android.support.test.InstrumentationRegistry;
+import android.view.KeyEvent;
 
 import androidx.test.espresso.Espresso;
 import androidx.test.espresso.matcher.ViewMatchers.Visibility;
@@ -55,11 +54,16 @@
 import org.chromium.chrome.browser.autofill_assistant.proto.SupportedScriptProto.PresentationProto;
 import org.chromium.chrome.browser.autofill_assistant.proto.TellProto;
 import org.chromium.chrome.browser.flags.ChromeSwitches;
+import org.chromium.chrome.browser.omnibox.UrlBar;
 import org.chromium.chrome.browser.tabmodel.TabModelUtils;
 import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
 import org.chromium.chrome.test.ChromeTabbedActivityTestRule;
 import org.chromium.chrome.test.util.ChromeTabUtils;
+import org.chromium.chrome.test.util.OmniboxTestUtils;
+import org.chromium.chrome.test.util.WaitForFocusHelper;
 import org.chromium.components.browser_ui.widget.scrim.ScrimCoordinator;
+import org.chromium.content_public.browser.test.util.KeyUtils;
+import org.chromium.content_public.browser.test.util.TestThreadUtils;
 import org.chromium.net.test.EmbeddedTestServer;
 import org.chromium.ui.test.util.UiRestriction;
 
@@ -387,7 +391,7 @@
         int initialTabId =
                 TabModelUtils.getCurrentTabId(mTestRule.getActivity().getCurrentTabModel());
 
-        startAutofillAssistantOnTab(TEST_PAGE_A, scriptA, scriptB);
+        startAutofillAssistantOnTab(TEST_PAGE_A, scriptA);
         waitUntilViewMatchesCondition(
                 allOf(withText("Sticky"), isDescendantOfA(withId(R.id.header))),
                 isCompletelyDisplayed());
@@ -397,7 +401,7 @@
         waitUntilViewAssertionTrue(allOf(withText("Sticky"), isDescendantOfA(withId(R.id.header))),
                 doesNotExist(), DEFAULT_MAX_TIME_TO_POLL);
 
-        startAutofillAssistantOnTab(TEST_PAGE_B);
+        startAutofillAssistantOnTab(TEST_PAGE_B, scriptB);
         waitUntilViewMatchesCondition(withText("Prompt B"), isCompletelyDisplayed());
 
         Espresso.pressBack();
@@ -497,19 +501,22 @@
                 withEffectiveVisibility(Visibility.VISIBLE));
 
         // Clicking location bar hides UI and shows the keyboard.
-        onView(withId(org.chromium.chrome.R.id.url_bar)).perform(click());
+        final UrlBar urlBar = mTestRule.getActivity().findViewById(R.id.url_bar);
+        WaitForFocusHelper.acquireFocusForView(urlBar);
+        OmniboxTestUtils.waitForFocusAndKeyboardActive(urlBar, true);
         waitUntilViewMatchesCondition(withText("Prompt"), not(isCompletelyDisplayed()));
-        waitUntilKeyboardMatchesCondition(mTestRule, /* isShowing= */ true);
 
         // Closing keyboard brings it back.
+        Espresso.closeSoftKeyboard();
         Espresso.pressBack();
         waitUntilViewMatchesCondition(withText("Prompt"), isCompletelyDisplayed());
         waitUntilViewMatchesCondition(is(mScrimCoordinator.getViewForTesting()),
                 withEffectiveVisibility(Visibility.VISIBLE));
 
         // Committing URL shows error.
-        onView(withId(org.chromium.chrome.R.id.url_bar))
-                .perform(click(), typeText(getURL(TEST_PAGE_B)), pressImeActionButton());
+        TestThreadUtils.runOnUiThreadBlocking(() -> { urlBar.setText(getURL(TEST_PAGE_B)); });
+        KeyUtils.singleKeyEventView(
+                InstrumentationRegistry.getInstrumentation(), urlBar, KeyEvent.KEYCODE_ENTER);
         waitUntilViewMatchesCondition(withText(containsString("Sorry")), isCompletelyDisplayed());
     }
 
@@ -624,7 +631,6 @@
 
     @Test
     @MediumTest
-    @DisabledTest(message = "crbug.com/1171149")
     public void interactingWithLocationBarHidesOnboarding() {
         // Onboarding has not been accepted.
         AutofillAssistantPreferencesUtil.setInitialPreferences(false);
@@ -640,11 +646,13 @@
                 withEffectiveVisibility(Visibility.VISIBLE));
 
         // Clicking location bar hides UI and shows the keyboard.
-        onView(withId(org.chromium.chrome.R.id.url_bar)).perform(click());
+        final UrlBar urlBar = mTestRule.getActivity().findViewById(R.id.url_bar);
+        WaitForFocusHelper.acquireFocusForView(urlBar);
+        OmniboxTestUtils.waitForFocusAndKeyboardActive(urlBar, true);
         waitUntilViewMatchesCondition(withId(R.id.button_init_ok), not(isDisplayed()));
-        waitUntilKeyboardMatchesCondition(mTestRule, /* isShowing= */ true);
 
         // Closing keyboard brings it back.
+        Espresso.closeSoftKeyboard();
         Espresso.pressBack();
         waitUntilViewMatchesCondition(withId(R.id.button_init_ok), isCompletelyDisplayed());
         waitUntilViewMatchesCondition(is(mScrimCoordinator.getViewForTesting()),
diff --git a/chrome/android/features/keyboard_accessory/public/java/src/org/chromium/chrome/browser/keyboard_accessory/data/PropertyProvider.java b/chrome/android/features/keyboard_accessory/public/java/src/org/chromium/chrome/browser/keyboard_accessory/data/PropertyProvider.java
index 1f75313..1d6263f 100644
--- a/chrome/android/features/keyboard_accessory/public/java/src/org/chromium/chrome/browser/keyboard_accessory/data/PropertyProvider.java
+++ b/chrome/android/features/keyboard_accessory/public/java/src/org/chromium/chrome/browser/keyboard_accessory/data/PropertyProvider.java
@@ -4,16 +4,16 @@
 
 package org.chromium.chrome.browser.keyboard_accessory.data;
 
-import java.util.ArrayList;
-import java.util.List;
+import java.util.HashSet;
+import java.util.Set;
 
 /**
- * A simple class that holds a list of {@link Observer}s which can be notified about new data by
+ * A simple class that holds a set of {@link Observer}s which can be notified about new data by
  * directly passing that data into {@link PropertyProvider#notifyObservers(T)}.
  * @param <T> The object this provider provides.
  */
 public class PropertyProvider<T> implements Provider<T> {
-    private final List<Observer<T>> mObservers = new ArrayList<>();
+    private final Set<Observer<T>> mObservers = new HashSet<>();
     protected int mType;
 
     public PropertyProvider() {
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridDialogCoordinator.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridDialogCoordinator.java
index 15aabab..feb45ec5 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridDialogCoordinator.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridDialogCoordinator.java
@@ -125,7 +125,7 @@
      * Destroy any members that needs clean up.
      */
     public void destroy() {
-        mTabListCoordinator.destroy();
+        mTabListCoordinator.onDestroy();
         mMediator.destroy();
         mModelChangeProcessor.destroy();
         if (mTabSelectionEditorCoordinator != null) {
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupUiCoordinator.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupUiCoordinator.java
index 60bfd7e..8b6aa6de 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupUiCoordinator.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupUiCoordinator.java
@@ -209,7 +209,7 @@
         // Early return if the component hasn't initialized yet.
         if (mActivity == null) return;
 
-        mTabStripCoordinator.destroy();
+        mTabStripCoordinator.onDestroy();
         if (mTabGridDialogCoordinator != null) {
             mTabGridDialogCoordinator.destroy();
         }
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListCoordinator.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListCoordinator.java
index 3c87a80..0836b6e 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListCoordinator.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListCoordinator.java
@@ -24,7 +24,7 @@
 import androidx.recyclerview.widget.RecyclerView;
 
 import org.chromium.base.MathUtils;
-import org.chromium.chrome.browser.lifecycle.Destroyable;
+import org.chromium.chrome.browser.lifecycle.DestroyObserver;
 import org.chromium.chrome.browser.profiles.Profile;
 import org.chromium.chrome.browser.tab.Tab;
 import org.chromium.chrome.browser.tabmodel.TabModelSelector;
@@ -48,7 +48,7 @@
  * Coordinator for showing UI for a list of tabs. Can be used in GRID or STRIP modes.
  */
 public class TabListCoordinator
-        implements PriceMessageService.PriceWelcomeMessageProvider, Destroyable {
+        implements PriceMessageService.PriceWelcomeMessageProvider, DestroyObserver {
     /**
      * Modes of showing the list of tabs.
      *
@@ -383,7 +383,7 @@
      * Destroy any members that needs clean up.
      */
     @Override
-    public void destroy() {
+    public void onDestroy() {
         mMediator.destroy();
         if (mGlobalLayoutListener != null) {
             mRecyclerView.getViewTreeObserver().removeOnGlobalLayoutListener(mGlobalLayoutListener);
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSelectionEditorCoordinator.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSelectionEditorCoordinator.java
index a3bac9f0..dbea270 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSelectionEditorCoordinator.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSelectionEditorCoordinator.java
@@ -212,7 +212,7 @@
      * Destroy any members that needs clean up.
      */
     public void destroy() {
-        mTabListCoordinator.destroy();
+        mTabListCoordinator.onDestroy();
         mTabSelectionEditorLayout.destroy();
         mTabSelectionEditorMediator.destroy();
         mTabSelectionEditorLayoutChangeProcessor.destroy();
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSwitcherCoordinator.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSwitcherCoordinator.java
index f1eb5f19..ff82045 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSwitcherCoordinator.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSwitcherCoordinator.java
@@ -26,7 +26,7 @@
 import org.chromium.chrome.browser.flags.CachedFeatureFlags;
 import org.chromium.chrome.browser.flags.ChromeFeatureList;
 import org.chromium.chrome.browser.lifecycle.ActivityLifecycleDispatcher;
-import org.chromium.chrome.browser.lifecycle.Destroyable;
+import org.chromium.chrome.browser.lifecycle.DestroyObserver;
 import org.chromium.chrome.browser.multiwindow.MultiWindowModeStateDispatcher;
 import org.chromium.chrome.browser.price_tracking.PriceDropNotificationManager;
 import org.chromium.chrome.browser.share.ShareDelegate;
@@ -55,7 +55,7 @@
  * TabSwitcher UI.
  */
 public class TabSwitcherCoordinator
-        implements Destroyable, TabSwitcher, TabSwitcher.TabListDelegate,
+        implements DestroyObserver, TabSwitcher, TabSwitcher.TabListDelegate,
                    TabSwitcherMediator.ResetHandler, TabSwitcherMediator.MessageItemsController,
                    TabSwitcherMediator.PriceWelcomeMessageController {
     /**
@@ -615,10 +615,10 @@
 
     // ResetHandler implementation.
     @Override
-    public void destroy() {
+    public void onDestroy() {
         mMenuOrKeyboardActionController.unregisterMenuOrKeyboardActionHandler(
                 mTabSwitcherMenuActionHandler);
-        mTabListCoordinator.destroy();
+        mTabListCoordinator.onDestroy();
         mMessageCardProviderCoordinator.destroy();
         mContainerViewChangeProcessor.destroy();
         if (mTabGridDialogCoordinator != null) {
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/suggestions/TabSuggestionsOrchestrator.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/suggestions/TabSuggestionsOrchestrator.java
index db5a0921..d5c496c 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/suggestions/TabSuggestionsOrchestrator.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/suggestions/TabSuggestionsOrchestrator.java
@@ -18,7 +18,7 @@
 import org.chromium.base.metrics.RecordUserAction;
 import org.chromium.chrome.browser.flags.ChromeFeatureList;
 import org.chromium.chrome.browser.lifecycle.ActivityLifecycleDispatcher;
-import org.chromium.chrome.browser.lifecycle.Destroyable;
+import org.chromium.chrome.browser.lifecycle.DestroyObserver;
 import org.chromium.chrome.browser.tabmodel.TabModelSelector;
 import org.chromium.chrome.browser.tasks.tab_management.suggestions.TabSuggestion.TabSuggestionAction;
 
@@ -33,7 +33,7 @@
  * Represents the entry point for the TabSuggestions component. Responsible for
  * registering and invoking the different {@link TabSuggestionsFetcher}.
  */
-public class TabSuggestionsOrchestrator implements TabSuggestions, Destroyable {
+public class TabSuggestionsOrchestrator implements TabSuggestions, DestroyObserver {
     public static final String TAB_SUGGESTIONS_UMA_PREFIX = "TabSuggestionsOrchestrator";
     private static final String LAST_TIMESTAMP_KEY = "LastTimestamp";
     private static final String BACKOFF_COUNT_KEY = "BackoffCountKey";
@@ -130,7 +130,7 @@
     }
 
     @Override
-    public void destroy() {
+    public void onDestroy() {
         mTabContextObserver.destroy();
         mActivityLifecycleDispatcher.unregister(this);
     }
diff --git a/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/suggestions/TabSuggestionsOrchestratorTest.java b/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/suggestions/TabSuggestionsOrchestratorTest.java
index 6df1515..0c908b3 100644
--- a/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/suggestions/TabSuggestionsOrchestratorTest.java
+++ b/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/suggestions/TabSuggestionsOrchestratorTest.java
@@ -144,7 +144,7 @@
                 mTabModelSelector, mDispatcher, new InMemorySharedPreferences());
         tabSuggestionsOrchestrator.setFetchersForTesting();
         verify(mDispatcher, times(1)).register(eq(tabSuggestionsOrchestrator));
-        tabSuggestionsOrchestrator.destroy();
+        tabSuggestionsOrchestrator.onDestroy();
         verify(mDispatcher, times(1)).unregister(eq(tabSuggestionsOrchestrator));
     }
 
diff --git a/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/v2/FeedStream.java b/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/v2/FeedStream.java
index 503270d7e..7246fa7 100644
--- a/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/v2/FeedStream.java
+++ b/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/v2/FeedStream.java
@@ -551,7 +551,7 @@
         int feedCount = mContentManager.getItemCount() - mHeaderCount;
         if (feedCount > 0) {
             mContentManager.removeContents(mHeaderCount, feedCount);
-            mRecyclerViewAnimationFinishDetector.asyncWait();
+            notifyContentChangeOnAnimationFinish();
         }
 
         mContentManager.setHandlers(new HashMap<>());
@@ -871,7 +871,7 @@
         }
 
         if (hasContentChange) {
-            mRecyclerViewAnimationFinishDetector.asyncWait();
+            notifyContentChangeOnAnimationFinish();
         }
     }
 
@@ -895,6 +895,19 @@
         return true;
     }
 
+    private void notifyContentChangeOnAnimationFinish() {
+        // This works around the bug that the out-of-screen toolbar is not brought back together
+        // with the new tab page view when it slides down. This is because the RecyclerView
+        // animation may not finish when content changed event is triggered and thus the new tab
+        // page layout view may still be partially off screen.
+        mRecyclerViewAnimationFinishDetector.asyncWait(mRecyclerView, () -> {
+            for (ContentChangedListener listener : mContentChangedListeners) {
+                listener.onContentChanged(
+                        mContentManager != null ? mContentManager.getContentList() : null);
+            }
+        });
+    }
+
     @VisibleForTesting
     void setHelpAndFeedbackLauncherForTest(HelpAndFeedbackLauncher launcher) {
         mHelpAndFeedbackLauncher = launcher;
@@ -942,16 +955,23 @@
 
     // Detects animation finishes in RecyclerView.
     // https://stackoverflow.com/questions/33710605/detect-animation-finish-in-androids-recyclerview
-    private class RecyclerViewAnimationFinishDetector
+    private static class RecyclerViewAnimationFinishDetector
             implements RecyclerView.ItemAnimator.ItemAnimatorFinishedListener {
-        private boolean mWaitingStarted;
+        private RecyclerView mRecyclerView;
+        private Runnable mFinishedCallback;
 
-        /** Asynchronously waits for the animation to finish. */
-        public void asyncWait() {
-            if (mWaitingStarted) {
+        /**
+         * Asynchronously waits for the animation to finish.
+         *
+         * @param recyclerView RecyclerView to wait for animation to finish.
+         * @param finishedCallback Callback to invoke when the animation finishes.
+         */
+        public void asyncWait(RecyclerView recyclerView, Runnable finishedCallback) {
+            if (mRecyclerView != null) {
                 return;
             }
-            mWaitingStarted = true;
+            mRecyclerView = recyclerView;
+            mFinishedCallback = finishedCallback;
 
             // The RecyclerView has not started animating yet, so post a message to the
             // message queue that will be run after the RecyclerView has started animating.
@@ -970,15 +990,10 @@
         }
 
         private void onFinished() {
-            mWaitingStarted = false;
-
-            // This works around the bug that the out-of-screen toolbar is not brought back together
-            // with the new tab page view when it slides down. This is because the RecyclerView
-            // animation may not finish when content changed event is triggered and thus the new tab
-            // page layout view may still be partially off screen.
-            for (ContentChangedListener listener : mContentChangedListeners) {
-                listener.onContentChanged(
-                        mContentManager != null ? mContentManager.getContentList() : null);
+            mRecyclerView = null;
+            if (mFinishedCallback != null) {
+                mFinishedCallback.run();
+                mFinishedCallback = null;
             }
         }
 
diff --git a/chrome/android/java/AndroidManifest.xml b/chrome/android/java/AndroidManifest.xml
index 75896d9..aa4f8f2c 100644
--- a/chrome/android/java/AndroidManifest.xml
+++ b/chrome/android/java/AndroidManifest.xml
@@ -406,7 +406,7 @@
         </receiver>
 
         <!-- Locale related -->
-        <receiver android:name="org.chromium.chrome.browser.notifications.channels.LocaleChangedBroadcastReceiver"
+        <receiver android:name="org.chromium.chrome.browser.locale.LocaleChangedBroadcastReceiver"
             android:exported="false">
             <intent-filter>
                 <action android:name="android.intent.action.LOCALE_CHANGED" />
diff --git a/chrome/browser/search_engines/android/java/res/drawable/search_sogou.xml b/chrome/android/java/res/drawable/search_sogou.xml
similarity index 100%
rename from chrome/browser/search_engines/android/java/res/drawable/search_sogou.xml
rename to chrome/android/java/res/drawable/search_sogou.xml
diff --git a/chrome/android/java/res/values/ids.xml b/chrome/android/java/res/values/ids.xml
index da754bf..8c0c75ce 100644
--- a/chrome/android/java/res/values/ids.xml
+++ b/chrome/android/java/res/values/ids.xml
@@ -15,6 +15,7 @@
     <item name="tabswitcher_multiple_drawable" type="id"/>
     <item name="tabswitcher_single_drawable" type="id"/>
     <item name="custom_tab_bottom_bar_wrapper" type="id"/>
+    <item name="default_search_engine_dialog_options" type="id"/>
 
     <!-- Media playback notification -->
     <item type="id" name="media_playback_notification" />
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ChromeInactivityTracker.java b/chrome/android/java/src/org/chromium/chrome/browser/ChromeInactivityTracker.java
index 9dd5845..96b4e1ed 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ChromeInactivityTracker.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ChromeInactivityTracker.java
@@ -7,7 +7,7 @@
 import androidx.annotation.VisibleForTesting;
 
 import org.chromium.chrome.browser.lifecycle.ActivityLifecycleDispatcher;
-import org.chromium.chrome.browser.lifecycle.Destroyable;
+import org.chromium.chrome.browser.lifecycle.DestroyObserver;
 import org.chromium.chrome.browser.lifecycle.PauseResumeWithNativeObserver;
 import org.chromium.chrome.browser.lifecycle.StartStopWithNativeObserver;
 import org.chromium.chrome.browser.preferences.SharedPreferencesManager;
@@ -17,7 +17,7 @@
  * TODO(crbug.com/1081453): Split ChromeInactivityTracker out from ChromeTabbedActivity.
  */
 public class ChromeInactivityTracker
-        implements StartStopWithNativeObserver, PauseResumeWithNativeObserver, Destroyable {
+        implements StartStopWithNativeObserver, PauseResumeWithNativeObserver, DestroyObserver {
     private static final String TAG = "InactivityTracker";
 
     private static final long UNKNOWN_LAST_BACKGROUNDED_TIME = -1;
@@ -94,7 +94,7 @@
     }
 
     @Override
-    public void destroy() {
+    public void onDestroy() {
         mLifecycleDispatcher.unregister(this);
     }
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java
index eb3f0f9..88ad86ab 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java
@@ -2414,7 +2414,7 @@
         }
 
         UmaSessionStats.updateMetricsServiceState();
-        mUmaSessionStats.startNewSession(getTabModelSelector(), getWindowAndroid());
+        mUmaSessionStats.startNewSession(getTabModelSelector());
     }
 
     /**
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/browserservices/ui/controller/trustedwebactivity/TwaVerifier.java b/chrome/android/java/src/org/chromium/chrome/browser/browserservices/ui/controller/trustedwebactivity/TwaVerifier.java
index b442675..81308e2a 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/browserservices/ui/controller/trustedwebactivity/TwaVerifier.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/browserservices/ui/controller/trustedwebactivity/TwaVerifier.java
@@ -16,7 +16,7 @@
 import org.chromium.chrome.browser.customtabs.content.CustomTabActivityTabProvider;
 import org.chromium.chrome.browser.dependency_injection.ActivityScope;
 import org.chromium.chrome.browser.lifecycle.ActivityLifecycleDispatcher;
-import org.chromium.chrome.browser.lifecycle.Destroyable;
+import org.chromium.chrome.browser.lifecycle.DestroyObserver;
 import org.chromium.components.embedder_support.util.Origin;
 import org.chromium.components.externalauth.ExternalAuthUtils;
 import org.chromium.content_public.browser.WebContents;
@@ -31,7 +31,7 @@
  * Provides Trusted Web Activity specific behaviour for the {@link CurrentPageVerifier}.
  */
 @ActivityScope
-public class TwaVerifier implements Verifier, Destroyable {
+public class TwaVerifier implements Verifier, DestroyObserver {
     /** The Digital Asset Link relationship used for Trusted Web Activities. */
     private static final int RELATIONSHIP = CustomTabsService.RELATION_HANDLE_ALL_URLS;
 
@@ -72,7 +72,7 @@
     }
 
     @Override
-    public void destroy() {
+    public void onDestroy() {
         mDestroyed = true;
     }
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/browserservices/ui/splashscreen/SplashController.java b/chrome/android/java/src/org/chromium/chrome/browser/browserservices/ui/splashscreen/SplashController.java
index 1af1090c..909bdf50 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/browserservices/ui/splashscreen/SplashController.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/browserservices/ui/splashscreen/SplashController.java
@@ -32,7 +32,7 @@
 import org.chromium.chrome.browser.flags.CachedFeatureFlags;
 import org.chromium.chrome.browser.flags.ChromeFeatureList;
 import org.chromium.chrome.browser.lifecycle.ActivityLifecycleDispatcher;
-import org.chromium.chrome.browser.lifecycle.Destroyable;
+import org.chromium.chrome.browser.lifecycle.DestroyObserver;
 import org.chromium.chrome.browser.lifecycle.InflationObserver;
 import org.chromium.chrome.browser.tab.Tab;
 import org.chromium.url.GURL;
@@ -48,7 +48,7 @@
 /** Shows and hides splash screen for Webapps, WebAPKs and TWAs. */
 @ActivityScope
 public class SplashController
-        extends CustomTabTabObserver implements InflationObserver, Destroyable {
+        extends CustomTabTabObserver implements InflationObserver, DestroyObserver {
     private static class SingleShotOnDrawListener implements ViewTreeObserver.OnDrawListener {
         private final View mView;
         private final Runnable mAction;
@@ -197,7 +197,7 @@
     }
 
     @Override
-    public void destroy() {
+    public void onDestroy() {
         if (mFadeOutAnimator != null) {
             mFadeOutAnimator.cancel();
         }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabIncognitoManager.java b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabIncognitoManager.java
index ffaaf88..4f1c14c 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabIncognitoManager.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabIncognitoManager.java
@@ -21,7 +21,7 @@
 import org.chromium.chrome.browser.dependency_injection.ActivityScope;
 import org.chromium.chrome.browser.flags.ChromeSwitches;
 import org.chromium.chrome.browser.lifecycle.ActivityLifecycleDispatcher;
-import org.chromium.chrome.browser.lifecycle.Destroyable;
+import org.chromium.chrome.browser.lifecycle.DestroyObserver;
 import org.chromium.chrome.browser.lifecycle.NativeInitObserver;
 import org.chromium.chrome.browser.profiles.OTRProfileID;
 import org.chromium.chrome.browser.profiles.Profile;
@@ -35,7 +35,8 @@
  * |isEnabledIncognitoCCT| returns true.
  */
 @ActivityScope
-public class CustomTabIncognitoManager implements NativeInitObserver, Destroyable, UnownedUserData {
+public class CustomTabIncognitoManager
+        implements NativeInitObserver, DestroyObserver, UnownedUserData {
     @SuppressLint("StaticFieldLeak") // This is for test only.
     private static CustomTabIncognitoManager sCustomTabIncognitoManagerUsedForTesting;
 
@@ -117,7 +118,7 @@
     }
 
     @Override
-    public void destroy() {
+    public void onDestroy() {
         if (mOTRProfileID != null) {
             Profile.getLastUsedRegularProfile()
                     .getOffTheRecordProfile(mOTRProfileID, /*createIfNeeded=*/true)
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabNightModeStateController.java b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabNightModeStateController.java
index a3ead23..73593e1 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabNightModeStateController.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabNightModeStateController.java
@@ -14,7 +14,7 @@
 import org.chromium.base.IntentUtils;
 import org.chromium.base.ObserverList;
 import org.chromium.chrome.browser.lifecycle.ActivityLifecycleDispatcher;
-import org.chromium.chrome.browser.lifecycle.Destroyable;
+import org.chromium.chrome.browser.lifecycle.DestroyObserver;
 import org.chromium.chrome.browser.night_mode.NightModeStateProvider;
 import org.chromium.chrome.browser.night_mode.NightModeUtils;
 import org.chromium.chrome.browser.night_mode.PowerSavingModeMonitor;
@@ -23,7 +23,7 @@
 /**
  * Maintains and provides the night mode state for {@link CustomTabActivity}.
  */
-public class CustomTabNightModeStateController implements Destroyable, NightModeStateProvider {
+public class CustomTabNightModeStateController implements DestroyObserver, NightModeStateProvider {
     private final ObserverList<Observer> mObservers = new ObserverList<>();
     private final PowerSavingModeMonitor mPowerSavingModeMonitor;
     private final SystemNightModeMonitor mSystemNightModeMonitor;
@@ -75,9 +75,9 @@
         }
     }
 
-    // Destroyable implementation.
+    // DestroyObserver implementation.
     @Override
-    public void destroy() {
+    public void onDestroy() {
         mSystemNightModeMonitor.removeObserver(mSystemNightModeObserver);
         mPowerSavingModeMonitor.removeObserver(mPowerSaveModeObserver);
     }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabTaskDescriptionHelper.java b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabTaskDescriptionHelper.java
index 972a6e84..552852b 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabTaskDescriptionHelper.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabTaskDescriptionHelper.java
@@ -23,7 +23,7 @@
 import org.chromium.chrome.browser.customtabs.content.TabObserverRegistrar.CustomTabTabObserver;
 import org.chromium.chrome.browser.dependency_injection.ActivityScope;
 import org.chromium.chrome.browser.lifecycle.ActivityLifecycleDispatcher;
-import org.chromium.chrome.browser.lifecycle.Destroyable;
+import org.chromium.chrome.browser.lifecycle.DestroyObserver;
 import org.chromium.chrome.browser.lifecycle.NativeInitObserver;
 import org.chromium.chrome.browser.profiles.Profile;
 import org.chromium.chrome.browser.tab.Tab;
@@ -43,7 +43,7 @@
  * The task description is what is shown in Android's Overview/Recents screen for each entry.
  */
 @ActivityScope
-public class CustomTabTaskDescriptionHelper implements NativeInitObserver, Destroyable {
+public class CustomTabTaskDescriptionHelper implements NativeInitObserver, DestroyObserver {
     private final Activity mActivity;
     private final CustomTabActivityTabProvider mTabProvider;
     private final TabObserverRegistrar mTabObserverRegistrar;
@@ -297,7 +297,7 @@
      * Destroys all dependent components of the task description helper.
      */
     @Override
-    public void destroy() {
+    public void onDestroy() {
         if (mFaviconHelper != null) {
             mFaviconHelper.destroy();
         }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabTrustedCdnPublisherUrlVisibility.java b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabTrustedCdnPublisherUrlVisibility.java
index ae6aa8f..79e30de 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabTrustedCdnPublisherUrlVisibility.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabTrustedCdnPublisherUrlVisibility.java
@@ -8,7 +8,7 @@
 import org.chromium.base.supplier.BooleanSupplier;
 import org.chromium.chrome.browser.flags.ChromeFeatureList;
 import org.chromium.chrome.browser.lifecycle.ActivityLifecycleDispatcher;
-import org.chromium.chrome.browser.lifecycle.Destroyable;
+import org.chromium.chrome.browser.lifecycle.DestroyObserver;
 import org.chromium.chrome.browser.tab.Tab;
 import org.chromium.chrome.browser.tab.TrustedCdn.PublisherUrlVisibility;
 import org.chromium.ui.base.WindowAndroid;
@@ -18,7 +18,7 @@
  * the availability of publisher URL of trusted CDN when attached to a custom tab activity.
  */
 class CustomTabTrustedCdnPublisherUrlVisibility
-        implements PublisherUrlVisibility, Destroyable, UnownedUserData {
+        implements PublisherUrlVisibility, DestroyObserver, UnownedUserData {
     private WindowAndroid mWindowAndroid;
     private BooleanSupplier mIsPublisherPackageForSession;
 
@@ -41,7 +41,7 @@
     }
 
     @Override
-    public void destroy() {
+    public void onDestroy() {
         PublisherUrlVisibility.detach(this);
         mWindowAndroid = null;
         mIsPublisherPackageForSession = null;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/content/CustomTabActivityTabController.java b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/content/CustomTabActivityTabController.java
index 0582d42..ec29f1e 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/content/CustomTabActivityTabController.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/content/CustomTabActivityTabController.java
@@ -338,7 +338,7 @@
             tab = maybeTakeTabFromStartupTabPreloader();
             if (tab != null) mode = TabCreationMode.FROM_STARTUP_TAB_PRELOADER;
         } else {
-            mStartupTabPreloader.destroy();
+            mStartupTabPreloader.onDestroy();
         }
 
         if (tab == null) {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/content/TabObserverRegistrar.java b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/content/TabObserverRegistrar.java
index 0292c5c..17e3b589 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/content/TabObserverRegistrar.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/content/TabObserverRegistrar.java
@@ -9,7 +9,7 @@
 import org.chromium.base.ObserverList;
 import org.chromium.chrome.browser.dependency_injection.ActivityScope;
 import org.chromium.chrome.browser.lifecycle.ActivityLifecycleDispatcher;
-import org.chromium.chrome.browser.lifecycle.Destroyable;
+import org.chromium.chrome.browser.lifecycle.DestroyObserver;
 import org.chromium.chrome.browser.metrics.PageLoadMetrics;
 import org.chromium.chrome.browser.tab.EmptyTabObserver;
 import org.chromium.chrome.browser.tab.Tab;
@@ -28,7 +28,7 @@
  * they enter/leave the TabModel.
  */
 @ActivityScope
-public class TabObserverRegistrar implements TabModelObserver, Destroyable {
+public class TabObserverRegistrar implements TabModelObserver, DestroyObserver {
     private CustomTabActivityTabProvider mTabProvider;
     private final Set<PageLoadMetrics.Observer> mPageLoadMetricsObservers = new HashSet<>();
     private final Set<TabObserver> mTabObservers = new HashSet<>();
@@ -183,7 +183,7 @@
     }
 
     @Override
-    public void destroy() {
+    public void onDestroy() {
         removePageLoadMetricsObservers();
     }
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/features/ImmersiveModeController.java b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/features/ImmersiveModeController.java
index 7d14ff2f..670ab6c 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/features/ImmersiveModeController.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/features/ImmersiveModeController.java
@@ -19,7 +19,7 @@
 
 import org.chromium.chrome.browser.dependency_injection.ActivityScope;
 import org.chromium.chrome.browser.lifecycle.ActivityLifecycleDispatcher;
-import org.chromium.chrome.browser.lifecycle.Destroyable;
+import org.chromium.chrome.browser.lifecycle.DestroyObserver;
 import org.chromium.chrome.browser.lifecycle.WindowFocusChangedObserver;
 
 import javax.inject.Inject;
@@ -28,8 +28,7 @@
  * Allows to enter and exit immersive mode in TWAs and WebAPKs.
  */
 @ActivityScope
-public class ImmersiveModeController implements WindowFocusChangedObserver, Destroyable {
-
+public class ImmersiveModeController implements WindowFocusChangedObserver, DestroyObserver {
     private static final int ENTER_IMMERSIVE_MODE_ON_WINDOW_FOCUS_DELAY_MILLIS = 300;
     private static final int RESTORE_IMMERSIVE_MODE_DELAY_MILLIS = 3000;
 
@@ -131,7 +130,7 @@
     }
 
     @Override
-    public void destroy() {
+    public void onDestroy() {
         mHandler.removeCallbacks(mUpdateImmersiveFlagsRunnable);
     }
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/directactions/DirectActionInitializer.java b/chrome/android/java/src/org/chromium/chrome/browser/directactions/DirectActionInitializer.java
index 3daabb2..527ad7f 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/directactions/DirectActionInitializer.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/directactions/DirectActionInitializer.java
@@ -19,7 +19,7 @@
 import org.chromium.chrome.browser.compositor.CompositorViewHolder;
 import org.chromium.chrome.browser.findinpage.FindToolbarManager;
 import org.chromium.chrome.browser.flags.ActivityType;
-import org.chromium.chrome.browser.lifecycle.Destroyable;
+import org.chromium.chrome.browser.lifecycle.DestroyObserver;
 import org.chromium.chrome.browser.lifecycle.NativeInitObserver;
 import org.chromium.chrome.browser.tabmodel.TabModelSelector;
 import org.chromium.components.browser_ui.bottomsheet.BottomSheetController;
@@ -37,7 +37,7 @@
  * the coordinator {@code mCoordinator}.
  */
 @TargetApi(29)
-public class DirectActionInitializer implements NativeInitObserver, Destroyable {
+public class DirectActionInitializer implements NativeInitObserver, DestroyObserver {
     private final Context mContext;
     private final BottomSheetController mBottomSheetController;
     private final BrowserControlsStateProvider mBrowserControls;
@@ -184,9 +184,9 @@
                 .allowlistActions(itemIds);
     }
 
-    // Implements Destroyable
+    // Implements DestroyObserver
     @Override
-    public void destroy() {
+    public void onDestroy() {
         mCoordinator = null;
         mDirectActionsRegistered = false;
     }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/firstrun/DefaultSearchEngineFirstRunFragment.java b/chrome/android/java/src/org/chromium/chrome/browser/firstrun/DefaultSearchEngineFirstRunFragment.java
index 4b829ea..6ab24ad 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/firstrun/DefaultSearchEngineFirstRunFragment.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/firstrun/DefaultSearchEngineFirstRunFragment.java
@@ -17,8 +17,8 @@
 import org.chromium.base.task.PostTask;
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.AppHooks;
-import org.chromium.chrome.browser.search_engines.DefaultSearchEngineDialogHelper;
-import org.chromium.chrome.browser.search_engines.SearchEnginePromoType;
+import org.chromium.chrome.browser.locale.DefaultSearchEngineDialogHelper;
+import org.chromium.chrome.browser.locale.LocaleManager.SearchEnginePromoType;
 import org.chromium.chrome.browser.search_engines.TemplateUrlServiceFactory;
 import org.chromium.components.browser_ui.widget.RadioButtonLayout;
 import org.chromium.content_public.browser.UiThreadTaskTraits;
@@ -34,7 +34,7 @@
     }
 
     @SearchEnginePromoType
-    private int mSearchEnginePromoDialogType;
+    private int mSearchEnginePromoDialoType;
     private boolean mShownRecorded;
 
     /** Layout that displays the available search engines to the user. */
@@ -54,11 +54,10 @@
         mButton.setEnabled(false);
 
         assert TemplateUrlServiceFactory.get().isLoaded();
-        mSearchEnginePromoDialogType =
+        mSearchEnginePromoDialoType =
                 AppHooks.get().getLocaleManager().getSearchEnginePromoShowType();
-        if (mSearchEnginePromoDialogType != SearchEnginePromoType.DONT_SHOW) {
-            new DefaultSearchEngineDialogHelper(mSearchEnginePromoDialogType,
-                    AppHooks.get().getLocaleManager(), mEngineLayout, mButton,
+        if (mSearchEnginePromoDialoType != SearchEnginePromoType.DONT_SHOW) {
+            new DefaultSearchEngineDialogHelper(mSearchEnginePromoDialoType, mEngineLayout, mButton,
                     getPageDelegate()::advanceToNextPage);
         }
 
@@ -79,7 +78,7 @@
         super.setUserVisibleHint(isVisibleToUser);
 
         if (isVisibleToUser) {
-            if (mSearchEnginePromoDialogType == SearchEnginePromoType.DONT_SHOW) {
+            if (mSearchEnginePromoDialoType == SearchEnginePromoType.DONT_SHOW) {
                 PostTask.postTask(UiThreadTaskTraits.DEFAULT, new Runnable() {
                     @Override
                     public void run() {
@@ -95,9 +94,9 @@
     private void recordShown() {
         if (mShownRecorded) return;
 
-        if (mSearchEnginePromoDialogType == SearchEnginePromoType.SHOW_NEW) {
+        if (mSearchEnginePromoDialoType == SearchEnginePromoType.SHOW_NEW) {
             RecordUserAction.record("SearchEnginePromo.NewDevice.Shown.FirstRun");
-        } else if (mSearchEnginePromoDialogType == SearchEnginePromoType.SHOW_EXISTING) {
+        } else if (mSearchEnginePromoDialoType == SearchEnginePromoType.SHOW_EXISTING) {
             RecordUserAction.record("SearchEnginePromo.ExistingDevice.Shown.FirstRun");
         }
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/firstrun/FirstRunFlowSequencer.java b/chrome/android/java/src/org/chromium/chrome/browser/firstrun/FirstRunFlowSequencer.java
index ccf59ad4..e3c8939 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/firstrun/FirstRunFlowSequencer.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/firstrun/FirstRunFlowSequencer.java
@@ -25,9 +25,9 @@
 import org.chromium.chrome.browser.LaunchIntentDispatcher;
 import org.chromium.chrome.browser.childaccounts.ChildAccountService;
 import org.chromium.chrome.browser.flags.ChromeSwitches;
+import org.chromium.chrome.browser.locale.LocaleManager;
 import org.chromium.chrome.browser.net.spdyproxy.DataReductionProxySettings;
 import org.chromium.chrome.browser.profiles.Profile;
-import org.chromium.chrome.browser.search_engines.SearchEnginePromoType;
 import org.chromium.chrome.browser.signin.services.IdentityServicesProvider;
 import org.chromium.chrome.browser.signin.services.SigninManager;
 import org.chromium.chrome.browser.vr.VrModuleProvider;
@@ -130,10 +130,10 @@
 
     @VisibleForTesting
     protected boolean shouldShowSearchEnginePage() {
-        @SearchEnginePromoType
+        @LocaleManager.SearchEnginePromoType
         int searchPromoType = AppHooks.get().getLocaleManager().getSearchEnginePromoShowType();
-        return searchPromoType == SearchEnginePromoType.SHOW_NEW
-                || searchPromoType == SearchEnginePromoType.SHOW_EXISTING;
+        return searchPromoType == LocaleManager.SearchEnginePromoType.SHOW_NEW
+                || searchPromoType == LocaleManager.SearchEnginePromoType.SHOW_EXISTING;
     }
 
     @VisibleForTesting
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/init/ActivityLifecycleDispatcherImpl.java b/chrome/android/java/src/org/chromium/chrome/browser/init/ActivityLifecycleDispatcherImpl.java
index 69d69ed6..89bdae7c 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/init/ActivityLifecycleDispatcherImpl.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/init/ActivityLifecycleDispatcherImpl.java
@@ -13,7 +13,7 @@
 import org.chromium.chrome.browser.lifecycle.ActivityLifecycleDispatcher;
 import org.chromium.chrome.browser.lifecycle.ActivityResultWithNativeObserver;
 import org.chromium.chrome.browser.lifecycle.ConfigurationChangedObserver;
-import org.chromium.chrome.browser.lifecycle.Destroyable;
+import org.chromium.chrome.browser.lifecycle.DestroyObserver;
 import org.chromium.chrome.browser.lifecycle.InflationObserver;
 import org.chromium.chrome.browser.lifecycle.LifecycleObserver;
 import org.chromium.chrome.browser.lifecycle.NativeInitObserver;
@@ -36,7 +36,7 @@
             new ObserverList<>();
     private final ObserverList<StartStopWithNativeObserver> mStartStopObservers =
             new ObserverList<>();
-    private final ObserverList<Destroyable> mDestroyables = new ObserverList<>();
+    private final ObserverList<DestroyObserver> mDestroyables = new ObserverList<>();
     private final ObserverList<SaveInstanceStateObserver> mSaveInstanceStateObservers =
             new ObserverList<>();
     private final ObserverList<WindowFocusChangedObserver> mWindowFocusChangesObservers =
@@ -71,8 +71,8 @@
         if (observer instanceof NativeInitObserver) {
             mNativeInitObservers.addObserver((NativeInitObserver) observer);
         }
-        if (observer instanceof Destroyable) {
-            mDestroyables.addObserver((Destroyable) observer);
+        if (observer instanceof DestroyObserver) {
+            mDestroyables.addObserver((DestroyObserver) observer);
         }
         if (observer instanceof SaveInstanceStateObserver) {
             mSaveInstanceStateObservers.addObserver((SaveInstanceStateObserver) observer);
@@ -106,8 +106,8 @@
         if (observer instanceof NativeInitObserver) {
             mNativeInitObservers.removeObserver((NativeInitObserver) observer);
         }
-        if (observer instanceof Destroyable) {
-            mDestroyables.removeObserver((Destroyable) observer);
+        if (observer instanceof DestroyObserver) {
+            mDestroyables.removeObserver((DestroyObserver) observer);
         }
         if (observer instanceof SaveInstanceStateObserver) {
             mSaveInstanceStateObservers.removeObserver((SaveInstanceStateObserver) observer);
@@ -206,8 +206,8 @@
     void dispatchOnDestroy() {
         mActivityState = ActivityState.DESTROYED;
 
-        for (Destroyable destroyable : mDestroyables) {
-            destroyable.destroy();
+        for (DestroyObserver destroyable : mDestroyables) {
+            destroyable.onDestroy();
         }
 
         // Drain observers to prevent possible memory leaks.
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/init/StartupTabPreloader.java b/chrome/android/java/src/org/chromium/chrome/browser/init/StartupTabPreloader.java
index 477f2df..7a3f50c8 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/init/StartupTabPreloader.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/init/StartupTabPreloader.java
@@ -19,7 +19,7 @@
 import org.chromium.chrome.browser.WebContentsFactory;
 import org.chromium.chrome.browser.flags.ChromeFeatureList;
 import org.chromium.chrome.browser.lifecycle.ActivityLifecycleDispatcher;
-import org.chromium.chrome.browser.lifecycle.Destroyable;
+import org.chromium.chrome.browser.lifecycle.DestroyObserver;
 import org.chromium.chrome.browser.profiles.Profile;
 import org.chromium.chrome.browser.profiles.ProfileManager;
 import org.chromium.chrome.browser.tab.EmptyTabObserver;
@@ -42,7 +42,7 @@
  * This class attempts to preload the tab if the url is known from the intent when the profile
  * is created. This is done to improve startup latency.
  */
-public class StartupTabPreloader implements ProfileManager.Observer, Destroyable {
+public class StartupTabPreloader implements ProfileManager.Observer, DestroyObserver {
     private static final String EXTRA_DISABLE_STARTUP_TAB_PRELOADER =
             "org.chromium.chrome.browser.init.DISABLE_STARTUP_TAB_PRELOADER";
 
@@ -70,7 +70,7 @@
     }
 
     @Override
-    public void destroy() {
+    public void onDestroy() {
         if (mTab != null) mTab.destroy();
         mTab = null;
 
@@ -243,7 +243,7 @@
     private class StartupTabObserver extends EmptyTabObserver {
         @Override
         public void onCrash(Tab tab) {
-            destroy();
+            onDestroy();
         }
     }
 }
diff --git a/chrome/browser/search_engines/android/java/src/org/chromium/chrome/browser/search_engines/DefaultSearchEngineDialogHelper.java b/chrome/android/java/src/org/chromium/chrome/browser/locale/DefaultSearchEngineDialogHelper.java
similarity index 73%
rename from chrome/browser/search_engines/android/java/src/org/chromium/chrome/browser/search_engines/DefaultSearchEngineDialogHelper.java
rename to chrome/android/java/src/org/chromium/chrome/browser/locale/DefaultSearchEngineDialogHelper.java
index e5a8cc249..7841732e 100644
--- a/chrome/browser/search_engines/android/java/src/org/chromium/chrome/browser/search_engines/DefaultSearchEngineDialogHelper.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/locale/DefaultSearchEngineDialogHelper.java
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-package org.chromium.chrome.browser.search_engines;
+package org.chromium.chrome.browser.locale;
 
 import android.view.View;
 import android.view.View.OnClickListener;
@@ -12,6 +12,7 @@
 
 import androidx.annotation.Nullable;
 
+import org.chromium.chrome.browser.locale.LocaleManager.SearchEnginePromoType;
 import org.chromium.components.browser_ui.widget.RadioButtonLayout;
 import org.chromium.components.search_engines.TemplateUrl;
 
@@ -22,27 +23,32 @@
 /** Handles user interactions with a user dialog that lets them pick a default search engine. */
 public class DefaultSearchEngineDialogHelper implements OnCheckedChangeListener, OnClickListener {
     /** Handles interactions with the TemplateUrlService and LocaleManager. */
-    public interface Delegate {
-        /**
-         * Get the list of search engines that a user may choose between.
-         * @param promoType Which search engine list to show.
-         * @return List of engines to show.
-         */
-        List<TemplateUrl> getSearchEnginesForPromoDialog(@SearchEnginePromoType int type);
+    public static class HelperDelegate {
+        private final int mDialogType;
 
         /**
-         * To be called after the user has made a selection from a search engine promo dialog.
-         * @param type The type of search engine promo dialog that was shown.
-         * @param keywords The keywords for all search engines listed in the order shown to the
-         *         user.
-         * @param keyword The keyword for the search engine chosen.
+         * Basic constructor for the delegate.
+         * @param dialogType {@link SearchEnginePromoType} for the dialog this delegate belongs to.
          */
-        void onUserSearchEngineChoice(
-                @SearchEnginePromoType int type, List<String> keywords, String keyword);
+        public HelperDelegate(@SearchEnginePromoType int dialogType) {
+            mDialogType = dialogType;
+        }
+
+        /** Determine what search engines will be listed. */
+        protected List<TemplateUrl> getSearchEngines() {
+            List<TemplateUrl> templateUrls =
+                    LocaleManager.getInstance().getSearchEnginesForPromoDialog(mDialogType);
+            return templateUrls;
+        }
+
+        /** Called when the search engine the user selected is confirmed to be the one they want. */
+        protected void onUserSeachEngineChoice(List<String> keywords, String keyword) {
+            LocaleManager.getInstance().onUserSearchEngineChoiceFromPromoDialog(
+                    mDialogType, keywords, keyword);
+        }
     }
 
-    private final int mDialogType;
-    private final Delegate mDelegate;
+    private final HelperDelegate mDelegate;
     private final Runnable mFinishRunnable;
     private final Button mConfirmButton;
 
@@ -67,21 +73,19 @@
      * Constructs a DefaultSearchEngineDialogHelper.
      *
      * @param dialogType     Dialog type to show.
-     * @param delegate       Delegate for getting search engine list/selection notification.
      * @param controls       {@link RadioButtonLayout} that will contains all the engine options.
      * @param confirmButton  Button that the user clicks on to confirm their selection.
      * @param finishRunnable Runs after the user has confirmed their selection.
      */
-    public DefaultSearchEngineDialogHelper(@SearchEnginePromoType int dialogType, Delegate delegate,
+    public DefaultSearchEngineDialogHelper(@SearchEnginePromoType int dialogType,
             RadioButtonLayout controls, Button confirmButton, Runnable finishRunnable) {
-        mDialogType = dialogType;
         mConfirmButton = confirmButton;
         mConfirmButton.setOnClickListener(this);
         mFinishRunnable = finishRunnable;
-        mDelegate = delegate;
+        mDelegate = createDelegate(dialogType);
 
         // Shuffle up the engines.
-        List<TemplateUrl> engines = mDelegate.getSearchEnginesForPromoDialog(dialogType);
+        List<TemplateUrl> engines = mDelegate.getSearchEngines();
         List<CharSequence> engineNames = new ArrayList<>();
         mSearchEngineKeywords = new ArrayList<>();
         Collections.shuffle(engines);
@@ -135,11 +139,15 @@
 
         mConfirmedKeyword = mCurrentlySelectedKeyword;
 
-        mDelegate.onUserSearchEngineChoice(
-                mDialogType, mSearchEngineKeywords, mConfirmedKeyword.toString());
+        mDelegate.onUserSeachEngineChoice(mSearchEngineKeywords, mConfirmedKeyword.toString());
         mFinishRunnable.run();
     }
 
+    /** Creates the delegate that interacts with the TemplateUrlService. */
+    protected HelperDelegate createDelegate(@SearchEnginePromoType int dialogType) {
+        return new HelperDelegate(dialogType);
+    }
+
     /** Prevent the user from moving forward until they've clicked a search engine. */
     private final void updateButtonState() {
         mConfirmButton.setEnabled(mCurrentlySelectedKeyword != null);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/locale/DefaultSearchEnginePromoDialog.java b/chrome/android/java/src/org/chromium/chrome/browser/locale/DefaultSearchEnginePromoDialog.java
index 20fe3e1..d02c9c3 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/locale/DefaultSearchEnginePromoDialog.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/locale/DefaultSearchEnginePromoDialog.java
@@ -4,41 +4,136 @@
 
 package org.chromium.chrome.browser.locale;
 
+import android.annotation.SuppressLint;
 import android.app.Activity;
+import android.content.DialogInterface;
+import android.os.Bundle;
+import android.widget.Button;
 
 import androidx.annotation.Nullable;
 import androidx.annotation.VisibleForTesting;
 
 import org.chromium.base.Callback;
-import org.chromium.chrome.browser.search_engines.DefaultSearchEngineDialogHelper;
+import org.chromium.base.metrics.RecordUserAction;
+import org.chromium.chrome.R;
+import org.chromium.chrome.browser.locale.LocaleManager.SearchEnginePromoType;
+import org.chromium.components.browser_ui.widget.PromoDialog;
+import org.chromium.components.browser_ui.widget.RadioButtonLayout;
 
-/**
- * Extends org.chromium.chrome.browser.search_engines.DefaultSearchEnginePromoDialog
- * for downstream build. Should be deleted once the downstream is updated to use
- * org.chromium.chrome.browser.search_engines.DefaultSearchEnginePromoDialog.
- */
-public class DefaultSearchEnginePromoDialog
-        extends org.chromium.chrome.browser.search_engines.DefaultSearchEnginePromoDialog {
-    public DefaultSearchEnginePromoDialog(Activity activity,
-            DefaultSearchEngineDialogHelper.Delegate delegate, int dialogType,
-            @Nullable Callback<Boolean> onSuccessCallback) {
-        super(activity, delegate, dialogType, onSuccessCallback);
-    }
-
+/** A dialog that forces the user to choose a default search engine. */
+public class DefaultSearchEnginePromoDialog extends PromoDialog {
     /** Notified about events happening to the dialog. */
-    public interface DefaultSearchEnginePromoDialogObserver {
+    public static interface DefaultSearchEnginePromoDialogObserver {
         void onDialogShown(DefaultSearchEnginePromoDialog shownDialog);
     }
+    private static DefaultSearchEnginePromoDialogObserver sObserver;
+
+    @SuppressLint("StaticFieldLeak")
+    private static DefaultSearchEnginePromoDialog sCurrentDialog;
+
+    /** Used to determine the promo dialog contents. */
+    @SearchEnginePromoType
+    private final int mDialogType;
+
+    /** Called when the dialog is dismissed after the user has chosen a search engine. */
+    private final Callback<Boolean> mOnSuccessCallback;
+
+    /** Encapsulates most of the logic for filling the dialog and handling clicks. */
+    private DefaultSearchEngineDialogHelper mHelper;
 
     /**
-     * Forwards a static instance of the observer to
-     * org.chromium.chrome.browser.search_engines.DefaultSearchEnginePromoDialog.
+     * Construct the default search engine promo.
+     *
+     * @param activity    Activity to build the dialog with.
+     * @param dialogType  Type of dialog to show.
+     * @param onSuccessCallback Notified whether the user successfully chose a search engine and
+     *                          dismissed the dialog.
      */
+    DefaultSearchEnginePromoDialog(Activity activity, @SearchEnginePromoType int dialogType,
+            @Nullable Callback<Boolean> onSuccessCallback) {
+        super(activity);
+        mDialogType = dialogType;
+        mOnSuccessCallback = onSuccessCallback;
+        setOnDismissListener(this);
+
+        // No one should be able to bypass this dialog by clicking outside or by hitting back.
+        setCancelable(false);
+        setCanceledOnTouchOutside(false);
+
+        if (dialogType == LocaleManager.SearchEnginePromoType.SHOW_NEW) forceOpaqueBackground();
+    }
+
+    @Override
+    protected DialogParams getDialogParams() {
+        PromoDialog.DialogParams params = new PromoDialog.DialogParams();
+        params.headerStringResource = R.string.search_engine_dialog_title;
+        params.footerStringResource = R.string.search_engine_dialog_footer;
+        params.primaryButtonStringResource = R.string.ok;
+        return params;
+    }
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        Button okButton = (Button) findViewById(R.id.button_primary);
+        okButton.setEnabled(false);
+
+        RadioButtonLayout radioButtons = new RadioButtonLayout(getContext());
+        radioButtons.setId(R.id.default_search_engine_dialog_options);
+        addControl(radioButtons);
+
+        Runnable dismissRunnable = new Runnable() {
+            @Override
+            public void run() {
+                dismiss();
+            }
+        };
+        mHelper = new DefaultSearchEngineDialogHelper(
+                mDialogType, radioButtons, okButton, dismissRunnable);
+    }
+
+    @Override
+    public void show() {
+        super.show();
+        if (sCurrentDialog != null) sCurrentDialog.dismiss();
+        setCurrentDialog(this);
+
+        if (mDialogType == LocaleManager.SearchEnginePromoType.SHOW_NEW) {
+            RecordUserAction.record("SearchEnginePromo.NewDevice.Shown.Dialog");
+        } else if (mDialogType == LocaleManager.SearchEnginePromoType.SHOW_EXISTING) {
+            RecordUserAction.record("SearchEnginePromo.ExistingDevice.Shown.Dialog");
+        }
+        if (sObserver != null) sObserver.onDialogShown(this);
+    }
+
+    @Override
+    public void onDismiss(DialogInterface dialog) {
+        if (mHelper.getConfirmedKeyword() == null) {
+            // If no selection, finish the Activity so that the user has to respond to the dialog
+            // next time.
+            if (getOwnerActivity() != null) getOwnerActivity().finish();
+        }
+
+        if (mOnSuccessCallback != null) {
+            mOnSuccessCallback.onResult(mHelper.getConfirmedKeyword() != null);
+        }
+
+        if (sCurrentDialog == this) setCurrentDialog(null);
+    }
+
+    /** See {@link #sObserver}. */
     @VisibleForTesting
     public static void setObserverForTests(DefaultSearchEnginePromoDialogObserver observer) {
-        org.chromium.chrome.browser.search_engines.DefaultSearchEnginePromoDialog
-                .setObserverForTests2(
-                        (org.chromium.chrome.browser.search_engines.DefaultSearchEnginePromoDialog
-                                        .DefaultSearchEnginePromoDialogObserver) observer);
+        sObserver = observer;
+    }
+
+    /** @return The current visible Default Search Engine dialog. */
+    static DefaultSearchEnginePromoDialog getCurrentDialog() {
+        return sCurrentDialog;
+    }
+
+    private static void setCurrentDialog(DefaultSearchEnginePromoDialog dialog) {
+        sCurrentDialog = dialog;
     }
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/notifications/channels/LocaleChangedBroadcastReceiver.java b/chrome/android/java/src/org/chromium/chrome/browser/locale/LocaleChangedBroadcastReceiver.java
similarity index 89%
rename from chrome/android/java/src/org/chromium/chrome/browser/notifications/channels/LocaleChangedBroadcastReceiver.java
rename to chrome/android/java/src/org/chromium/chrome/browser/locale/LocaleChangedBroadcastReceiver.java
index 48a49e76..6669efb 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/notifications/channels/LocaleChangedBroadcastReceiver.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/locale/LocaleChangedBroadcastReceiver.java
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-package org.chromium.chrome.browser.notifications.channels;
+package org.chromium.chrome.browser.locale;
 
 import android.content.BroadcastReceiver;
 import android.content.Context;
@@ -10,6 +10,7 @@
 
 import org.chromium.base.task.PostTask;
 import org.chromium.base.task.TaskTraits;
+import org.chromium.chrome.browser.notifications.channels.ChannelsUpdater;
 
 /**
  * Triggered when Android's locale changes.
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/locale/LocaleManager.java b/chrome/android/java/src/org/chromium/chrome/browser/locale/LocaleManager.java
index 687051c..2e419ac2 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/locale/LocaleManager.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/locale/LocaleManager.java
@@ -26,9 +26,6 @@
 import org.chromium.chrome.browser.flags.ChromeSwitches;
 import org.chromium.chrome.browser.preferences.ChromePreferenceKeys;
 import org.chromium.chrome.browser.preferences.SharedPreferencesManager;
-import org.chromium.chrome.browser.search_engines.DefaultSearchEngineDialogHelper;
-import org.chromium.chrome.browser.search_engines.DefaultSearchEnginePromoDialog;
-import org.chromium.chrome.browser.search_engines.SogouPromoDialog;
 import org.chromium.chrome.browser.search_engines.TemplateUrlServiceFactory;
 import org.chromium.chrome.browser.search_engines.settings.SearchEngineSettings;
 import org.chromium.chrome.browser.ui.messages.snackbar.Snackbar;
@@ -48,8 +45,8 @@
  * Manager for some locale specific logics.
  * TODO(https://crbug.com/1198923) Turn this into a per-activity object.
  */
-public class LocaleManager implements DefaultSearchEngineDialogHelper.Delegate {
-    private static final String SPECIAL_LOCALE_ID = "US";
+public class LocaleManager {
+    public static final String SPECIAL_LOCALE_ID = "US";
 
     /** The current state regarding search engine promo dialogs. */
     @IntDef({SearchEnginePromoState.SHOULD_CHECK, SearchEnginePromoState.CHECKED_NOT_SHOWN,
@@ -265,14 +262,14 @@
                 return;
             case SearchEnginePromoType.SHOW_SOGOU:
                 dialogSupplier = ()
-                        -> new SogouPromoDialog(activity, this::onSelectSearchEngine,
+                        -> new SogouPromoDialog(activity, LocaleManager.this,
                                 finalizeInternalCallback, mSettingsLauncher);
                 break;
             case SearchEnginePromoType.SHOW_EXISTING:
             case SearchEnginePromoType.SHOW_NEW:
                 dialogSupplier = ()
                         -> new DefaultSearchEnginePromoDialog(
-                                activity, LocaleManager.this, shouldShow, finalizeInternalCallback);
+                                activity, shouldShow, finalizeInternalCallback);
                 break;
             default:
                 assert false;
@@ -291,16 +288,6 @@
     }
 
     /**
-     * Called when search engine to use is selected on SogouPromoDialog.
-     * @param useSogou {@code true} if Sogou engine is chosen.
-     */
-    private void onSelectSearchEngine(boolean useSogou) {
-        setSearchEngineAutoSwitch(useSogou);
-        addSpecialSearchEngines();
-        if (useSogou) overrideDefaultSearchEngine();
-    }
-
-    /**
      * @return Whether auto switch for search engine is enabled.
      */
     public boolean isSearchEngineAutoSwitchEnabled() {
@@ -401,18 +388,16 @@
         return mLocaleTemplateUrlLoader;
     }
 
-    @Override
+    /**
+     * Get the list of search engines that a user may choose between.
+     * @param promoType Which search engine list to show.
+     * @return List of engines to show.
+     */
     public List<TemplateUrl> getSearchEnginesForPromoDialog(@SearchEnginePromoType int promoType) {
         throw new IllegalStateException(
                 "Not applicable unless existing or new promos are required");
     }
 
-    @Override
-    public void onUserSearchEngineChoice(
-            @SearchEnginePromoType int type, List<String> keywords, String keyword) {
-        onUserSearchEngineChoiceFromPromoDialog(type, keywords, keyword);
-    }
-
     /** Set a LocaleManager instance. This is called only by AppHooks. */
     public static void setInstance(LocaleManager instance) {
         sInstance = instance;
diff --git a/chrome/browser/search_engines/android/java/src/org/chromium/chrome/browser/search_engines/SogouPromoDialog.java b/chrome/android/java/src/org/chromium/chrome/browser/locale/SogouPromoDialog.java
similarity index 88%
rename from chrome/browser/search_engines/android/java/src/org/chromium/chrome/browser/search_engines/SogouPromoDialog.java
rename to chrome/android/java/src/org/chromium/chrome/browser/locale/SogouPromoDialog.java
index 5d034bd..a429e700 100644
--- a/chrome/browser/search_engines/android/java/src/org/chromium/chrome/browser/search_engines/SogouPromoDialog.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/locale/SogouPromoDialog.java
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-package org.chromium.chrome.browser.search_engines;
+package org.chromium.chrome.browser.locale;
 
 import android.app.Activity;
 import android.content.DialogInterface;
@@ -20,6 +20,7 @@
 
 import org.chromium.base.Callback;
 import org.chromium.base.metrics.RecordHistogram;
+import org.chromium.chrome.R;
 import org.chromium.chrome.browser.preferences.ChromePreferenceKeys;
 import org.chromium.chrome.browser.preferences.SharedPreferencesManager;
 import org.chromium.chrome.browser.search_engines.settings.SearchEngineSettings;
@@ -52,9 +53,7 @@
     /** Run when the dialog is dismissed. */
     private final Callback<Boolean> mOnDismissedCallback;
 
-    /** Called when the search engine to use is selected. */
-    private final Callback<Boolean> mOnSelectEngineCallback;
-
+    private final LocaleManager mLocaleManager;
     private final ClickableSpan mSpan;
 
     @UserChoice
@@ -63,9 +62,10 @@
     /**
      * Creates an instance of the dialog.
      */
-    public SogouPromoDialog(Activity activity, @NonNull Callback<Boolean> onSelectEngine,
+    SogouPromoDialog(Activity activity, LocaleManager localeManager,
             @Nullable Callback<Boolean> onDismissed, @NonNull SettingsLauncher settingsLauncher) {
         super(activity);
+        mLocaleManager = localeManager;
         mSpan = new NoUnderlineClickableSpan(activity.getResources(), (widget) -> {
             mChoice = UserChoice.SETTINGS;
             settingsLauncher.launchSettingsActivity(getContext(), SearchEngineSettings.class);
@@ -74,7 +74,6 @@
         setOnDismissListener(this);
         setCanceledOnTouchOutside(false);
         mOnDismissedCallback = onDismissed;
-        mOnSelectEngineCallback = onSelectEngine;
     }
 
     @Override
@@ -119,16 +118,27 @@
         dismiss();
     }
 
+    private void keepGoogle() {
+        mLocaleManager.setSearchEngineAutoSwitch(false);
+        mLocaleManager.addSpecialSearchEngines();
+    }
+
+    private void useSogou() {
+        mLocaleManager.setSearchEngineAutoSwitch(true);
+        mLocaleManager.addSpecialSearchEngines();
+        mLocaleManager.overrideDefaultSearchEngine();
+    }
+
     @Override
     public void onDismiss(DialogInterface dialog) {
         switch (mChoice) {
             case UserChoice.KEEP_GOOGLE:
             case UserChoice.SETTINGS:
             case UserChoice.BACK_KEY:
-                mOnSelectEngineCallback.onResult(false);
+                keepGoogle();
                 break;
             case UserChoice.USE_SOGOU:
-                mOnSelectEngineCallback.onResult(true);
+                useSogou();
                 break;
             default:
                 assert false : "Unexpected choice";
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/media/MediaViewerUtils.java b/chrome/android/java/src/org/chromium/chrome/browser/media/MediaViewerUtils.java
index 1b3a7b2..8cded04 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/media/MediaViewerUtils.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/media/MediaViewerUtils.java
@@ -162,7 +162,7 @@
         if (originalUrl != null) {
             intent.putExtra(Intent.EXTRA_ORIGINATING_URI, Uri.parse(originalUrl));
         }
-        if (referrer != null) intent.putExtra(Intent.EXTRA_REFERRER, Uri.parse(originalUrl));
+        if (referrer != null) intent.putExtra(Intent.EXTRA_REFERRER, Uri.parse(referrer));
     }
 
     /**
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/metrics/UmaSessionStats.java b/chrome/android/java/src/org/chromium/chrome/browser/metrics/UmaSessionStats.java
index 7739cfe..ce77ed7 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/metrics/UmaSessionStats.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/metrics/UmaSessionStats.java
@@ -4,7 +4,6 @@
 
 package org.chromium.chrome.browser.metrics;
 
-import android.Manifest;
 import android.content.ComponentCallbacks;
 import android.content.Context;
 import android.content.res.Configuration;
@@ -19,7 +18,6 @@
 import org.chromium.base.task.TaskTraits;
 import org.chromium.chrome.browser.DefaultBrowserInfo;
 import org.chromium.chrome.browser.instantapps.InstantAppsHandler;
-import org.chromium.chrome.browser.omnibox.voice.VoiceRecognitionHandler.AudioPermissionState;
 import org.chromium.chrome.browser.privacy.settings.PrivacyPreferencesManagerImpl;
 import org.chromium.chrome.browser.tab.Tab;
 import org.chromium.chrome.browser.tabmodel.TabModel;
@@ -28,7 +26,6 @@
 import org.chromium.components.embedder_support.util.UrlUtilities;
 import org.chromium.content_public.browser.BrowserStartupController;
 import org.chromium.content_public.browser.WebContents;
-import org.chromium.ui.base.WindowAndroid;
 import org.chromium.url.GURL;
 
 /**
@@ -91,11 +88,9 @@
     /**
      * Starts a new session for logging.
      * @param tabModelSelector A TabModelSelector instance for recording tab counts on page loads.
-     *        If null, UmaSessionStats does not record page loads and tab counts.
-     * @param windowAndroid The WindowAndroid used for querying app permission status.
-     *        If null, UmaSessionStats will not record permission status.
+     * If null, UmaSessionStats does not record page loads and tab counts.
      */
-    public void startNewSession(TabModelSelector tabModelSelector, WindowAndroid windowAndroid) {
+    public void startNewSession(TabModelSelector tabModelSelector) {
         ensureNativeInitialized();
 
         mTabModelSelector = tabModelSelector;
@@ -126,24 +121,6 @@
         updatePreferences();
         updateMetricsServiceState();
         DefaultBrowserInfo.logDefaultBrowserStats();
-        if (windowAndroid != null) {
-            recordAudioPermissionState(windowAndroid);
-        }
-    }
-
-    private void recordAudioPermissionState(WindowAndroid windowAndroid) {
-        @AudioPermissionState
-        int permissionState;
-        if (windowAndroid.hasPermission(Manifest.permission.RECORD_AUDIO)) {
-            permissionState = AudioPermissionState.GRANTED;
-        } else if (windowAndroid.canRequestPermission(Manifest.permission.RECORD_AUDIO)) {
-            permissionState = AudioPermissionState.DENIED_CAN_ASK_AGAIN;
-        } else {
-            permissionState = AudioPermissionState.DENIED_CANNOT_ASK_AGAIN;
-        }
-        RecordHistogram.recordEnumeratedHistogram(
-                "VoiceInteraction.AudioPermissionEvent.SessionStart", permissionState,
-                AudioPermissionState.NUM_ENTRIES);
     }
 
     private static void ensureNativeInitialized() {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/modaldialog/TabModalLifetimeHandler.java b/chrome/android/java/src/org/chromium/chrome/browser/modaldialog/TabModalLifetimeHandler.java
index 75d8567..724678f 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/modaldialog/TabModalLifetimeHandler.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/modaldialog/TabModalLifetimeHandler.java
@@ -11,7 +11,7 @@
 import org.chromium.chrome.browser.contextualsearch.ContextualSearchManager;
 import org.chromium.chrome.browser.fullscreen.FullscreenManager;
 import org.chromium.chrome.browser.lifecycle.ActivityLifecycleDispatcher;
-import org.chromium.chrome.browser.lifecycle.Destroyable;
+import org.chromium.chrome.browser.lifecycle.DestroyObserver;
 import org.chromium.chrome.browser.lifecycle.NativeInitObserver;
 import org.chromium.chrome.browser.modaldialog.ChromeTabModalPresenter.TabModalBrowserControlsVisibilityDelegate;
 import org.chromium.chrome.browser.tab.EmptyTabObserver;
@@ -33,7 +33,7 @@
  * Class responsible for handling dismissal of a tab modal dialog on user actions outside the tab
  * modal dialog.
  */
-public class TabModalLifetimeHandler implements NativeInitObserver, Destroyable {
+public class TabModalLifetimeHandler implements NativeInitObserver, DestroyObserver {
     /** The observer to dismiss all dialogs when the attached tab is not interactable. */
     private final TabObserver mTabObserver = new EmptyTabObserver() {
         @Override
@@ -173,7 +173,7 @@
     }
 
     @Override
-    public void destroy() {
+    public void onDestroy() {
         if (mTabModelObserver != null) mTabModelObserver.destroy();
         if (mPresenter != null) mPresenter.destroy();
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/multiwindow/MultiInstanceManager.java b/chrome/android/java/src/org/chromium/chrome/browser/multiwindow/MultiInstanceManager.java
index 231ef124..983c30b7 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/multiwindow/MultiInstanceManager.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/multiwindow/MultiInstanceManager.java
@@ -31,7 +31,7 @@
 import org.chromium.chrome.browser.flags.ChromeSwitches;
 import org.chromium.chrome.browser.lifecycle.ActivityLifecycleDispatcher;
 import org.chromium.chrome.browser.lifecycle.ConfigurationChangedObserver;
-import org.chromium.chrome.browser.lifecycle.Destroyable;
+import org.chromium.chrome.browser.lifecycle.DestroyObserver;
 import org.chromium.chrome.browser.lifecycle.NativeInitObserver;
 import org.chromium.chrome.browser.lifecycle.PauseResumeWithNativeObserver;
 import org.chromium.chrome.browser.lifecycle.RecreateObserver;
@@ -52,7 +52,7 @@
 public class MultiInstanceManager
         implements PauseResumeWithNativeObserver, RecreateObserver, ConfigurationChangedObserver,
                    NativeInitObserver, MultiWindowModeStateDispatcher.MultiWindowModeObserver,
-                   Destroyable, MenuOrKeyboardActionController.MenuOrKeyboardActionHandler {
+                   DestroyObserver, MenuOrKeyboardActionController.MenuOrKeyboardActionHandler {
     /**
      * Should be called when multi-instance mode is started.
      */
@@ -119,7 +119,7 @@
     }
 
     @Override
-    public void destroy() {
+    public void onDestroy() {
         mMultiWindowModeStateDispatcher.removeObserver(this);
         mMenuOrKeyboardActionController.unregisterMenuOrKeyboardActionHandler(this);
         DisplayManager displayManager =
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ntp/NewTabPageLayout.java b/chrome/android/java/src/org/chromium/chrome/browser/ntp/NewTabPageLayout.java
index 6a705a1b..2b6d6ef3 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ntp/NewTabPageLayout.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ntp/NewTabPageLayout.java
@@ -903,9 +903,9 @@
     }
 
     private void maybeShowVideoTutorialTryNowIPH() {
+        if (getToolbarTransitionPercentage() > 0f) return;
         VideoTutorialTryNowTracker tryNowTracker = VideoTutorialServiceFactory.getTryNowTracker();
         UserEducationHelper userEducationHelper = new UserEducationHelper(mActivity, new Handler());
-        // TODO(shaktisahu): Pass correct y-inset.
         // TODO(shaktisahu): Determine if there is conflict with another IPH.
         if (tryNowTracker.didClickTryNowButton(FeatureType.SEARCH)) {
             IPHCommandBuilder iphCommandBuilder = createIPHCommandBuilder(mActivity.getResources(),
@@ -917,7 +917,6 @@
         }
 
         if (tryNowTracker.didClickTryNowButton(FeatureType.VOICE_SEARCH)) {
-            // TODO(shaktisahu): Pass voice search button.
             IPHCommandBuilder iphCommandBuilder = createIPHCommandBuilder(mActivity.getResources(),
                     R.string.video_tutorials_iph_tap_voice_icon_to_start,
                     R.string.video_tutorials_iph_tap_voice_icon_to_start,
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ntp/search/SearchBoxCoordinator.java b/chrome/android/java/src/org/chromium/chrome/browser/ntp/search/SearchBoxCoordinator.java
index 251fc66..e15ed41 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ntp/search/SearchBoxCoordinator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ntp/search/SearchBoxCoordinator.java
@@ -54,7 +54,7 @@
     }
 
     public void destroy() {
-        mMediator.destroy();
+        mMediator.onDestroy();
     }
 
     public void setAlpha(float alpha) {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ntp/search/SearchBoxMediator.java b/chrome/android/java/src/org/chromium/chrome/browser/ntp/search/SearchBoxMediator.java
index 146755dd..d539363 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ntp/search/SearchBoxMediator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ntp/search/SearchBoxMediator.java
@@ -22,7 +22,7 @@
 import org.chromium.chrome.browser.lens.LensIntentParams;
 import org.chromium.chrome.browser.lens.LensQueryParams;
 import org.chromium.chrome.browser.lifecycle.ActivityLifecycleDispatcher;
-import org.chromium.chrome.browser.lifecycle.Destroyable;
+import org.chromium.chrome.browser.lifecycle.DestroyObserver;
 import org.chromium.chrome.browser.lifecycle.NativeInitObserver;
 import org.chromium.chrome.browser.omnibox.voice.AssistantVoiceSearchService;
 import org.chromium.chrome.browser.preferences.SharedPreferencesManager;
@@ -41,7 +41,7 @@
 import java.util.List;
 
 class SearchBoxMediator
-        implements Destroyable, NativeInitObserver, AssistantVoiceSearchService.Observer {
+        implements DestroyObserver, NativeInitObserver, AssistantVoiceSearchService.Observer {
     private final Context mContext;
     private final PropertyModel mModel;
     private final ViewGroup mView;
@@ -75,7 +75,7 @@
     }
 
     @Override
-    public void destroy() {
+    public void onDestroy() {
         if (mAssistantVoiceSearchService != null) {
             mAssistantVoiceSearchService.destroy();
             mAssistantVoiceSearchService = null;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/omaha/UpdateInfoBarController.java b/chrome/android/java/src/org/chromium/chrome/browser/omaha/UpdateInfoBarController.java
index dfaaeeb..bfa06a2 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/omaha/UpdateInfoBarController.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/omaha/UpdateInfoBarController.java
@@ -8,7 +8,7 @@
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.app.ChromeActivity;
 import org.chromium.chrome.browser.infobar.InfoBarIdentifier;
-import org.chromium.chrome.browser.lifecycle.Destroyable;
+import org.chromium.chrome.browser.lifecycle.DestroyObserver;
 import org.chromium.chrome.browser.omaha.UpdateStatusProvider.UpdateInteractionSource;
 import org.chromium.chrome.browser.omaha.UpdateStatusProvider.UpdateState;
 import org.chromium.chrome.browser.omaha.UpdateStatusProvider.UpdateStatus;
@@ -17,7 +17,7 @@
 import org.chromium.ui.widget.Toast;
 
 /** Helper class that creates infobars based on {@link UpdateState} changes. */
-public class UpdateInfoBarController implements Destroyable {
+public class UpdateInfoBarController implements DestroyObserver {
     private final Callback<UpdateStatus> mObserver = status -> {
         handleStatusChange(status);
     };
@@ -32,9 +32,9 @@
         return new UpdateInfoBarController(activity);
     }
 
-    // Destroyable implementation.
+    // DestroyObserver implementation.
     @Override
-    public void destroy() {
+    public void onDestroy() {
         UpdateStatusProvider.getInstance().removeObserver(mObserver);
         mActivity.getLifecycleDispatcher().unregister(this);
         mActivity = null;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/omaha/notification/UpdateNotificationControllerImpl.java b/chrome/android/java/src/org/chromium/chrome/browser/omaha/notification/UpdateNotificationControllerImpl.java
index b81eaf5..8deea21 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/omaha/notification/UpdateNotificationControllerImpl.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/omaha/notification/UpdateNotificationControllerImpl.java
@@ -28,7 +28,7 @@
 import org.chromium.chrome.browser.init.ChromeBrowserInitializer;
 import org.chromium.chrome.browser.init.EmptyBrowserParts;
 import org.chromium.chrome.browser.lifecycle.ActivityLifecycleDispatcher;
-import org.chromium.chrome.browser.lifecycle.Destroyable;
+import org.chromium.chrome.browser.lifecycle.DestroyObserver;
 import org.chromium.chrome.browser.notifications.NotificationConstants;
 import org.chromium.chrome.browser.notifications.NotificationUmaTracker;
 import org.chromium.chrome.browser.notifications.NotificationWrapperBuilderFactory;
@@ -48,7 +48,8 @@
  * is available. It listens to {@link UpdateStatusProvider}, and handle the intent to start update
  * flow.
  */
-public class UpdateNotificationControllerImpl implements UpdateNotificationController, Destroyable {
+public class UpdateNotificationControllerImpl
+        implements UpdateNotificationController, DestroyObserver {
     private static final String TAG = "UpdateNotif";
     private static final String INLINE_UPDATE_NOTIFICATION_RECEIVED_EXTRA =
             "org.chromium.chrome.browser.omaha.inline_update_notification_received_extra";
@@ -88,9 +89,9 @@
         processUpdateStatus();
     }
 
-    // Destroyable implementation.
+    // DestroyObserver implementation.
     @Override
-    public void destroy() {
+    public void onDestroy() {
         UpdateStatusProvider.getInstance().removeObserver(mObserver);
         mActivityLifecycle.unregister(this);
         mActivityLifecycle = null;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/omaha/notification/UpdateNotificationServiceBridge.java b/chrome/android/java/src/org/chromium/chrome/browser/omaha/notification/UpdateNotificationServiceBridge.java
index feeafa4..d89304f 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/omaha/notification/UpdateNotificationServiceBridge.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/omaha/notification/UpdateNotificationServiceBridge.java
@@ -21,7 +21,7 @@
 import org.chromium.base.annotations.NativeMethods;
 import org.chromium.chrome.browser.flags.ChromeFeatureList;
 import org.chromium.chrome.browser.lifecycle.ActivityLifecycleDispatcher;
-import org.chromium.chrome.browser.lifecycle.Destroyable;
+import org.chromium.chrome.browser.lifecycle.DestroyObserver;
 import org.chromium.chrome.browser.omaha.UpdateStatusProvider;
 import org.chromium.chrome.browser.omaha.UpdateStatusProvider.UpdateState;
 
@@ -31,7 +31,8 @@
  * backend notification scheduling system.
  */
 @JNINamespace("updates")
-public class UpdateNotificationServiceBridge implements UpdateNotificationController, Destroyable {
+public class UpdateNotificationServiceBridge
+        implements UpdateNotificationController, DestroyObserver {
     private final Callback<UpdateStatusProvider.UpdateStatus> mObserver = status -> {
         mUpdateStatus = status;
         processUpdateStatus();
@@ -56,9 +57,9 @@
         processUpdateStatus();
     }
 
-    // Destroyable implementation.
+    // DestroyObserver implementation.
     @Override
-    public void destroy() {
+    public void onDestroy() {
         UpdateStatusProvider.getInstance().removeObserver(mObserver);
         mActivityLifecycle.unregister(this);
         mActivityLifecycle = null;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/LocationBar.java b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/LocationBar.java
index b46a63d..1bd6f3c 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/LocationBar.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/LocationBar.java
@@ -9,14 +9,13 @@
 
 import androidx.annotation.Nullable;
 
-import org.chromium.chrome.browser.lifecycle.Destroyable;
 import org.chromium.chrome.browser.omnibox.voice.VoiceRecognitionHandler;
 import org.chromium.chrome.browser.tab.Tab;
 
 /**
  * Container that holds the {@link UrlBar} and SSL state related with the current {@link Tab}.
  */
-public interface LocationBar extends Destroyable {
+public interface LocationBar {
     /** Handle all necessary tasks that can be delayed until initialization completes. */
     default void onDeferredStartup() {}
 
@@ -74,4 +73,7 @@
      */
     @Nullable
     OmniboxStub getOmniboxStub();
+
+    /** Destroys the LocationBar. */
+    void destroy();
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/LocationBarCoordinator.java b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/LocationBarCoordinator.java
index d9c0a437..a485512d 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/LocationBarCoordinator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/LocationBarCoordinator.java
@@ -24,7 +24,6 @@
 import org.chromium.chrome.browser.lens.LensController;
 import org.chromium.chrome.browser.lens.LensFeature;
 import org.chromium.chrome.browser.lifecycle.ActivityLifecycleDispatcher;
-import org.chromium.chrome.browser.lifecycle.Destroyable;
 import org.chromium.chrome.browser.lifecycle.NativeInitObserver;
 import org.chromium.chrome.browser.omnibox.LocationBarMediator.OmniboxUma;
 import org.chromium.chrome.browser.omnibox.LocationBarMediator.SaveOfflineButtonState;
@@ -67,7 +66,10 @@
                                                      OmniboxSuggestionsDropdownEmbedder,
                                                      AutocompleteDelegate {
     /** Identifies coordinators with methods specific to a device type. */
-    public interface SubCoordinator extends Destroyable {}
+    public interface SubCoordinator {
+        /** Destroys SubCoordinator. */
+        void destroy();
+    }
 
     private LocationBarLayout mLocationBarLayout;
     @Nullable
@@ -218,9 +220,6 @@
 
     @Override
     public void destroy() {
-        mActivityLifecycleDispatcher.unregister(this);
-        mActivityLifecycleDispatcher = null;
-
         if (mSubCoordinator != null) {
             mSubCoordinator.destroy();
             mSubCoordinator = null;
@@ -263,6 +262,9 @@
 
     @Override
     public void onFinishNativeInitialization() {
+        mActivityLifecycleDispatcher.unregister(this);
+        mActivityLifecycleDispatcher = null;
+
         mTemplateUrlServiceSupplier.set(TemplateUrlServiceFactory.get());
         mLocationBarMediator.onFinishNativeInitialization();
         mAutocompleteCoordinator.onNativeInitialized();
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/payments/handler/PaymentHandlerMediator.java b/chrome/android/java/src/org/chromium/chrome/browser/payments/handler/PaymentHandlerMediator.java
index aca4c4c..bc8f646 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/payments/handler/PaymentHandlerMediator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/payments/handler/PaymentHandlerMediator.java
@@ -12,7 +12,7 @@
 import org.chromium.chrome.browser.app.ChromeActivity;
 import org.chromium.chrome.browser.compositor.bottombar.OverlayPanel.StateChangeReason;
 import org.chromium.chrome.browser.lifecycle.ActivityLifecycleDispatcher;
-import org.chromium.chrome.browser.lifecycle.Destroyable;
+import org.chromium.chrome.browser.lifecycle.DestroyObserver;
 import org.chromium.chrome.browser.payments.ServiceWorkerPaymentAppBridge;
 import org.chromium.chrome.browser.payments.handler.PaymentHandlerCoordinator.PaymentHandlerUiObserver;
 import org.chromium.chrome.browser.payments.handler.toolbar.PaymentHandlerToolbarCoordinator.PaymentHandlerToolbarObserver;
@@ -54,7 +54,7 @@
     // Used to postpone execution of a callback to avoid destroy objects (e.g., WebContents) in
     // their own methods.
     private final Handler mHandler = new Handler();
-    private final Destroyable mActivityDestroyListener;
+    private final DestroyObserver mActivityDestroyListener;
     private final ActivityLifecycleDispatcher mActivityLifecycleDispatcher;
     private final View mTabView;
     private final BottomSheetController mBottomSheetController;
@@ -109,9 +109,9 @@
         mModel.set(PaymentHandlerProperties.CONTENT_VISIBLE_HEIGHT_PX, contentVisibleHeight());
 
         mActivityLifecycleDispatcher = activityLifeCycleDispatcher;
-        mActivityDestroyListener = new Destroyable() {
+        mActivityDestroyListener = new DestroyObserver() {
             @Override
-            public void destroy() {
+            public void onDestroy() {
                 mCloseReason = CloseReason.ACTIVITY_DIED;
                 mHandler.post(mHider);
             }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/searchwidget/SearchActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/searchwidget/SearchActivity.java
index ea860d1d..19f5f67d 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/searchwidget/SearchActivity.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/searchwidget/SearchActivity.java
@@ -359,6 +359,8 @@
         if (mTab != null && mTab.isInitialized()) mTab.destroy();
         if (mLocationBarCoordinator != null && mLocationBarCoordinator.getOmniboxStub() != null) {
             mLocationBarCoordinator.getOmniboxStub().removeUrlFocusChangeListener(this);
+            mLocationBarCoordinator.destroy();
+            mLocationBarCoordinator = null;
         }
         mHandler.removeCallbacksAndMessages(null);
         super.onDestroy();
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/tab/AccessibilityVisibilityHandler.java b/chrome/android/java/src/org/chromium/chrome/browser/tab/AccessibilityVisibilityHandler.java
index aa72dd3..93b5539 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/tab/AccessibilityVisibilityHandler.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/tab/AccessibilityVisibilityHandler.java
@@ -6,13 +6,13 @@
 
 import org.chromium.chrome.browser.ActivityTabProvider;
 import org.chromium.chrome.browser.lifecycle.ActivityLifecycleDispatcher;
-import org.chromium.chrome.browser.lifecycle.Destroyable;
+import org.chromium.chrome.browser.lifecycle.DestroyObserver;
 import org.chromium.chrome.browser.ui.TabObscuringHandler;
 
 /**
  * Handles the visibility update of the activity tab.
  */
-public class AccessibilityVisibilityHandler implements Destroyable {
+public class AccessibilityVisibilityHandler implements DestroyObserver {
     private final ActivityTabProvider.ActivityTabTabObserver mActivityTabObserver;
     private TabImpl mTab;
 
@@ -37,10 +37,10 @@
         lifecycleDispatcher.register(this);
     }
 
-    // Destroyable
+    // DestroyObserver
 
     @Override
-    public void destroy() {
+    public void onDestroy() {
         mActivityTabObserver.destroy();
         mTab = null;
     }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/tab/AutofillSessionLifetimeController.java b/chrome/android/java/src/org/chromium/chrome/browser/tab/AutofillSessionLifetimeController.java
index b17080a..5610ab2 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/tab/AutofillSessionLifetimeController.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/tab/AutofillSessionLifetimeController.java
@@ -11,7 +11,7 @@
 import org.chromium.base.compat.ApiHelperForO;
 import org.chromium.chrome.browser.ActivityTabProvider;
 import org.chromium.chrome.browser.lifecycle.ActivityLifecycleDispatcher;
-import org.chromium.chrome.browser.lifecycle.Destroyable;
+import org.chromium.chrome.browser.lifecycle.DestroyObserver;
 import org.chromium.content_public.browser.NavigationHandle;
 
 /**
@@ -40,7 +40,7 @@
  *    content views become invisible, we have to use onDidStartNavigation rather than one of the
  *    later events.
  */
-public class AutofillSessionLifetimeController implements Destroyable {
+public class AutofillSessionLifetimeController implements DestroyObserver {
     private Activity mActivity;
     private final ActivityTabProvider.ActivityTabTabObserver mActivityTabObserver;
 
@@ -73,9 +73,9 @@
         lifecycleDispatcher.register(this);
     }
 
-    // Destroyable
+    // DestroyObserver
     @Override
-    public void destroy() {
+    public void onDestroy() {
         mActivityTabObserver.destroy();
         mActivity = null;
     }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/tabbed_mode/TabbedRootUiCoordinator.java b/chrome/android/java/src/org/chromium/chrome/browser/tabbed_mode/TabbedRootUiCoordinator.java
index 4e69b683..369d458d 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/tabbed_mode/TabbedRootUiCoordinator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/tabbed_mode/TabbedRootUiCoordinator.java
@@ -166,7 +166,7 @@
     }
 
     @Override
-    public void destroy() {
+    public void onDestroy() {
         if (mSystemUiCoordinator != null) mSystemUiCoordinator.destroy();
         if (mEmptyBackgroundViewWrapper != null) mEmptyBackgroundViewWrapper.destroy();
 
@@ -226,7 +226,7 @@
             mMerchantTrustSignalsCoordinator = null;
         }
 
-        super.destroy();
+        super.onDestroy();
     }
 
     @Override
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/tasks/JourneyManager.java b/chrome/android/java/src/org/chromium/chrome/browser/tasks/JourneyManager.java
index 05eec56..3a78f79 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/tasks/JourneyManager.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/tasks/JourneyManager.java
@@ -17,7 +17,7 @@
 import org.chromium.chrome.browser.compositor.layouts.EmptyOverviewModeObserver;
 import org.chromium.chrome.browser.compositor.layouts.OverviewModeBehavior;
 import org.chromium.chrome.browser.lifecycle.ActivityLifecycleDispatcher;
-import org.chromium.chrome.browser.lifecycle.Destroyable;
+import org.chromium.chrome.browser.lifecycle.DestroyObserver;
 import org.chromium.chrome.browser.tab.Tab;
 import org.chromium.chrome.browser.tab.TabHidingType;
 import org.chromium.chrome.browser.tab.TabObserver;
@@ -38,7 +38,7 @@
 /**
  * Manages Journey related signals, specifically those related to tab engagement.
  */
-public class JourneyManager implements Destroyable {
+public class JourneyManager implements DestroyObserver {
     @VisibleForTesting
     static final String PREFS_FILE = "last_engagement_for_tab_id_pref";
 
@@ -174,7 +174,7 @@
     }
 
     @Override
-    public void destroy() {
+    public void onDestroy() {
         mTabModelSelectorTabObserver.destroy();
         mTabModelSelectorTabModelObserver.destroy();
         mOverviewModeBehavior.removeOverviewModeObserver(mOverviewModeObserver);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/ToolbarManager.java b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/ToolbarManager.java
index 204f00629..207f980 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/ToolbarManager.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/ToolbarManager.java
@@ -1312,6 +1312,11 @@
             mBottomControlsCoordinatorSupplier = null;
         }
 
+        if (mLocationBar != null) {
+            mLocationBar.destroy();
+            mLocationBar = null;
+        }
+
         mToolbar.removeUrlExpansionObserver(mStatusBarColorController);
         mToolbar.destroy();
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/top/TopToolbarCoordinator.java b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/top/TopToolbarCoordinator.java
index 0be8bdc..91b5dcee 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/top/TopToolbarCoordinator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/top/TopToolbarCoordinator.java
@@ -246,7 +246,8 @@
             mOverlayCoordinator = new TopToolbarOverlayCoordinator(mToolbarLayout.getContext(),
                     layoutManager, mControlContainer::getProgressBarDrawingInfo, tabSupplier,
                     browserControlsStateProvider, mResourceManagerSupplier, topUiThemeColorProvider,
-                    LayoutType.BROWSING | LayoutType.SIMPLE_ANIMATION, false);
+                    LayoutType.BROWSING | LayoutType.SIMPLE_ANIMATION | LayoutType.TAB_SWITCHER,
+                    false);
             layoutManager.addSceneOverlay(mOverlayCoordinator);
             mToolbarLayout.setOverlayCoordinator(mOverlayCoordinator);
         }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ui/BottomContainer.java b/chrome/android/java/src/org/chromium/chrome/browser/ui/BottomContainer.java
index 5aa5a79..0f86c2c 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ui/BottomContainer.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ui/BottomContainer.java
@@ -10,7 +10,7 @@
 
 import org.chromium.base.Callback;
 import org.chromium.chrome.browser.browser_controls.BrowserControlsStateProvider;
-import org.chromium.chrome.browser.lifecycle.Destroyable;
+import org.chromium.chrome.browser.lifecycle.DestroyObserver;
 import org.chromium.ui.base.ApplicationViewportInsetSupplier;
 
 /**
@@ -18,7 +18,7 @@
  * bottom controls' offset changes.
  */
 public class BottomContainer
-        extends FrameLayout implements Destroyable, BrowserControlsStateProvider.Observer {
+        extends FrameLayout implements DestroyObserver, BrowserControlsStateProvider.Observer {
     /** An observer of the viewport insets to change this container's position. */
     private final Callback<Integer> mViewportInsetObserver;
 
@@ -78,7 +78,7 @@
     }
 
     @Override
-    public void destroy() {
+    public void onDestroy() {
         mBrowserControlsStateProvider.removeObserver(this);
         mViewportInsetSupplier.removeObserver(mViewportInsetObserver);
     }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ui/BottomSheetManager.java b/chrome/android/java/src/org/chromium/chrome/browser/ui/BottomSheetManager.java
index 7255e297..e521f2e 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ui/BottomSheetManager.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ui/BottomSheetManager.java
@@ -17,7 +17,7 @@
 import org.chromium.chrome.browser.browser_controls.BrowserControlsVisibilityManager;
 import org.chromium.chrome.browser.compositor.bottombar.OverlayPanel;
 import org.chromium.chrome.browser.compositor.bottombar.OverlayPanelManager;
-import org.chromium.chrome.browser.lifecycle.Destroyable;
+import org.chromium.chrome.browser.lifecycle.DestroyObserver;
 import org.chromium.chrome.browser.tab.EmptyTabObserver;
 import org.chromium.chrome.browser.tab.Tab;
 import org.chromium.chrome.browser.tab.TabObserver;
@@ -43,7 +43,7 @@
  * A class that manages activity-specific interactions with the BottomSheet component that it
  * otherwise shouldn't know about.
  */
-class BottomSheetManager extends EmptyBottomSheetObserver implements Destroyable {
+class BottomSheetManager extends EmptyBottomSheetObserver implements DestroyObserver {
     /** A means of accessing the focus state of the omibox. */
     private final ObservableSupplier<Boolean> mOmniboxFocusStateSupplier;
 
@@ -395,7 +395,7 @@
     }
 
     @Override
-    public void destroy() {
+    public void onDestroy() {
         mCallbackController.destroy();
         if (mLastActivityTab != null) mLastActivityTab.removeObserver(mTabObserver);
         mTabProvider.removeObserver(mActivityTabObserver);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ui/RootUiCoordinator.java b/chrome/android/java/src/org/chromium/chrome/browser/ui/RootUiCoordinator.java
index 76a7e6a..ec861ff1 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ui/RootUiCoordinator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ui/RootUiCoordinator.java
@@ -54,7 +54,7 @@
 import org.chromium.chrome.browser.image_descriptions.ImageDescriptionsController;
 import org.chromium.chrome.browser.layouts.LayoutStateProvider;
 import org.chromium.chrome.browser.layouts.LayoutType;
-import org.chromium.chrome.browser.lifecycle.Destroyable;
+import org.chromium.chrome.browser.lifecycle.DestroyObserver;
 import org.chromium.chrome.browser.lifecycle.InflationObserver;
 import org.chromium.chrome.browser.lifecycle.NativeInitObserver;
 import org.chromium.chrome.browser.messages.ChromeMessageQueueMediator;
@@ -128,7 +128,7 @@
  * still being discussed See https://crbug.com/931496.
  */
 public class RootUiCoordinator
-        implements Destroyable, InflationObserver, NativeInitObserver,
+        implements DestroyObserver, InflationObserver, NativeInitObserver,
                    MenuOrKeyboardActionController.MenuOrKeyboardActionHandler, AppMenuBlocker {
     protected ChromeActivity mActivity;
     protected @Nullable AppMenuCoordinator mAppMenuCoordinator;
@@ -294,7 +294,7 @@
     }
 
     @Override
-    public void destroy() {
+    public void onDestroy() {
         // TODO(meiliang): Understand why we need to set most of the class member instances to null
         //  other than the mActivity. If the nulling calls are not necessary, we can remove them.
         mCallbackController.destroy();
@@ -356,7 +356,7 @@
             mActivity.getModalDialogManager().removeObserver(mModalDialogManagerObserver);
         }
 
-        if (mBottomSheetManager != null) mBottomSheetManager.destroy();
+        if (mBottomSheetManager != null) mBottomSheetManager.onDestroy();
         if (mBottomSheetController != null) {
             if (mContextualSearchSuppressor != null) {
                 mBottomSheetController.removeObserver(mContextualSearchSuppressor);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ui/system/StatusBarColorController.java b/chrome/android/java/src/org/chromium/chrome/browser/ui/system/StatusBarColorController.java
index ae4bc324..210c7d8e 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ui/system/StatusBarColorController.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ui/system/StatusBarColorController.java
@@ -21,7 +21,7 @@
 import org.chromium.chrome.browser.compositor.layouts.EmptyOverviewModeObserver;
 import org.chromium.chrome.browser.compositor.layouts.OverviewModeBehavior;
 import org.chromium.chrome.browser.lifecycle.ActivityLifecycleDispatcher;
-import org.chromium.chrome.browser.lifecycle.Destroyable;
+import org.chromium.chrome.browser.lifecycle.DestroyObserver;
 import org.chromium.chrome.browser.ntp.NewTabPage;
 import org.chromium.chrome.browser.status_indicator.StatusIndicatorCoordinator;
 import org.chromium.chrome.browser.tab.Tab;
@@ -41,7 +41,7 @@
  * Maintains the status bar color for a {@link Window}.
  */
 public class StatusBarColorController
-        implements Destroyable, TopToolbarCoordinator.UrlExpansionObserver,
+        implements DestroyObserver, TopToolbarCoordinator.UrlExpansionObserver,
                    StatusIndicatorCoordinator.StatusIndicatorObserver {
     public static final @ColorInt int UNDEFINED_STATUS_BAR_COLOR = Color.TRANSPARENT;
     public static final @ColorInt int DEFAULT_STATUS_BAR_COLOR = Color.argb(0x01, 0, 0, 0);
@@ -210,9 +210,9 @@
         mTopUiThemeColor = topUiThemeColorProvider;
     }
 
-    // Destroyable implementation.
+    // DestroyObserver implementation.
     @Override
-    public void destroy() {
+    public void onDestroy() {
         mStatusBarColorTabObserver.destroy();
         if (mOverviewModeBehavior != null) {
             mOverviewModeBehavior.removeOverviewModeObserver(mOverviewModeObserver);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebApkActivityCoordinator.java b/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebApkActivityCoordinator.java
index 26b68a70..52ebce2 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebApkActivityCoordinator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebApkActivityCoordinator.java
@@ -11,7 +11,7 @@
 import org.chromium.chrome.browser.browserservices.ui.view.DisclosureInfobar;
 import org.chromium.chrome.browser.dependency_injection.ActivityScope;
 import org.chromium.chrome.browser.lifecycle.ActivityLifecycleDispatcher;
-import org.chromium.chrome.browser.lifecycle.Destroyable;
+import org.chromium.chrome.browser.lifecycle.DestroyObserver;
 
 import javax.inject.Inject;
 
@@ -22,7 +22,7 @@
  * Add methods here if other components need to communicate with the WebAPK activity component.
  */
 @ActivityScope
-public class WebApkActivityCoordinator implements Destroyable {
+public class WebApkActivityCoordinator implements DestroyObserver {
     private final BrowserServicesIntentDataProvider mIntentDataProvider;
     private final Lazy<WebApkUpdateManager> mWebApkUpdateManager;
 
@@ -58,7 +58,7 @@
     }
 
     @Override
-    public void destroy() {
+    public void onDestroy() {
         // The common case is to be connected to just one WebAPK's services. For the sake of
         // simplicity disconnect from the services of all WebAPKs.
         ChromeWebApkHost.disconnectFromAllServices(true /* waitForPendingWork */);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebApkUpdateManager.java b/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebApkUpdateManager.java
index 8bc56b2..cd6d7765 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebApkUpdateManager.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebApkUpdateManager.java
@@ -25,7 +25,7 @@
 import org.chromium.chrome.browser.dependency_injection.ActivityScope;
 import org.chromium.chrome.browser.flags.ChromeSwitches;
 import org.chromium.chrome.browser.lifecycle.ActivityLifecycleDispatcher;
-import org.chromium.chrome.browser.lifecycle.Destroyable;
+import org.chromium.chrome.browser.lifecycle.DestroyObserver;
 import org.chromium.chrome.browser.metrics.WebApkUma;
 import org.chromium.chrome.browser.metrics.WebApkUma.UpdateRequestQueued;
 import org.chromium.chrome.browser.tab.Tab;
@@ -47,7 +47,7 @@
  * an update request to the WebAPK Server when an update is needed.
  */
 @ActivityScope
-public class WebApkUpdateManager implements WebApkUpdateDataFetcher.Observer, Destroyable {
+public class WebApkUpdateManager implements WebApkUpdateDataFetcher.Observer, DestroyObserver {
     private static final String TAG = "WebApkUpdateManager";
 
     // Maximum wait time for WebAPK update to be scheduled.
@@ -107,7 +107,7 @@
     }
 
     @Override
-    public void destroy() {
+    public void onDestroy() {
         destroyFetcher();
         if (mUpdateFailureHandler != null) {
             mUpdateFailureHandler.removeCallbacksAndMessages(null);
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/firstrun/FirstRunIntegrationTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/firstrun/FirstRunIntegrationTest.java
index e81be07c..f52550f 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/firstrun/FirstRunIntegrationTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/firstrun/FirstRunIntegrationTest.java
@@ -55,13 +55,13 @@
 import org.chromium.chrome.browser.customtabs.CustomTabsTestUtils;
 import org.chromium.chrome.browser.document.ChromeLauncherActivity;
 import org.chromium.chrome.browser.firstrun.FirstRunActivityTestObserver.ScopedObserverData;
+import org.chromium.chrome.browser.locale.DefaultSearchEngineDialogHelperUtils;
 import org.chromium.chrome.browser.locale.LocaleManager;
+import org.chromium.chrome.browser.locale.LocaleManager.SearchEnginePromoType;
 import org.chromium.chrome.browser.policy.EnterpriseInfo;
 import org.chromium.chrome.browser.preferences.ChromePreferenceKeys;
 import org.chromium.chrome.browser.preferences.SharedPreferencesManager;
 import org.chromium.chrome.browser.privacy.settings.PrivacyPreferencesManagerImpl;
-import org.chromium.chrome.browser.search_engines.DefaultSearchEngineDialogHelperUtils;
-import org.chromium.chrome.browser.search_engines.SearchEnginePromoType;
 import org.chromium.chrome.browser.search_engines.TemplateUrlServiceFactory;
 import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
 import org.chromium.chrome.test.MultiActivityTestRule;
@@ -241,7 +241,7 @@
         }
 
         // Select a default search engine.
-        if (searchPromoType == SearchEnginePromoType.DONT_SHOW) {
+        if (searchPromoType == LocaleManager.SearchEnginePromoType.DONT_SHOW) {
             Assert.assertFalse("Search engine page was shown.",
                     freProperties.getBoolean(FirstRunActivityBase.SHOW_SEARCH_ENGINE_PAGE));
         } else {
@@ -354,13 +354,13 @@
     @Test
     @MediumTest
     public void testDefaultSearchEngine_DontShow() throws Exception {
-        runSearchEnginePromptTest(SearchEnginePromoType.DONT_SHOW);
+        runSearchEnginePromptTest(LocaleManager.SearchEnginePromoType.DONT_SHOW);
     }
 
     @Test
     @MediumTest
     public void testDefaultSearchEngine_ShowExisting() throws Exception {
-        runSearchEnginePromptTest(SearchEnginePromoType.SHOW_EXISTING);
+        runSearchEnginePromptTest(LocaleManager.SearchEnginePromoType.SHOW_EXISTING);
     }
 
     @Test
@@ -368,7 +368,7 @@
     public void testDefaultSearchEngine_WithCctPolicy() throws Exception {
         skipTosDialogViaPolicy();
 
-        runSearchEnginePromptTest(SearchEnginePromoType.SHOW_EXISTING);
+        runSearchEnginePromptTest(LocaleManager.SearchEnginePromoType.SHOW_EXISTING);
     }
 
     private void runSearchEnginePromptTest(@SearchEnginePromoType final int searchPromoType)
@@ -558,7 +558,7 @@
         launchViewIntent(FOO_URL);
         FirstRunActivity secondFreActivity = waitForDifferentFirstRunActivity(firstFreActivity);
 
-        clickThroughFirstRun(secondFreActivity, SearchEnginePromoType.DONT_SHOW);
+        clickThroughFirstRun(secondFreActivity, LocaleManager.SearchEnginePromoType.DONT_SHOW);
         verifyUrlEquals(FOO_URL, waitAndGetUriFromChromeActivity(ChromeTabbedActivity.class));
     }
 
@@ -571,7 +571,7 @@
         launchCustomTabs(FOO_URL);
         FirstRunActivity secondFreActivity = waitForDifferentFirstRunActivity(firstFreActivity);
 
-        clickThroughFirstRun(secondFreActivity, SearchEnginePromoType.DONT_SHOW);
+        clickThroughFirstRun(secondFreActivity, LocaleManager.SearchEnginePromoType.DONT_SHOW);
         verifyUrlEquals(FOO_URL, waitAndGetUriFromChromeActivity(CustomTabActivity.class));
     }
 
@@ -584,7 +584,7 @@
         launchViewIntent(FOO_URL);
         FirstRunActivity secondFreActivity = waitForDifferentFirstRunActivity(firstFreActivity);
 
-        clickThroughFirstRun(secondFreActivity, SearchEnginePromoType.DONT_SHOW);
+        clickThroughFirstRun(secondFreActivity, LocaleManager.SearchEnginePromoType.DONT_SHOW);
         verifyUrlEquals(FOO_URL, waitAndGetUriFromChromeActivity(ChromeTabbedActivity.class));
     }
 
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/search_engines/DefaultSearchEngineDialogHelperTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/locale/DefaultSearchEngineDialogHelperTest.java
similarity index 86%
rename from chrome/android/javatests/src/org/chromium/chrome/browser/search_engines/DefaultSearchEngineDialogHelperTest.java
rename to chrome/android/javatests/src/org/chromium/chrome/browser/locale/DefaultSearchEngineDialogHelperTest.java
index 43a128c570..20a88d79 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/search_engines/DefaultSearchEngineDialogHelperTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/locale/DefaultSearchEngineDialogHelperTest.java
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-package org.chromium.chrome.browser.search_engines;
+package org.chromium.chrome.browser.locale;
 
 import android.content.Context;
 import android.support.test.InstrumentationRegistry;
@@ -18,6 +18,7 @@
 import org.junit.runner.RunWith;
 
 import org.chromium.base.test.UiThreadTest;
+import org.chromium.chrome.browser.locale.LocaleManager.SearchEnginePromoType;
 import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
 import org.chromium.components.browser_ui.widget.RadioButtonLayout;
 import org.chromium.components.search_engines.TemplateUrl;
@@ -32,17 +33,20 @@
  */
 @RunWith(ChromeJUnit4ClassRunner.class)
 public class DefaultSearchEngineDialogHelperTest {
-    private class TestDelegate implements DefaultSearchEngineDialogHelper.Delegate {
+    private class TestDelegate extends DefaultSearchEngineDialogHelper.HelperDelegate {
+        public TestDelegate(int dialogType) {
+            super(dialogType);
+        }
+
         public String chosenKeyword;
 
         @Override
-        public List<TemplateUrl> getSearchEnginesForPromoDialog(@SearchEnginePromoType int type) {
+        protected List<TemplateUrl> getSearchEngines() {
             return mTemplateUrls;
         }
 
         @Override
-        public void onUserSearchEngineChoice(
-                @SearchEnginePromoType int type, List<String> keywords, String keyword) {
+        protected void onUserSeachEngineChoice(List<String> keywords, String keyword) {
             chosenKeyword = keyword;
         }
     }
@@ -76,12 +80,17 @@
     }
 
     private class TestDialogHelper extends DefaultSearchEngineDialogHelper {
-        public final TestDelegate delegate;
+        public TestDelegate delegate;
 
-        public TestDialogHelper(@SearchEnginePromoType int dialogType, TestDelegate delegate,
-                RadioButtonLayout controls, Button confirmButton, Runnable finishRunnable) {
-            super(dialogType, delegate, controls, confirmButton, finishRunnable);
-            this.delegate = delegate;
+        public TestDialogHelper(@SearchEnginePromoType int dialogType, RadioButtonLayout controls,
+                Button confirmButton, Runnable finishRunnable) {
+            super(dialogType, controls, confirmButton, finishRunnable);
+        }
+
+        @Override
+        protected HelperDelegate createDelegate(int dialogType) {
+            delegate = new TestDelegate(mDialogType);
+            return delegate;
         }
     }
 
@@ -96,7 +105,6 @@
 
     private final DismissRunnable mDismissRunnable = new DismissRunnable();
     private final List<TemplateUrl> mTemplateUrls = new ArrayList<>();
-    private final TestDelegate mTestDelegate = new TestDelegate();
 
     private Context mContext;
     private @SearchEnginePromoType int mDialogType;
@@ -116,12 +124,12 @@
     @SmallTest
     @UiThreadTest
     public void testInitialState() {
-        mDialogType = SearchEnginePromoType.SHOW_EXISTING;
+        mDialogType = LocaleManager.SearchEnginePromoType.SHOW_EXISTING;
 
         RadioButtonLayout radioLayout = new RadioButtonLayout(mContext);
         Button okButton = new Button(mContext);
-        TestDialogHelper helper = new TestDialogHelper(
-                mDialogType, mTestDelegate, radioLayout, okButton, mDismissRunnable);
+        TestDialogHelper helper =
+                new TestDialogHelper(mDialogType, radioLayout, okButton, mDismissRunnable);
 
         // Confirm that no radio buttons are marked as selected.
         for (int i = 0; i < radioLayout.getChildCount(); i++) {
@@ -176,7 +184,7 @@
         final int maxAttempts = 3;
         boolean succeeded = false;
 
-        mDialogType = SearchEnginePromoType.SHOW_EXISTING;
+        mDialogType = LocaleManager.SearchEnginePromoType.SHOW_EXISTING;
 
         // Repeatedly create pairs of helpers and confirm that they are shuffled differently.  If
         // this test repeatedly iterates without succeeding, then something is terribly wrong.
@@ -184,15 +192,13 @@
             RadioButtonLayout firstLayout = new RadioButtonLayout(mContext);
             Button firstButton = new Button(mContext);
             DismissRunnable firstRunnable = new DismissRunnable();
-            new TestDialogHelper(
-                    mDialogType, mTestDelegate, firstLayout, firstButton, firstRunnable);
+            new TestDialogHelper(mDialogType, firstLayout, firstButton, firstRunnable);
             Assert.assertEquals(mTemplateUrls.size(), firstLayout.getChildCount());
 
             RadioButtonLayout secondLayout = new RadioButtonLayout(mContext);
             Button secondButton = new Button(mContext);
             DismissRunnable secondRunnable = new DismissRunnable();
-            new TestDialogHelper(
-                    mDialogType, mTestDelegate, secondLayout, secondButton, secondRunnable);
+            new TestDialogHelper(mDialogType, secondLayout, secondButton, secondRunnable);
             Assert.assertEquals(mTemplateUrls.size(), firstLayout.getChildCount());
 
             boolean listsMatched = true;
@@ -214,12 +220,12 @@
     @SmallTest
     @UiThreadTest
     public void testSelectEngine() {
-        mDialogType = SearchEnginePromoType.SHOW_EXISTING;
+        mDialogType = LocaleManager.SearchEnginePromoType.SHOW_EXISTING;
 
         RadioButtonLayout radioLayout = new RadioButtonLayout(mContext);
         Button okButton = new Button(mContext);
-        TestDialogHelper helper = new TestDialogHelper(
-                mDialogType, mTestDelegate, radioLayout, okButton, mDismissRunnable);
+        TestDialogHelper helper =
+                new TestDialogHelper(mDialogType, radioLayout, okButton, mDismissRunnable);
 
         Assert.assertNull(
                 "Engine selected after construction", helper.getCurrentlySelectedKeyword());
@@ -241,12 +247,12 @@
     @SmallTest
     @UiThreadTest
     public void testFlipFlopSelection() {
-        mDialogType = SearchEnginePromoType.SHOW_EXISTING;
+        mDialogType = LocaleManager.SearchEnginePromoType.SHOW_EXISTING;
 
         RadioButtonLayout radioLayout = new RadioButtonLayout(mContext);
         Button okButton = new Button(mContext);
-        TestDialogHelper helper = new TestDialogHelper(
-                mDialogType, mTestDelegate, radioLayout, okButton, mDismissRunnable);
+        TestDialogHelper helper =
+                new TestDialogHelper(mDialogType, radioLayout, okButton, mDismissRunnable);
 
         Assert.assertNull(
                 "Engine selected after construction", helper.getCurrentlySelectedKeyword());
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/search_engines/DefaultSearchEngineDialogHelperUtils.java b/chrome/android/javatests/src/org/chromium/chrome/browser/locale/DefaultSearchEngineDialogHelperUtils.java
similarity index 95%
rename from chrome/android/javatests/src/org/chromium/chrome/browser/search_engines/DefaultSearchEngineDialogHelperUtils.java
rename to chrome/android/javatests/src/org/chromium/chrome/browser/locale/DefaultSearchEngineDialogHelperUtils.java
index f8b98645..e58c6178 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/search_engines/DefaultSearchEngineDialogHelperUtils.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/locale/DefaultSearchEngineDialogHelperUtils.java
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-package org.chromium.chrome.browser.search_engines;
+package org.chromium.chrome.browser.locale;
 
 import android.view.View;
 import android.view.ViewGroup;
@@ -14,6 +14,7 @@
 import org.chromium.base.test.util.Criteria;
 import org.chromium.base.test.util.CriteriaHelper;
 import org.chromium.chrome.R;
+import org.chromium.chrome.browser.search_engines.TemplateUrlServiceFactory;
 import org.chromium.content_public.browser.UiThreadTaskTraits;
 import org.chromium.content_public.browser.test.util.TestThreadUtils;
 
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/search_engines/DefaultSearchEnginePromoDialogTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/locale/DefaultSearchEnginePromoDialogTest.java
similarity index 81%
rename from chrome/android/javatests/src/org/chromium/chrome/browser/search_engines/DefaultSearchEnginePromoDialogTest.java
rename to chrome/android/javatests/src/org/chromium/chrome/browser/locale/DefaultSearchEnginePromoDialogTest.java
index a266e18..abf0d01 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/search_engines/DefaultSearchEnginePromoDialogTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/locale/DefaultSearchEnginePromoDialogTest.java
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-package org.chromium.chrome.browser.search_engines;
+package org.chromium.chrome.browser.locale;
 
 import android.app.Activity;
 import android.support.test.InstrumentationRegistry;
@@ -22,7 +22,7 @@
 import org.chromium.chrome.browser.ChromeTabbedActivity;
 import org.chromium.chrome.browser.flags.ChromeSwitches;
 import org.chromium.chrome.browser.init.ChromeBrowserInitializer;
-import org.chromium.chrome.browser.locale.LocaleManager;
+import org.chromium.chrome.browser.search_engines.TemplateUrlServiceFactory;
 import org.chromium.chrome.browser.searchwidget.SearchActivity;
 import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
 import org.chromium.chrome.test.util.ActivityTestUtils;
@@ -30,7 +30,6 @@
 import org.chromium.components.search_engines.TemplateUrlService;
 import org.chromium.content_public.browser.test.util.TestThreadUtils;
 
-import java.util.ArrayList;
 import java.util.List;
 import java.util.concurrent.Callable;
 import java.util.concurrent.ExecutionException;
@@ -99,23 +98,15 @@
 
     private DefaultSearchEnginePromoDialog showDialog(final Activity activity)
             throws ExecutionException {
-        DefaultSearchEngineDialogHelper.Delegate delegate =
-                new DefaultSearchEngineDialogHelper.Delegate() {
+        return TestThreadUtils.runOnUiThreadBlocking(
+                new Callable<DefaultSearchEnginePromoDialog>() {
                     @Override
-                    public List<TemplateUrl> getSearchEnginesForPromoDialog(
-                            @SearchEnginePromoType int type) {
-                        return new ArrayList<>();
+                    public DefaultSearchEnginePromoDialog call() {
+                        DefaultSearchEnginePromoDialog dialog = new DefaultSearchEnginePromoDialog(
+                                activity, LocaleManager.SearchEnginePromoType.SHOW_EXISTING, null);
+                        dialog.show();
+                        return dialog;
                     }
-
-                    @Override
-                    public void onUserSearchEngineChoice(@SearchEnginePromoType int type,
-                            List<String> keywords, String keyword) {}
-                };
-        return TestThreadUtils.runOnUiThreadBlocking(() -> {
-            DefaultSearchEnginePromoDialog dialog = new DefaultSearchEnginePromoDialog(
-                    activity, delegate, SearchEnginePromoType.SHOW_EXISTING, null);
-            dialog.show();
-            return dialog;
-        });
+                });
     }
 }
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/locale/LocaleManagerTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/locale/LocaleManagerTest.java
index c7556d034..39f8e20 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/locale/LocaleManagerTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/locale/LocaleManagerTest.java
@@ -19,7 +19,6 @@
 import org.chromium.chrome.browser.AppHooks;
 import org.chromium.chrome.browser.flags.ChromeSwitches;
 import org.chromium.chrome.browser.init.ChromeBrowserInitializer;
-import org.chromium.chrome.browser.search_engines.SearchEnginePromoType;
 import org.chromium.chrome.browser.searchwidget.SearchActivity;
 import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
 import org.chromium.chrome.test.util.ActivityTestUtils;
@@ -56,7 +55,7 @@
             @Override
             public int getSearchEnginePromoShowType() {
                 getShowTypeCallback.notifyCalled();
-                return SearchEnginePromoType.DONT_SHOW;
+                return LocaleManager.SearchEnginePromoType.DONT_SHOW;
             }
         });
 
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/metrics/StartupPermissionsMetricsTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/metrics/StartupPermissionsMetricsTest.java
deleted file mode 100644
index c658e141..0000000
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/metrics/StartupPermissionsMetricsTest.java
+++ /dev/null
@@ -1,115 +0,0 @@
-// Copyright 2021 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-package org.chromium.chrome.browser.metrics;
-
-import static org.mockito.Mockito.doReturn;
-import static org.mockito.Mockito.eq;
-
-import android.Manifest;
-import android.content.Context;
-import android.support.test.InstrumentationRegistry;
-
-import androidx.test.filters.MediumTest;
-
-import org.junit.Assert;
-import org.junit.Before;
-import org.junit.ClassRule;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-
-import org.chromium.base.test.util.Batch;
-import org.chromium.base.test.util.CommandLineFlags;
-import org.chromium.base.test.util.MetricsUtils.HistogramDelta;
-import org.chromium.chrome.browser.flags.ChromeSwitches;
-import org.chromium.chrome.browser.omnibox.voice.VoiceRecognitionHandler;
-import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
-import org.chromium.chrome.test.ChromeTabbedActivityTestRule;
-import org.chromium.chrome.test.batch.BlankCTATabInitialStateRule;
-import org.chromium.content_public.browser.test.util.TestThreadUtils;
-import org.chromium.ui.base.WindowAndroid;
-
-/**
- * Tests for startup timing histograms.
- */
-@RunWith(ChromeJUnit4ClassRunner.class)
-@CommandLineFlags.Add(ChromeSwitches.DISABLE_FIRST_RUN_EXPERIENCE)
-@Batch(Batch.PER_CLASS)
-public class StartupPermissionsMetricsTest {
-    @ClassRule
-    public static ChromeTabbedActivityTestRule sActivityTestRule =
-            new ChromeTabbedActivityTestRule();
-
-    @Rule
-    public BlankCTATabInitialStateRule mInitialStateRule =
-            new BlankCTATabInitialStateRule(sActivityTestRule, false);
-
-    @Mock
-    private WindowAndroid mWindowAndroid;
-
-    private UmaSessionStats mUmaSessionStats;
-
-    @Before
-    public void setUp() {
-        MockitoAnnotations.initMocks(this);
-
-        Context appContext = InstrumentationRegistry.getInstrumentation()
-                                     .getTargetContext()
-                                     .getApplicationContext();
-
-        TestThreadUtils.runOnUiThreadBlocking(
-                () -> { mUmaSessionStats = new UmaSessionStats(appContext); });
-    }
-
-    @Test
-    @MediumTest
-    public void testPermissionsGranted() throws Exception {
-        doReturn(true).when(mWindowAndroid).hasPermission(eq(Manifest.permission.RECORD_AUDIO));
-        doReturn(true)
-                .when(mWindowAndroid)
-                .canRequestPermission(eq(Manifest.permission.RECORD_AUDIO));
-
-        HistogramDelta grantedDelta =
-                new HistogramDelta("VoiceInteraction.AudioPermissionEvent.SessionStart",
-                        VoiceRecognitionHandler.AudioPermissionState.GRANTED);
-        TestThreadUtils.runOnUiThreadBlocking(
-                () -> mUmaSessionStats.startNewSession(null, mWindowAndroid));
-        Assert.assertEquals(1, grantedDelta.getDelta());
-    }
-
-    @Test
-    @MediumTest
-    public void testPermissionsDeniedCanAsk() throws Exception {
-        doReturn(false).when(mWindowAndroid).hasPermission(eq(Manifest.permission.RECORD_AUDIO));
-        doReturn(true)
-                .when(mWindowAndroid)
-                .canRequestPermission(eq(Manifest.permission.RECORD_AUDIO));
-
-        HistogramDelta deniedCanAskDelta =
-                new HistogramDelta("VoiceInteraction.AudioPermissionEvent.SessionStart",
-                        VoiceRecognitionHandler.AudioPermissionState.DENIED_CAN_ASK_AGAIN);
-        TestThreadUtils.runOnUiThreadBlocking(
-                () -> mUmaSessionStats.startNewSession(null, mWindowAndroid));
-        Assert.assertEquals(1, deniedCanAskDelta.getDelta());
-    }
-
-    @Test
-    @MediumTest
-    public void testPermissionsDeniedCannotAsk() throws Exception {
-        doReturn(false).when(mWindowAndroid).hasPermission(eq(Manifest.permission.RECORD_AUDIO));
-        doReturn(false)
-                .when(mWindowAndroid)
-                .canRequestPermission(eq(Manifest.permission.RECORD_AUDIO));
-
-        HistogramDelta deniedCannotAskDelta =
-                new HistogramDelta("VoiceInteraction.AudioPermissionEvent.SessionStart",
-                        VoiceRecognitionHandler.AudioPermissionState.DENIED_CANNOT_ASK_AGAIN);
-        TestThreadUtils.runOnUiThreadBlocking(
-                () -> mUmaSessionStats.startNewSession(null, mWindowAndroid));
-        Assert.assertEquals(1, deniedCannotAskDelta.getDelta());
-    }
-}
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/searchwidget/SearchActivityTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/searchwidget/SearchActivityTest.java
index 31281cc..fd76846 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/searchwidget/SearchActivityTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/searchwidget/SearchActivityTest.java
@@ -53,6 +53,9 @@
 import org.chromium.chrome.browser.document.ChromeLauncherActivity;
 import org.chromium.chrome.browser.flags.ChromeFeatureList;
 import org.chromium.chrome.browser.flags.ChromeSwitches;
+import org.chromium.chrome.browser.locale.DefaultSearchEngineDialogHelperUtils;
+import org.chromium.chrome.browser.locale.DefaultSearchEnginePromoDialog;
+import org.chromium.chrome.browser.locale.DefaultSearchEnginePromoDialog.DefaultSearchEnginePromoDialogObserver;
 import org.chromium.chrome.browser.locale.LocaleManager;
 import org.chromium.chrome.browser.omnibox.LocationBarCoordinator;
 import org.chromium.chrome.browser.omnibox.OmniboxSuggestionType;
@@ -62,10 +65,6 @@
 import org.chromium.chrome.browser.omnibox.suggestions.OmniboxSuggestionsDropdown;
 import org.chromium.chrome.browser.omnibox.suggestions.base.BaseSuggestionView;
 import org.chromium.chrome.browser.omnibox.voice.VoiceRecognitionHandler;
-import org.chromium.chrome.browser.search_engines.DefaultSearchEngineDialogHelperUtils;
-import org.chromium.chrome.browser.search_engines.DefaultSearchEnginePromoDialog;
-import org.chromium.chrome.browser.search_engines.DefaultSearchEnginePromoDialog.DefaultSearchEnginePromoDialogObserver;
-import org.chromium.chrome.browser.search_engines.SearchEnginePromoType;
 import org.chromium.chrome.browser.search_engines.TemplateUrlServiceFactory;
 import org.chromium.chrome.browser.searchwidget.SearchActivity.SearchActivityDelegate;
 import org.chromium.chrome.browser.share.clipboard.ClipboardImageFileProvider;
@@ -192,7 +191,7 @@
 
         mTestDelegate = new TestDelegate();
         SearchActivity.setDelegateForTests(mTestDelegate);
-        DefaultSearchEnginePromoDialog.setObserverForTests2(mTestDelegate);
+        DefaultSearchEnginePromoDialog.setObserverForTests(mTestDelegate);
     }
 
     @After
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/tasks/JourneyManagerTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/tasks/JourneyManagerTest.java
index 9d78e21..1671289 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/tasks/JourneyManagerTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/tasks/JourneyManagerTest.java
@@ -429,7 +429,7 @@
 
     @Test
     public void destroy_unregistersLifecycleObserver() {
-        mJourneyManager.destroy();
+        mJourneyManager.onDestroy();
         verify(mDispatcher).unregister(mJourneyManager);
         verify(mOverviewModeBehavior).removeOverviewModeObserver(mOverviewModeObserver);
     }
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/webapps/WebApkUpdateManagerUnitTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/webapps/WebApkUpdateManagerUnitTest.java
index e39418f..8b4eddf0 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/webapps/WebApkUpdateManagerUnitTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/webapps/WebApkUpdateManagerUnitTest.java
@@ -722,7 +722,7 @@
         updateIfNeeded(WEBAPK_PACKAGE_NAME, updateManager);
         assertTrue(updateManager.updateCheckStarted());
 
-        updateManager.destroy();
+        updateManager.onDestroy();
 
         ShadowLooper.runUiThreadTasksIncludingDelayedTasks();
         assertFalse(updateManager.updateRequested());
diff --git a/chrome/app/chromeos_strings.grdp b/chrome/app/chromeos_strings.grdp
index 0c5dfee..e9d4ebb 100644
--- a/chrome/app/chromeos_strings.grdp
+++ b/chrome/app/chromeos_strings.grdp
@@ -1943,8 +1943,11 @@
   <message name="IDS_SETTINGS_INTERNET_NETWORK_SIM_CARD_LOCKED" desc="Settings > Internet > Network details > Lock/unlock SIM card: Message when SIM card is locked.">
     SIM card is locked
   </message>
-  <message name="IDS_SETTINGS_INTERNET_NETWORK_SIM_LOCK_ENABLE" desc="Settings > Internet > Network details > Lock/unlock SIM card: Label for checkbox to enable SIM card locking.">
-    Enable SIM card locking (require PIN to use mobile data)
+  <message name="IDS_SETTINGS_INTERNET_NETWORK_SIM_LOCK_ENABLE" desc="Settings > Internet > Network details > Lock/unlock SIM card: Label for toggle to enable SIM card locking.">
+   Lock SIM card
+  </message>
+  <message name="IDS_SETTINGS_INTERNET_NETWORK_SIM_LOCK_ENABLE_SUBLABEL" desc="Settings > Internet > Network details > Lock/unlock SIM card: Sublabel for toggle to enable SIM card locking.">
+    PIN is required to use mobile data
   </message>
   <message name="IDS_SETTINGS_INTERNET_NETWORK_SIM_BUTTON_CHANGE" desc="Settings > Internet > Network details > Lock/unlock SIM card: Label for dialog button to change a PIN.">
     Change
diff --git a/chrome/app/chromeos_strings_grdp/IDS_SETTINGS_INTERNET_NETWORK_SIM_LOCK_ENABLE.png.sha1 b/chrome/app/chromeos_strings_grdp/IDS_SETTINGS_INTERNET_NETWORK_SIM_LOCK_ENABLE.png.sha1
new file mode 100644
index 0000000..98918bce
--- /dev/null
+++ b/chrome/app/chromeos_strings_grdp/IDS_SETTINGS_INTERNET_NETWORK_SIM_LOCK_ENABLE.png.sha1
@@ -0,0 +1 @@
+293cdf4a07c520970f784b58a298330aeac1dff1
\ No newline at end of file
diff --git a/chrome/app/chromeos_strings_grdp/IDS_SETTINGS_INTERNET_NETWORK_SIM_LOCK_ENABLE_SUBLABEL.png.sha1 b/chrome/app/chromeos_strings_grdp/IDS_SETTINGS_INTERNET_NETWORK_SIM_LOCK_ENABLE_SUBLABEL.png.sha1
new file mode 100644
index 0000000..98918bce
--- /dev/null
+++ b/chrome/app/chromeos_strings_grdp/IDS_SETTINGS_INTERNET_NETWORK_SIM_LOCK_ENABLE_SUBLABEL.png.sha1
@@ -0,0 +1 @@
+293cdf4a07c520970f784b58a298330aeac1dff1
\ No newline at end of file
diff --git a/chrome/app/generated_resources.grd b/chrome/app/generated_resources.grd
index d0a02e08..0d34805 100644
--- a/chrome/app/generated_resources.grd
+++ b/chrome/app/generated_resources.grd
@@ -6111,7 +6111,7 @@
         When you're ready, find your reading list here
       </message>
       <message name="IDS_READING_LIST_ENTRY_POINT_PROMO" desc="Text shown on promotional UI to encourage users to add a tab to their reading list">
-        Add this page to reading list
+        To add this page to your reading list, click the Bookmark icon
       </message>
       <message name="IDS_REOPEN_TAB_PROMO" desc="Text shown on promotional UI appearing next to the app menu button">
         Reopen a tab if you accidentally closed it
diff --git a/chrome/app/generated_resources_grd/IDS_READING_LIST_ENTRY_POINT_PROMO.png.sha1 b/chrome/app/generated_resources_grd/IDS_READING_LIST_ENTRY_POINT_PROMO.png.sha1
index 0ccba30..8d915d9 100644
--- a/chrome/app/generated_resources_grd/IDS_READING_LIST_ENTRY_POINT_PROMO.png.sha1
+++ b/chrome/app/generated_resources_grd/IDS_READING_LIST_ENTRY_POINT_PROMO.png.sha1
@@ -1 +1 @@
-360426ccb3305f41f4c1966e34f16a737c605abc
\ No newline at end of file
+0b041781b2cd0a901397683645b03d31915fa697
\ No newline at end of file
diff --git a/chrome/app/shared_settings_strings.grdp b/chrome/app/shared_settings_strings.grdp
index 8a5fb699..437e206 100644
--- a/chrome/app/shared_settings_strings.grdp
+++ b/chrome/app/shared_settings_strings.grdp
@@ -16,8 +16,8 @@
   <message name="IDS_SETTINGS_CAPTIONS_PREFERENCES_TITLE" desc="Section title for caption style setting that is supported by some apps and sites">
     Caption preferences
   </message>
-  <message name="IDS_SETTINGS_CAPTIONS_PREFERENCES_SUBTITLE" desc="Section subtitle for Caption preferences. Disclaimer that not all apps and sites support the system caption style. Some apps and sites don't use the web captioning standard and have their own captioning platform.">
-    Customize caption size and style for apps and sites that support this setting
+  <message name="IDS_SETTINGS_CAPTIONS_PREFERENCES_SUBTITLE" desc="Section subtitle for Caption preferences for Live Caption. Some apps and sites also use the system caption style, but others don't use the web captioning standard and have their own captioning platform.">
+    Customize caption size and style for Live Caption. Some apps and sites will also use this setting.
   </message>
   <message name="IDS_SETTINGS_CAPTIONS_TEXT_SIZE" desc="Name of the caption text size preference.">
     Text size
diff --git a/chrome/app/shared_settings_strings_grdp/IDS_SETTINGS_CAPTIONS_PREFERENCES_SUBTITLE.png.sha1 b/chrome/app/shared_settings_strings_grdp/IDS_SETTINGS_CAPTIONS_PREFERENCES_SUBTITLE.png.sha1
index a414430..9dc5f3a 100644
--- a/chrome/app/shared_settings_strings_grdp/IDS_SETTINGS_CAPTIONS_PREFERENCES_SUBTITLE.png.sha1
+++ b/chrome/app/shared_settings_strings_grdp/IDS_SETTINGS_CAPTIONS_PREFERENCES_SUBTITLE.png.sha1
@@ -1 +1 @@
-2fc24641bfdba5e4c51c0bd7e8daefad60578f2f
\ No newline at end of file
+a2b0c7fc20158533ea7f08ad8193bb8ba46cb823
\ No newline at end of file
diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn
index acee68a3..e4cfc24f 100644
--- a/chrome/browser/BUILD.gn
+++ b/chrome/browser/BUILD.gn
@@ -1965,6 +1965,7 @@
     "//chrome/browser/search/task_module:mojo_bindings",
     "//chrome/browser/sharing:buildflags",
     "//chrome/browser/sharing/proto",
+    "//chrome/browser/signin:identity_manager_provider",
     "//chrome/browser/storage_access_api:permissions",
     "//chrome/browser/thumbnail",
     "//chrome/browser/touch_to_fill",
@@ -3610,6 +3611,8 @@
       "enterprise/reporting/prefs.h",
       "enterprise/reporting/profile_report_generator_desktop.cc",
       "enterprise/reporting/profile_report_generator_desktop.h",
+      "enterprise/reporting/real_time_report_generator_desktop.cc",
+      "enterprise/reporting/real_time_report_generator_desktop.h",
       "enterprise/reporting/report_generator_desktop.cc",
       "enterprise/reporting/report_generator_desktop.h",
       "enterprise/reporting/report_scheduler_desktop.cc",
diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc
index 7712ffb..ab1b56b 100644
--- a/chrome/browser/about_flags.cc
+++ b/chrome/browser/about_flags.cc
@@ -2968,10 +2968,6 @@
      flag_descriptions::kNotificationIndicatorName,
      flag_descriptions::kNotificationIndicatorDescription, kOsCrOS,
      FEATURE_VALUE_TYPE(features::kNotificationIndicator)},
-    {"enable-app-list-search-autocomplete",
-     flag_descriptions::kEnableAppListSearchAutocompleteName,
-     flag_descriptions::kEnableAppListSearchAutocompleteDescription, kOsCrOS,
-     FEATURE_VALUE_TYPE(app_list_features::kEnableAppListSearchAutocomplete)},
     {kLacrosSupportInternalName, flag_descriptions::kLacrosSupportName,
      flag_descriptions::kLacrosSupportDescription, kOsCrOS,
      FEATURE_VALUE_TYPE(chromeos::features::kLacrosSupport)},
@@ -4621,10 +4617,6 @@
      flag_descriptions::kSidePanelDescription, kOsDesktop,
      FEATURE_VALUE_TYPE(features::kSidePanel)},
 
-    {"side-panel-prototype", flag_descriptions::kSidePanelPrototypeName,
-     flag_descriptions::kSidePanelPrototypeDescription, kOsDesktop,
-     FEATURE_VALUE_TYPE(features::kSidePanelPrototype)},
-
     {"tab-outlines-in-low-contrast-themes",
      flag_descriptions::kTabOutlinesInLowContrastThemesName,
      flag_descriptions::kTabOutlinesInLowContrastThemesDescription, kOsDesktop,
diff --git a/chrome/browser/android/lifecycle/BUILD.gn b/chrome/browser/android/lifecycle/BUILD.gn
index d5e330b2..7aed32f9 100644
--- a/chrome/browser/android/lifecycle/BUILD.gn
+++ b/chrome/browser/android/lifecycle/BUILD.gn
@@ -10,7 +10,7 @@
     "java/src/org/chromium/chrome/browser/lifecycle/ActivityLifecycleDispatcher.java",
     "java/src/org/chromium/chrome/browser/lifecycle/ActivityResultWithNativeObserver.java",
     "java/src/org/chromium/chrome/browser/lifecycle/ConfigurationChangedObserver.java",
-    "java/src/org/chromium/chrome/browser/lifecycle/Destroyable.java",
+    "java/src/org/chromium/chrome/browser/lifecycle/DestroyObserver.java",
     "java/src/org/chromium/chrome/browser/lifecycle/InflationObserver.java",
     "java/src/org/chromium/chrome/browser/lifecycle/LifecycleObserver.java",
     "java/src/org/chromium/chrome/browser/lifecycle/NativeInitObserver.java",
diff --git a/chrome/browser/android/lifecycle/java/src/org/chromium/chrome/browser/lifecycle/Destroyable.java b/chrome/browser/android/lifecycle/java/src/org/chromium/chrome/browser/lifecycle/DestroyObserver.java
similarity index 72%
rename from chrome/browser/android/lifecycle/java/src/org/chromium/chrome/browser/lifecycle/Destroyable.java
rename to chrome/browser/android/lifecycle/java/src/org/chromium/chrome/browser/lifecycle/DestroyObserver.java
index a33c2b9..be413268 100644
--- a/chrome/browser/android/lifecycle/java/src/org/chromium/chrome/browser/lifecycle/Destroyable.java
+++ b/chrome/browser/android/lifecycle/java/src/org/chromium/chrome/browser/lifecycle/DestroyObserver.java
@@ -8,9 +8,7 @@
  * Implement this interface and register in {@link
  * org.chromium.chrome.browser.init.ActivityLifecycleDispatcher} to receive destroy events.
  */
-public interface Destroyable extends LifecycleObserver {
-    /**
-     * Called when activity is being destroyed.
-     */
-    void destroy();
+public interface DestroyObserver extends LifecycleObserver {
+    /** Called when activity is being destroyed. */
+    void onDestroy();
 }
diff --git a/chrome/browser/app_controller_mac.mm b/chrome/browser/app_controller_mac.mm
index 78ad8e29..c0759429 100644
--- a/chrome/browser/app_controller_mac.mm
+++ b/chrome/browser/app_controller_mac.mm
@@ -116,7 +116,6 @@
 #include "extensions/browser/extension_system.h"
 #include "net/base/filename_util.h"
 #include "net/base/mac/url_conversions.h"
-#include "ui/base/cocoa/focus_window_set.h"
 #include "ui/base/l10n/l10n_util.h"
 #include "ui/base/l10n/l10n_util_mac.h"
 #include "url/gurl.h"
@@ -224,6 +223,81 @@
       NSApplicationPresentationFullScreen;
 }
 
+// Returns the list of gfx::NativeWindows for all browser windows (excluding
+// apps).
+std::set<gfx::NativeWindow> GetBrowserNativeWindows() {
+  std::set<gfx::NativeWindow> result;
+  for (auto* browser : *BrowserList::GetInstance()) {
+    if (!browser)
+      continue;
+    // When focusing Chrome, don't focus any browser windows associated with
+    // an app.
+    // https://crbug.com/960904
+    if (browser->is_type_app())
+      continue;
+    result.insert(browser->window()->GetNativeWindow());
+  }
+  return result;
+}
+
+void FocusWindowSetOnCurrentSpace(const std::set<gfx::NativeWindow>& windows) {
+  // This callback runs before AppKit picks its own window to
+  // deminiaturize, so we get to pick one from the right set. Limit to
+  // the windows on the current workspace. Otherwise we jump spaces
+  // haphazardly.
+  //
+  // Also consider both visible and hidden windows; this call races
+  // with the system unhiding the application. http://crbug.com/368238
+  //
+  // NOTE: If this is called in the
+  // applicationShouldHandleReopen:hasVisibleWindows: hook when
+  // clicking the dock icon, and that caused macOS to begin switch
+  // spaces, isOnActiveSpace gives the answer for the PREVIOUS
+  // space. This means that we actually raise and focus the wrong
+  // space's windows, leaving the new key window off-screen. To detect
+  // this, check if the key window is on the active space prior to
+  // calling.
+  //
+  // Also, if we decide to deminiaturize a window during a space switch,
+  // that can switch spaces and then switch back. Fortunately, this only
+  // happens if, say, space 1 contains an app, space 2 contains a
+  // miniaturized browser. We click the icon, macOS switches to space 1,
+  // we deminiaturize the browser, and that triggers switching back.
+  //
+  // TODO(davidben): To limit those cases, consider preferentially
+  // deminiaturizing a window on the current space.
+  NSWindow* frontmost_window = nil;
+  NSWindow* frontmost_window_all_spaces = nil;
+  NSWindow* frontmost_miniaturized_window = nil;
+  bool all_miniaturized = true;
+  for (NSWindow* win in [[NSApp orderedWindows] reverseObjectEnumerator]) {
+    if (windows.find(win) == windows.end())
+      continue;
+    if ([win isMiniaturized]) {
+      frontmost_miniaturized_window = win;
+    } else if ([win isVisible]) {
+      all_miniaturized = false;
+      frontmost_window_all_spaces = win;
+      if ([win isOnActiveSpace]) {
+        // Raise the old |frontmost_window| (if any). The topmost |win| will be
+        // raised with makeKeyAndOrderFront: below.
+        [frontmost_window orderFront:nil];
+        frontmost_window = win;
+      }
+    }
+  }
+  if (all_miniaturized && frontmost_miniaturized_window) {
+    DCHECK(!frontmost_window);
+    // Note the call to makeKeyAndOrderFront: will deminiaturize the window.
+    frontmost_window = frontmost_miniaturized_window;
+  }
+
+  if (frontmost_window) {
+    [frontmost_window makeKeyAndOrderFront:nil];
+    [NSApp activateIgnoringOtherApps:YES];
+  }
+}
+
 }  // namespace
 
 // Returns the last profile. This is extracted as a standalone function in order
@@ -696,11 +770,9 @@
   // happened during a space change. Now that the change has
   // completed, raise browser windows.
   _reopenTime = base::TimeTicks();
-  std::set<gfx::NativeWindow> browserWindows;
-  for (auto* browser : *BrowserList::GetInstance())
-    browserWindows.insert(browser->window()->GetNativeWindow());
+  std::set<gfx::NativeWindow> browserWindows = GetBrowserNativeWindows();
   if (!browserWindows.empty())
-    ui::FocusWindowSetOnCurrentSpace(browserWindows);
+    FocusWindowSetOnCurrentSpace(browserWindows);
 }
 
 // Called when shutting down or logging out.
@@ -1254,21 +1326,7 @@
   // If there are any, return here. Otherwise, the windows are panels or
   // notifications so we still need to open a new window.
   if (hasVisibleWindows) {
-    std::set<gfx::NativeWindow> browserWindows;
-    for (auto* browser : *BrowserList::GetInstance()) {
-      // When focusing Chrome, don't focus any browser windows associated with
-      // a currently running app shim, so ignore them.
-      if (browser && browser->deprecated_is_app()) {
-        extensions::ExtensionRegistry* registry =
-            extensions::ExtensionRegistry::Get(browser->profile());
-        const extensions::Extension* extension = registry->GetExtensionById(
-            web_app::GetAppIdFromApplicationName(browser->app_name()),
-            extensions::ExtensionRegistry::ENABLED);
-        if (extension && extension->is_hosted_app())
-          continue;
-      }
-      browserWindows.insert(browser->window()->GetNativeWindow());
-    }
+    std::set<gfx::NativeWindow> browserWindows = GetBrowserNativeWindows();
     if (!browserWindows.empty()) {
       NSWindow* keyWindow = [NSApp keyWindow];
       if (keyWindow && ![keyWindow isOnActiveSpace]) {
@@ -1286,7 +1344,7 @@
         // See http://crbug.com/309656.
         _reopenTime = base::TimeTicks::Now();
       } else {
-        ui::FocusWindowSetOnCurrentSpace(browserWindows);
+        FocusWindowSetOnCurrentSpace(browserWindows);
       }
       // Return NO; we've done (or soon will do) the deminiaturize, so
       // AppKit shouldn't do anything.
diff --git a/chrome/browser/apps/platform_apps/api/media_galleries/media_galleries_apitest.cc b/chrome/browser/apps/platform_apps/api/media_galleries/media_galleries_apitest.cc
index 22e387d..2b4df8a 100644
--- a/chrome/browser/apps/platform_apps/api/media_galleries/media_galleries_apitest.cc
+++ b/chrome/browser/apps/platform_apps/api/media_galleries/media_galleries_apitest.cc
@@ -379,8 +379,7 @@
 
   base::ListValue custom_args;
   custom_args.AppendInteger(base::checked_cast<int>(pref_id));
-  custom_args.AppendString(
-      browser()->profile()->GetPath().BaseName().MaybeAsASCII());
+  custom_args.AppendString(browser()->profile()->GetBaseName().MaybeAsASCII());
 
   ASSERT_TRUE(RunMediaGalleriesTestWithArg("tourl", custom_args)) << message_;
 }
diff --git a/chrome/browser/apps/platform_apps/app_shim_quit_interactive_uitest_mac.mm b/chrome/browser/apps/platform_apps/app_shim_quit_interactive_uitest_mac.mm
index c487ead..0b2a3fc 100644
--- a/chrome/browser/apps/platform_apps/app_shim_quit_interactive_uitest_mac.mm
+++ b/chrome/browser/apps/platform_apps/app_shim_quit_interactive_uitest_mac.mm
@@ -69,7 +69,7 @@
         GetExtensionByPath(registry->enabled_extensions(), app_path_)->id();
     mojo::Remote<chrome::mojom::AppShimHost> host;
     auto app_shim_info = chrome::mojom::AppShimInfo::New();
-    app_shim_info->profile_path = profile()->GetPath().BaseName();
+    app_shim_info->profile_path = profile()->GetBaseName();
     app_shim_info->app_id = extension_id_;
     app_shim_info->app_url = GURL("https://example.com");
     app_shim_info->launch_type =
diff --git a/chrome/browser/ash/crosapi/browser_data_migrator.cc b/chrome/browser/ash/crosapi/browser_data_migrator.cc
index a3327e61..443be033 100644
--- a/chrome/browser/ash/crosapi/browser_data_migrator.cc
+++ b/chrome/browser/ash/crosapi/browser_data_migrator.cc
@@ -13,6 +13,7 @@
 #include "base/files/file_util.h"
 #include "base/metrics/histogram_macros.h"
 #include "base/path_service.h"
+#include "base/strings/string_piece.h"
 #include "base/strings/stringprintf.h"
 #include "base/system/sys_info.h"
 #include "base/task/post_task.h"
@@ -20,21 +21,54 @@
 #include "base/threading/thread_restrictions.h"
 #include "chrome/browser/ash/crosapi/browser_util.h"
 #include "chrome/browser/ash/profiles/profile_helper.h"
+#include "chrome/browser/browser_process.h"
 #include "chrome/common/chrome_paths.h"
 #include "components/account_id/account_id.h"
 #include "components/user_manager/user_manager.h"
+#include "components/version_info/version_info.h"
 
 namespace ash {
 namespace {
-// The name of temporary directory that will store copies of files from user
-// data directory. At the end of the migration, it will be moved to the
-// appropriate destination.
+// The name of temporary directory that will store copies of files from the
+// original user data directory. At the end of the migration, it will be moved
+// to the appropriate destination.
 constexpr char kTmpDir[] = "browser_data_migrator";
-// The base names of files and directories directly under the original user data
-// directory that should not be copied. e.g. caches or files only needed by ash.
+// The base names of files and directories directly under the original profile
+// data directory that should not be copied. e.g. caches or files only needed by
+// ash.
 const char* const kNoCopyPaths[] = {kTmpDir, "Downloads", "Cache"};
+// The base names of files and directories directory under the user data
+// directory.
+const char* const kCopyUserDataPaths[] = {"First Run"};
+
+// Lacros' user data is backward compatible up until this version.
+constexpr char kRequiredDataVersion[] = "0";
+
+// Copies `item` to location pointed by `dest`. Returns true on success and
+// false on failure.
+bool CopyTargetItem(const BrowserDataMigrator::TargetItem& item,
+                    const base::FilePath& dest) {
+  if (item.is_directory) {
+    if (base::CopyDirectory(item.path, dest, true /* recursive */))
+      return true;
+  } else {
+    if (base::CopyFile(item.path, dest))
+      return true;
+  }
+
+  PLOG(ERROR) << "Copy failed for " << item.path;
+  return false;
+}
 }  // namespace
 
+BrowserDataMigrator::TargetItem::TargetItem(base::FilePath path,
+                                            ItemType item_type)
+    : path(path), is_directory(item_type == ItemType::kDirectory) {}
+
+bool BrowserDataMigrator::TargetItem::operator==(const TargetItem& rhs) const {
+  return this->path == rhs.path && this->is_directory == rhs.is_directory;
+}
+
 BrowserDataMigrator::TargetInfo::TargetInfo() : total_byte_count(0) {}
 BrowserDataMigrator::TargetInfo::TargetInfo(const TargetInfo&) = default;
 BrowserDataMigrator::TargetInfo::~TargetInfo() = default;
@@ -70,22 +104,32 @@
   std::unique_ptr<BrowserDataMigrator> browser_data_migrator =
       std::make_unique<BrowserDataMigrator>(profile_data_dir);
 
+  // Check if user data directory needs to be wiped for a backward incompatible
+  // update.
+  base::Version data_version = crosapi::browser_util::GetDataVer(
+      g_browser_process->local_state(), user_id_hash);
+  base::Version current_version = version_info::GetVersion();
+  base::Version required_version =
+      base::Version(base::StringPiece(kRequiredDataVersion));
+  bool is_data_wipe_required =
+      IsDataWipeRequired(data_version, current_version, required_version);
+
   if (async) {
     base::ThreadPool::PostTaskAndReplyWithResult(
         FROM_HERE,
         {base::MayBlock(), base::TaskPriority::USER_VISIBLE,
          base::TaskShutdownBehavior::BLOCK_SHUTDOWN},
         base::BindOnce(&BrowserDataMigrator::MigrateInternal,
-                       std::move(browser_data_migrator)),
+                       std::move(browser_data_migrator), is_data_wipe_required),
         base::BindOnce(&BrowserDataMigrator::MigrateInternalFinishedUIThread,
-                       std::move(callback)));
+                       std::move(callback), user_id_hash));
   } else {
     // Temporarily allowing blocking since we have to ensure that the migration
     // happens before profile is created.
     base::ScopedAllowBlocking allow_blocking;
-    // Migrate synchronously on UI thread.
-    bool did_migrate = browser_data_migrator->MigrateInternal();
-    MigrateInternalFinishedUIThread(std::move(callback), did_migrate);
+    MigrationResult result =
+        browser_data_migrator->MigrateInternal(is_data_wipe_required);
+    MigrateInternalFinishedUIThread(std::move(callback), user_id_hash, result);
   }
 }
 
@@ -97,7 +141,7 @@
 
 BrowserDataMigrator::BrowserDataMigrator(const base::FilePath& from)
     : from_dir_(from),
-      to_dir_(from.Append(kLacrosProfileDir)),
+      to_dir_(from.Append(kLacrosDir)),
       tmp_dir_(from.Append(kTmpDir)) {}
 
 BrowserDataMigrator::~BrowserDataMigrator() = default;
@@ -122,14 +166,52 @@
   UMA_HISTOGRAM_MEDIUM_TIMES(kTotalTime, timer->Elapsed());
 }
 
-// TODO(crbug.com/1178702): Once testing phase is over and lacros become the
+bool BrowserDataMigrator::IsDataWipeRequired(
+    base::Version data_version,
+    const base::Version& current_version,
+    const base::Version& required_version) {
+  // `data_version` is invalid if any wipe has not been recorded yet. In
+  // such a case, assume that the last data wipe happened significantly long
+  // time ago.
+  if (!data_version.IsValid()) {
+    data_version = base::Version("0");
+  }
+
+  if (current_version < required_version) {
+    // If `current_version` is smaller than the `required_version`, that means
+    // that the data wipe doesn't need to happen yet.
+    return false;
+  }
+
+  if (data_version >= required_version) {
+    // If `data_version` is greater or equal to `required_version`, this means
+    // data wipe has already happened and that user data is compatible with the
+    // current lacros.
+    return false;
+  }
+
+  return true;
+}
+
+// TODO(crbug.com/1178702): Once testing phase is over and lacros becomes the
 // only web browser, update the underlying logic of migration from copy to move.
 // Note that during testing phase we are copying files and leaving files in
 // original location intact. We will allow these two states to diverge.
-bool BrowserDataMigrator::MigrateInternal() {
+BrowserDataMigrator::MigrationResult BrowserDataMigrator::MigrateInternal(
+    bool is_data_wipe_required) {
+  if (is_data_wipe_required) {
+    if (!base::DeletePathRecursively(to_dir_)) {
+      RecordStatus(FinalStatus::kDataWipeFailed);
+      return {ResultValue::kFailed, ResultValue::kFailed};
+    }
+  }
+
+  ResultValue data_wipe_result =
+      is_data_wipe_required ? ResultValue::kSucceeded : ResultValue::kSkipped;
+
   if (!IsMigrationRequiredOnWorker()) {
     RecordStatus(FinalStatus::kSkipped);
-    return false;
+    return {data_wipe_result, ResultValue::kSkipped};
   }
 
   // Check if tmp directory already exists and delete if it does.
@@ -140,7 +222,7 @@
     if (!base::DeletePathRecursively(tmp_dir_)) {
       PLOG(ERROR) << "Failed to delete tmp dir";
       RecordStatus(FinalStatus::kDeleteTmpDirFailed);
-      return false;
+      return {data_wipe_result, ResultValue::kFailed};
     }
   }
 
@@ -149,7 +231,7 @@
 
   if (!HasEnoughDiskSpace(target_info)) {
     RecordStatus(FinalStatus::kNotEnoughSpace, &target_info);
-    return false;
+    return {data_wipe_result, ResultValue::kFailed};
   }
 
   if (!CopyToTmpDir(target_info)) {
@@ -157,7 +239,7 @@
       base::DeletePathRecursively(tmp_dir_);
     }
     RecordStatus(FinalStatus::kCopyFailed, &target_info);
-    return false;
+    return {data_wipe_result, ResultValue::kFailed};
   }
 
   if (!MoveTmpToTargetDir()) {
@@ -165,22 +247,27 @@
       base::DeletePathRecursively(tmp_dir_);
     }
     RecordStatus(FinalStatus::kMoveFailed, &target_info);
-    return false;
+    return {data_wipe_result, ResultValue::kFailed};
   }
 
-  // TODO(crbug.com/1178702): Add UMA data collection here for success status,
-  // data size and elapsed time.
   LOG(WARNING) << "BrowserDataMigrator::Migrate took "
                << timer.Elapsed().InMilliseconds() << " ms and migrated "
                << target_info.total_byte_count / (1000 * 1000) << " MBs.";
   RecordStatus(FinalStatus::kSuccess, &target_info, &timer);
-  return true;
+  return {data_wipe_result, ResultValue::kSucceeded};
 }
 
 void BrowserDataMigrator::MigrateInternalFinishedUIThread(
     base::OnceClosure callback,
-    bool did_migrate) {
-  if (did_migrate) {
+    const std::string& user_id_hash,
+    MigrationResult result) {
+  if (result.data_wipe == ResultValue::kSucceeded) {
+    crosapi::browser_util::RecordDataVer(g_browser_process->local_state(),
+                                         user_id_hash,
+                                         version_info::GetVersion());
+  }
+
+  if (result.data_migration == ResultValue::kSucceeded) {
     // If we did a migration, then we should set kClearUserDataDir1Pref. Note
     // that if we did the migration, then the new user-data-dir has the ash
     // profile as the main lacros profile.
@@ -217,12 +304,24 @@
 
     const base::FileEnumerator::FileInfo& info = enumerator.GetInfo();
     if (S_ISREG(info.stat().st_mode)) {
-      target_info.file_paths.emplace_back(entry);
+      target_info.profile_data_items.emplace_back(
+          TargetItem{entry, TargetItem::ItemType::kFile});
       target_info.total_byte_count += info.GetSize();
     } else {
       // Treat symlink the same as directory since even if it points to a file,
       // both `ComputeDirectorySize()` and `CopyDirectory()` can be used.
-      target_info.dir_paths.emplace_back(entry);
+      target_info.profile_data_items.emplace_back(
+          TargetItem{entry, TargetItem::ItemType::kDirectory});
+      target_info.total_byte_count += base::ComputeDirectorySize(entry);
+    }
+  }
+
+  // Copy files directly under user data directory.
+  for (auto* copy_path : kCopyUserDataPaths) {
+    base::FilePath entry = from_dir_.DirName().Append(copy_path);
+    if (base::PathExists(entry)) {
+      target_info.user_data_items.emplace_back(
+          TargetItem{entry, TargetItem::ItemType::kFile});
       target_info.total_byte_count += base::ComputeDirectorySize(entry);
     }
   }
@@ -244,7 +343,8 @@
 
 bool BrowserDataMigrator::CopyToTmpDir(const TargetInfo& target_info) const {
   base::File::Error error;
-  if (!base::CreateDirectoryAndGetError(tmp_dir_, &error)) {
+  if (!base::CreateDirectoryAndGetError(tmp_dir_.Append(kLacrosProfilePath),
+                                        &error)) {
     PLOG(ERROR) << "CreateDirectoryFailed " << error;
     // Maps to histogram enum `PlatformFileError`.
     UMA_HISTOGRAM_ENUMERATION(kCreateDirectoryFail, -error,
@@ -252,19 +352,19 @@
     return false;
   }
 
-  for (const auto& target_file : target_info.file_paths) {
-    if (!base::CopyFile(target_file, tmp_dir_.Append(target_file.BaseName()))) {
-      PLOG(ERROR) << "CopyFile failed for " << target_file;
+  for (const auto& target_item : target_info.profile_data_items) {
+    base::FilePath dest =
+        tmp_dir_.Append(kLacrosProfilePath).Append(target_item.path.BaseName());
+
+    if (!CopyTargetItem(target_item, dest))
       return false;
-    }
   }
 
-  for (const auto& target_dir : target_info.dir_paths) {
-    if (!base::CopyDirectory(target_dir, tmp_dir_.Append(target_dir.BaseName()),
-                             true /* recursive */)) {
-      PLOG(ERROR) << "CopyDirectory failed for " << target_dir;
+  for (const auto& target_item : target_info.user_data_items) {
+    base::FilePath dest = tmp_dir_.Append(target_item.path.BaseName());
+
+    if (!CopyTargetItem(target_item, dest))
       return false;
-    }
   }
 
   return true;
diff --git a/chrome/browser/ash/crosapi/browser_data_migrator.h b/chrome/browser/ash/crosapi/browser_data_migrator.h
index f38788f..d5c6b25e 100644
--- a/chrome/browser/ash/crosapi/browser_data_migrator.h
+++ b/chrome/browser/ash/crosapi/browser_data_migrator.h
@@ -11,14 +11,16 @@
 #include "base/files/file_path.h"
 #include "base/gtest_prod_util.h"
 #include "base/timer/elapsed_timer.h"
+#include "base/version.h"
 #include "chromeos/login/auth/user_context.h"
 
 namespace ash {
 
-// The new profile data directory location under the original profile data
-// directory location. More concretely the new location will be
-// `/home/chronos/u-<hash>/lacros/Default`.
-constexpr char kLacrosProfileDir[] = "lacros/Default";
+// The new profile data directory location is
+// '/home/chronos/u-<hash>/lacros/Default'. User data directory for lacros.
+constexpr char kLacrosDir[] = "lacros";
+// Profile data directory for lacros.
+constexpr char kLacrosProfilePath[] = "Default";
 
 // The following are UMA names.
 constexpr char kFinalStatus[] = "Ash.BrowserDataMigrator.FinalStatus";
@@ -32,6 +34,17 @@
 // an instance and calls `MigrateInternal()`.
 class BrowserDataMigrator {
  public:
+  // Used to describe a file/dir that has to be migrated.
+  struct TargetItem {
+    enum class ItemType { kFile, kDirectory };
+    TargetItem(base::FilePath path, ItemType item_type);
+    ~TargetItem() = default;
+    bool operator==(const TargetItem& rhs) const;
+
+    base::FilePath path;
+    bool is_directory;
+  };
+
   // Used to describe what files/dirs have to be migrated to the new location
   // and the total byte size of those files.
   struct TargetInfo {
@@ -39,8 +52,11 @@
     ~TargetInfo();
     TargetInfo(const TargetInfo&);
 
-    std::vector<base::FilePath> file_paths;
-    std::vector<base::FilePath> dir_paths;
+    // Items that have to be copied that are directly under user data directory.
+    std::vector<TargetItem> user_data_items;
+    // Items that have to be copied that are directly under profile data
+    // directory. Profile data directory itself is inside user data directory.
+    std::vector<TargetItem> profile_data_items;
     int64_t total_byte_count;
   };
 
@@ -57,7 +73,22 @@
     kNotEnoughSpace = 4,
     kCopyFailed = 5,
     kMoveFailed = 6,
-    kMaxValue = kMoveFailed
+    kDataWipeFailed = 7,
+    kMaxValue = kDataWipeFailed
+  };
+
+  enum class ResultValue {
+    kSkipped,
+    kSucceeded,
+    kFailed,
+  };
+
+  // Return value of `MigrateInternal()`.
+  struct MigrationResult {
+    // Describes the end result of user data wipe.
+    ResultValue data_wipe;
+    // Describes the end result of data migration.
+    ResultValue data_migration;
   };
 
   // The class is instantiated on UI thread, bound to `MigrateInternal()` and
@@ -82,16 +113,34 @@
   FRIEND_TEST_ALL_PREFIXES(BrowserDataMigratorTest, IsMigrationRequiredOnUI);
   FRIEND_TEST_ALL_PREFIXES(BrowserDataMigratorTest,
                            IsMigrationRequiredOnWorker);
+  FRIEND_TEST_ALL_PREFIXES(BrowserDataMigratorTest, IsDataWipeRequiredInvalid);
+  FRIEND_TEST_ALL_PREFIXES(BrowserDataMigratorTest,
+                           IsDataWipeRequiredFutureVersion);
+  FRIEND_TEST_ALL_PREFIXES(BrowserDataMigratorTest,
+                           IsDataWipeRequiredSameVersion);
+  FRIEND_TEST_ALL_PREFIXES(BrowserDataMigratorTest, IsDataWipeRequired);
+  FRIEND_TEST_ALL_PREFIXES(BrowserDataMigratorTest, IsDataWipeRequired2);
+  FRIEND_TEST_ALL_PREFIXES(BrowserDataMigratorTest, MaybeWipeUserDir);
   FRIEND_TEST_ALL_PREFIXES(BrowserDataMigratorTest, GetTargetInfo);
   FRIEND_TEST_ALL_PREFIXES(BrowserDataMigratorTest, RecordStatus);
   FRIEND_TEST_ALL_PREFIXES(BrowserDataMigratorTest, Migrate);
 
+  // Checks if lacros' data directory needs to be wiped before migration.
+  // `data_version` is the version of last data wipe. `current_version` is the
+  // version of ash-chrome. `required_version` is the version that introduces
+  // some breaking change. `data_version` needs to be greater or equal to
+  // `required_version`. If `required_version` is newer than `current_version`,
+  // data wipe is not required.
+  static bool IsDataWipeRequired(base::Version data_version,
+                                 const base::Version& current_version,
+                                 const base::Version& required_version);
   // Handles the migration on a worker thread. Returns whether a migration
   // occurred.
-  bool MigrateInternal();
+  MigrationResult MigrateInternal(bool is_data_wipe_required);
   // Called when the migration is finished on the UI thread.
   static void MigrateInternalFinishedUIThread(base::OnceClosure callback,
-                                              bool did_migrate);
+                                              const std::string& user_id_hash,
+                                              MigrationResult result);
   // Records to UMA histograms. Note that if `target_info` is nullptr, timer
   // will be ignored.
   static void RecordStatus(const FinalStatus& final_status,
diff --git a/chrome/browser/ash/crosapi/browser_data_migrator_browsertest.cc b/chrome/browser/ash/crosapi/browser_data_migrator_browsertest.cc
index ec61e2db..c5c43c56 100644
--- a/chrome/browser/ash/crosapi/browser_data_migrator_browsertest.cc
+++ b/chrome/browser/ash/crosapi/browser_data_migrator_browsertest.cc
@@ -62,12 +62,12 @@
       g_browser_process->profile_manager()->GetPrimaryUserProfile();
   {
     base::ScopedAllowBlockingForTesting allow_blocking;
-    const base::FilePath new_profile_data_directory =
-        profile->GetPath().Append(kLacrosProfileDir);
+    const base::FilePath new_user_data_directory =
+        profile->GetPath().Append(kLacrosDir);
     // Make sure that lacros directory does not exist before migration.
-    ASSERT_FALSE(base::DirectoryExists(new_profile_data_directory));
+    ASSERT_FALSE(base::DirectoryExists(new_user_data_directory));
     ASSERT_FALSE(base::PathExists(
-        new_profile_data_directory.Append(chrome::kPreferencesFilename)));
+        new_user_data_directory.Append(chrome::kPreferencesFilename)));
   }
 }
 
@@ -76,12 +76,13 @@
       g_browser_process->profile_manager()->GetPrimaryUserProfile();
   {
     base::ScopedAllowBlockingForTesting allow_blocking;
-    const base::FilePath new_profile_data_directory =
-        profile->GetPath().Append(kLacrosProfileDir);
+    const base::FilePath new_user_data_directory =
+        profile->GetPath().Append(kLacrosDir);
     // Check that the new profile data directory is created.
-    ASSERT_TRUE(base::DirectoryExists(new_profile_data_directory));
-    ASSERT_TRUE(base::PathExists(
-        new_profile_data_directory.Append(chrome::kPreferencesFilename)));
+    ASSERT_TRUE(base::DirectoryExists(new_user_data_directory));
+    ASSERT_TRUE(
+        base::PathExists(new_user_data_directory.Append(kLacrosProfilePath)
+                             .Append(chrome::kPreferencesFilename)));
   }
 }
 }  // namespace ash
diff --git a/chrome/browser/ash/crosapi/browser_data_migrator_unittest.cc b/chrome/browser/ash/crosapi/browser_data_migrator_unittest.cc
index b70da0b..6fce8440 100644
--- a/chrome/browser/ash/crosapi/browser_data_migrator_unittest.cc
+++ b/chrome/browser/ash/crosapi/browser_data_migrator_unittest.cc
@@ -4,6 +4,8 @@
 
 #include "chrome/browser/ash/crosapi/browser_data_migrator.h"
 
+#include <algorithm>
+
 #include "ash/constants/ash_features.h"
 #include "base/files/file_util.h"
 #include "base/files/scoped_temp_dir.h"
@@ -22,41 +24,56 @@
 constexpr char kFileData[] = "Hello";
 constexpr int kFileSize = sizeof(kFileData);
 constexpr char kDownloads[] = "Downloads";
+constexpr char kFirstRun[] = "First Run";
 }  // namespace
 
 class BrowserDataMigratorTest : public ::testing::Test {
  public:
   void SetUp() override {
-    // Setup from_dir_ as below.
-    // |- file
-    // |- directory
-    //    |- file
-    //    |- Downloads/file
-    // |- Downloads/file
-    ASSERT_TRUE(from_dir_.CreateUniqueTempDir());
-    ASSERT_TRUE(base::CreateDirectory(from_dir_.GetPath().Append(kDirName)));
+    // Setup `user_data_dir_` as below.
+    // ./                         /* user_data_dir_ */
+    // |- 'First Run'
+    // |- user/                   /* from_dir_ */
+    //     |- file
+    //     |- directory/
+    //         |- file
+    //         |- Downloads/file
+    //     |- Downloads/file
+
+    ASSERT_TRUE(user_data_dir_.CreateUniqueTempDir());
+    from_dir_ = user_data_dir_.GetPath().Append("user");
+
+    ASSERT_TRUE(base::WriteFile(user_data_dir_.GetPath().Append(
+                                    kFirstRun) /* .../'First Run' */,
+                                "", 0) == 0);
     ASSERT_TRUE(base::CreateDirectory(
-        from_dir_.GetPath().Append(kDirName).Append(kDownloads)));
-    ASSERT_TRUE(base::CreateDirectory(from_dir_.GetPath().Append(kDownloads)));
-    ASSERT_TRUE(base::WriteFile(from_dir_.GetPath().Append(kFileName),
-                                kFileData, kFileSize));
+        from_dir_.Append(kDirName) /* .../user/directory/ */));
+    ASSERT_TRUE(base::CreateDirectory(from_dir_.Append(kDirName).Append(
+        kDownloads) /* .../user/directory/Downloads/ */));
+    ASSERT_TRUE(base::CreateDirectory(
+        from_dir_.Append(kDownloads) /* .../user/Downloads/ */));
     ASSERT_TRUE(
-        base::WriteFile(from_dir_.GetPath().Append(kDirName).Append(kFileName),
+        base::WriteFile(from_dir_.Append(kFileName) /* .../user/file/ */,
                         kFileData, kFileSize));
-    ASSERT_TRUE(base::WriteFile(from_dir_.GetPath()
-                                    .Append(kDirName)
-                                    .Append(kDownloads)
-                                    .Append(kFileName),
+    ASSERT_TRUE(base::WriteFile(from_dir_.Append(kDirName).Append(
+                                    kFileName) /* .../user/directory/file/ */,
                                 kFileData, kFileSize));
     ASSERT_TRUE(base::WriteFile(
-        from_dir_.GetPath().Append(kDownloads).Append(kFileName), kFileData,
-        kFileSize));
+        from_dir_.Append(kDirName)
+            .Append(kDownloads)
+            .Append(kFileName) /* .../user/directory/Downloads/file/ */,
+        kFileData, kFileSize));
+    ASSERT_TRUE(
+        base::WriteFile(from_dir_.Append(kDownloads)
+                            .Append(kFileName) /* .../user/Downloads/file/ */,
+                        kFileData, kFileSize));
   }
 
-  void TearDown() override { EXPECT_TRUE(from_dir_.Delete()); }
+  void TearDown() override { EXPECT_TRUE(user_data_dir_.Delete()); }
 
  protected:
-  base::ScopedTempDir from_dir_;
+  base::ScopedTempDir user_data_dir_;
+  base::FilePath from_dir_;
 };
 
 TEST_F(BrowserDataMigratorTest, IsMigrationRequiredOnUI) {
@@ -107,27 +124,72 @@
   }
 }
 
+TEST_F(BrowserDataMigratorTest, IsDataWipeRequiredInvalid) {
+  const base::Version data_version;
+  const base::Version current{"3"};
+  const base::Version required{"2"};
+
+  ASSERT_FALSE(data_version.IsValid());
+  EXPECT_TRUE(
+      BrowserDataMigrator::IsDataWipeRequired(data_version, current, required));
+}
+
+TEST_F(BrowserDataMigratorTest, IsDataWipeRequiredFutureVersion) {
+  const base::Version data_version{"1"};
+  const base::Version current{"2"};
+  const base::Version required{"3"};
+
+  EXPECT_FALSE(
+      BrowserDataMigrator::IsDataWipeRequired(data_version, current, required));
+}
+
+TEST_F(BrowserDataMigratorTest, IsDataWipeRequiredSameVersion) {
+  const base::Version data_version{"3"};
+  const base::Version current{"4"};
+  const base::Version required{"3"};
+
+  EXPECT_FALSE(
+      BrowserDataMigrator::IsDataWipeRequired(data_version, current, required));
+}
+
+TEST_F(BrowserDataMigratorTest, IsDataWipeRequired) {
+  const base::Version data_version{"1"};
+  const base::Version current{"3"};
+  const base::Version required{"2"};
+
+  EXPECT_TRUE(
+      BrowserDataMigrator::IsDataWipeRequired(data_version, current, required));
+}
+
+TEST_F(BrowserDataMigratorTest, IsDataWipeRequired2) {
+  const base::Version data_version{"1"};
+  const base::Version current{"3"};
+  const base::Version required{"3"};
+
+  EXPECT_TRUE(
+      BrowserDataMigrator::IsDataWipeRequired(data_version, current, required));
+}
+
 TEST_F(BrowserDataMigratorTest, IsMigrationRequiredOnWorker) {
-  BrowserDataMigrator browser_data_migrator(from_dir_.GetPath());
+  BrowserDataMigrator browser_data_migrator(from_dir_);
 
   // If `BrowserDataMigrator::to_dir_` does not exist, run migration.
   EXPECT_TRUE(browser_data_migrator.IsMigrationRequiredOnWorker());
 
   // Create `BrowserDataMigrator::to_dir_`.
-  ASSERT_TRUE(
-      base::CreateDirectory(from_dir_.GetPath().Append(kLacrosProfileDir)));
+  ASSERT_TRUE(base::CreateDirectory(from_dir_.Append(kLacrosDir)));
 
   // If `BrowserDataMigrator::to_dir_` already exists, do not run migration.
   EXPECT_FALSE(browser_data_migrator.IsMigrationRequiredOnWorker());
 
-  ASSERT_TRUE(base::DeletePathRecursively(from_dir_.GetPath()));
+  ASSERT_TRUE(base::DeletePathRecursively(from_dir_));
 
   // If `BrowserDataMigrator::from_dir_` does not exist, do not run migration.
   EXPECT_FALSE(browser_data_migrator.IsMigrationRequiredOnWorker());
 }
 
 TEST_F(BrowserDataMigratorTest, GetTargetInfo) {
-  BrowserDataMigrator browser_data_migrator(from_dir_.GetPath());
+  BrowserDataMigrator browser_data_migrator(from_dir_);
 
   BrowserDataMigrator::TargetInfo target_info =
       browser_data_migrator.GetTargetInfo();
@@ -135,15 +197,36 @@
   EXPECT_EQ(target_info.total_byte_count,
             kFileSize * 3 /* expect three files */);
 
-  const base::FilePath expected_file_path =
-      from_dir_.GetPath().Append(kFileName);
-  const base::FilePath expected_dir_path = from_dir_.GetPath().Append(kDirName);
+  ASSERT_EQ(target_info.user_data_items.size(), 1);
 
-  ASSERT_EQ(target_info.file_paths.size(), 1);
-  EXPECT_EQ(target_info.file_paths[0], expected_file_path);
+  std::vector<BrowserDataMigrator::TargetItem> expected_user_data_items = {
+      BrowserDataMigrator::TargetItem{
+          from_dir_.DirName().Append(kFirstRun),
+          BrowserDataMigrator::TargetItem::ItemType::kFile}};
 
-  ASSERT_EQ(target_info.dir_paths.size(), 1);
-  EXPECT_EQ(target_info.dir_paths[0], expected_dir_path);
+  EXPECT_EQ(target_info.user_data_items[0], expected_user_data_items[0]);
+
+  ASSERT_EQ(target_info.profile_data_items.size(), 2);
+
+  std::vector<BrowserDataMigrator::TargetItem> expected_profile_data_items = {
+      BrowserDataMigrator::TargetItem{
+          from_dir_.Append(kDirName),
+          BrowserDataMigrator::TargetItem::ItemType::kDirectory},
+      BrowserDataMigrator::TargetItem{
+          from_dir_.Append(kFileName),
+          BrowserDataMigrator::TargetItem::ItemType::kFile}};
+
+  std::sort(
+      target_info.profile_data_items.begin(),
+      target_info.profile_data_items.end(),
+      [](BrowserDataMigrator::TargetItem i1,
+         BrowserDataMigrator::TargetItem i2) { return i1.path < i2.path; });
+
+  for (int i = 0; i < target_info.profile_data_items.size(); i++) {
+    SCOPED_TRACE(target_info.profile_data_items[i].path);
+    EXPECT_EQ(target_info.profile_data_items[i],
+              expected_profile_data_items[i]);
+  }
 }
 
 TEST_F(BrowserDataMigratorTest, RecordStatus) {
@@ -167,7 +250,7 @@
     // If `FInalStatus::kSuccess`, the three UMA `kFinalStatus`,
     // `kCopiedDataSize`, `kTotalTime` should be recorded.
     base::HistogramTester histogram_tester;
-    BrowserDataMigrator browser_data_migrator(from_dir_.GetPath());
+    BrowserDataMigrator browser_data_migrator(from_dir_);
 
     BrowserDataMigrator::TargetInfo target_info;
     target_info.total_byte_count = /* 200 MBs */ 200 * 1024 * 1024;
@@ -192,28 +275,37 @@
   base::HistogramTester histogram_tester;
 
   {
-    BrowserDataMigrator browser_data_migrator(from_dir_.GetPath());
+    BrowserDataMigrator browser_data_migrator(from_dir_);
 
-    browser_data_migrator.MigrateInternal();
+    browser_data_migrator.MigrateInternal(false /* is_data_wipe_required */);
 
     // Expected dir structure after migration.
-    //  |- Downloads/file
-    //  |- lacros/Default
-    //      |- file
-    //      |- directory
-    //         |- file
-    //         |- Downloads/file
+    // ./                         /* user_data_dir_ */
+    // |- 'First Run'
+    // |- user/                   /* from_dir_ */
+    //     |- Downloads/file
+    //     |- lacros
+    //         |- 'First Run'
+    //         |- Default/
+    //             |- file
+    //             |- directory
+    //                 |- file
+    //                 |- Downloads/file
 
+    EXPECT_FALSE(base::PathExists(from_dir_.Append(kLacrosDir)
+                                      .Append("Default")
+                                      .Append(kDownloads)
+                                      .Append(kFileName)));
+    const base::FilePath new_user_data_dir = from_dir_.Append(kLacrosDir);
+    EXPECT_TRUE(base::PathExists(new_user_data_dir.Append(kFirstRun)));
+    EXPECT_TRUE(
+        base::PathExists(from_dir_.Append(kDownloads).Append(kFileName)));
     EXPECT_TRUE(base::PathExists(
-        from_dir_.GetPath().Append(kDownloads).Append(kFileName)));
+        new_user_data_dir.Append("Default").Append(kFileName)));
     EXPECT_TRUE(base::PathExists(
-        from_dir_.GetPath().Append(kLacrosProfileDir).Append(kFileName)));
-    EXPECT_TRUE(base::PathExists(from_dir_.GetPath()
-                                     .Append(kLacrosProfileDir)
-                                     .Append(kDirName)
-                                     .Append(kFileName)));
-    EXPECT_TRUE(base::PathExists(from_dir_.GetPath()
-                                     .Append(kLacrosProfileDir)
+        new_user_data_dir.Append("Default").Append(kDirName).Append(
+            kFileName)));
+    EXPECT_TRUE(base::PathExists(new_user_data_dir.Append("Default")
                                      .Append(kDirName)
                                      .Append(kDownloads)
                                      .Append(kFileName)));
diff --git a/chrome/browser/ash/crosapi/browser_util.cc b/chrome/browser/ash/crosapi/browser_util.cc
index 6962663..24d7339 100644
--- a/chrome/browser/ash/crosapi/browser_util.cc
+++ b/chrome/browser/ash/crosapi/browser_util.cc
@@ -24,6 +24,7 @@
 #include "base/strings/string_split.h"
 #include "base/strings/string_util.h"
 #include "base/system/sys_info.h"
+#include "base/version.h"
 #include "chrome/browser/ash/crosapi/idle_service_ash.h"
 #include "chrome/browser/ash/profiles/profile_helper.h"
 #include "chrome/browser/browser_process.h"
@@ -44,10 +45,12 @@
 #include "components/metrics/metrics_pref_names.h"
 #include "components/prefs/pref_registry_simple.h"
 #include "components/prefs/pref_service.h"
+#include "components/prefs/scoped_user_pref_update.h"
 #include "components/user_manager/user.h"
 #include "components/user_manager/user_manager.h"
 #include "components/user_manager/user_type.h"
 #include "components/version_info/channel.h"
+#include "components/version_info/version_info.h"
 #include "media/capture/mojom/video_capture.mojom.h"
 #include "mojo/public/cpp/platform/platform_channel.h"
 #include "mojo/public/cpp/system/invitation.h"
@@ -238,6 +241,7 @@
 
 const char kLaunchOnLoginPref[] = "lacros.launch_on_login";
 const char kClearUserDataDir1Pref[] = "lacros.clear_user_data_dir_1";
+const char kDataVerPref[] = "lacros.data_version";
 
 void RegisterProfilePrefs(PrefRegistrySimple* registry) {
   registry->RegisterBooleanPref(kLaunchOnLoginPref, /*default_value=*/false);
@@ -245,6 +249,10 @@
                                 /*default_value=*/false);
 }
 
+void RegisterLocalStatePrefs(PrefRegistrySimple* registry) {
+  registry->RegisterDictionaryPref(kDataVerPref);
+}
+
 base::FilePath GetUserDataDir() {
   if (base::SysInfo::IsRunningOnChromeOS()) {
     // NOTE: On device this function is privacy/security sensitive. The
@@ -559,5 +567,27 @@
   return fd;
 }
 
+base::Version GetDataVer(PrefService* local_state,
+                         const std::string& user_id_hash) {
+  const base::DictionaryValue* data_versions =
+      local_state->GetDictionary(kDataVerPref);
+  const std::string* data_version_str =
+      data_versions->FindStringPath(user_id_hash);
+
+  if (!data_version_str)
+    return base::Version();
+
+  return base::Version(*data_version_str);
+}
+
+void RecordDataVer(PrefService* local_state,
+                   const std::string& user_id_hash,
+                   const base::Version& version) {
+  DCHECK(version.IsValid());
+  DictionaryPrefUpdate update(local_state, kDataVerPref);
+  base::DictionaryValue* dict = update.Get();
+  dict->SetString(user_id_hash, version.GetString());
+}
+
 }  // namespace browser_util
 }  // namespace crosapi
diff --git a/chrome/browser/ash/crosapi/browser_util.h b/chrome/browser/ash/crosapi/browser_util.h
index a71661b..c0a22d8 100644
--- a/chrome/browser/ash/crosapi/browser_util.h
+++ b/chrome/browser/ash/crosapi/browser_util.h
@@ -16,6 +16,7 @@
 #include "mojo/public/cpp/bindings/remote.h"
 
 class PrefRegistrySimple;
+class PrefService;
 
 namespace aura {
 class Window;
@@ -23,6 +24,7 @@
 
 namespace base {
 class FilePath;
+class Version;
 }  // namespace base
 
 namespace mojo {
@@ -76,9 +78,16 @@
 // introduced by account_manager in M91/M92 timeframe.
 extern const char kClearUserDataDir1Pref[];
 
+// A dictionary local state pref that records the last data version of
+// lacros-chrome.
+extern const char kDataVerPref[];
+
 // Registers user profile preferences related to the lacros-chrome binary.
 void RegisterProfilePrefs(PrefRegistrySimple* registry);
 
+// Registers prefs used via local state PrefService.
+void RegisterLocalStatePrefs(PrefRegistrySimple* registry);
+
 // Returns the user directory for lacros-chrome.
 base::FilePath GetUserDataDir();
 
@@ -167,6 +176,18 @@
     ::crosapi::EnvironmentProvider* environment_provider,
     crosapi::mojom::InitialBrowserAction initial_browser_action);
 
+// Reads `kDataVerPref` and gets corresponding data version for `user_id_hash`.
+// If no such version is registered yet, returns `Version` that is invalid.
+// Should only be called on UI thread since it reads from `LocalState`.
+base::Version GetDataVer(PrefService* local_state,
+                         const std::string& user_id_hash);
+
+// Records data version for `user_id_hash` in `LocalState`. Should only be
+// called on UI thread since it reads from `LocalState`.
+void RecordDataVer(PrefService* local_state,
+                   const std::string& user_id_hash,
+                   const base::Version& version);
+
 }  // namespace browser_util
 }  // namespace crosapi
 
diff --git a/chrome/browser/ash/crosapi/browser_util_unittest.cc b/chrome/browser/ash/crosapi/browser_util_unittest.cc
index bcad7ed..670ea6f 100644
--- a/chrome/browser/ash/crosapi/browser_util_unittest.cc
+++ b/chrome/browser/ash/crosapi/browser_util_unittest.cc
@@ -5,8 +5,10 @@
 #include "chrome/browser/ash/crosapi/browser_util.h"
 
 #include "ash/constants/ash_features.h"
+#include "base/files/scoped_temp_dir.h"
 #include "base/json/json_reader.h"
 #include "base/test/scoped_feature_list.h"
+#include "base/values.h"
 #include "chrome/browser/ash/login/users/fake_chrome_user_manager.h"
 #include "chrome/browser/ash/profiles/profile_helper.h"
 #include "chrome/browser/browser_process.h"
@@ -20,6 +22,7 @@
 #include "components/account_id/account_id.h"
 #include "components/user_manager/scoped_user_manager.h"
 #include "components/version_info/channel.h"
+#include "components/version_info/version_info.h"
 #include "content/public/test/browser_task_environment.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
@@ -37,6 +40,7 @@
     fake_user_manager_ = new ash::FakeChromeUserManager;
     scoped_user_manager_ = std::make_unique<user_manager::ScopedUserManager>(
         base::WrapUnique(fake_user_manager_));
+    browser_util::RegisterLocalStatePrefs(pref_service_.registry());
   }
 
   void AddRegularUser(const std::string& email) {
@@ -55,6 +59,7 @@
   TestingProfile testing_profile_;
   ash::FakeChromeUserManager* fake_user_manager_ = nullptr;
   std::unique_ptr<user_manager::ScopedUserManager> scoped_user_manager_;
+  TestingPrefServiceSimple pref_service_;
 
   ScopedTestingLocalState local_state_;
 };
@@ -408,4 +413,84 @@
       browser_util::DoesMetadataSupportNewAccountManager(&value.value()));
 }
 
+TEST_F(BrowserUtilTest, GetMissingDataVer) {
+  std::string user_id_hash = "1234";
+  base::Version version =
+      browser_util::GetDataVer(&pref_service_, user_id_hash);
+  EXPECT_FALSE(version.IsValid());
+}
+
+TEST_F(BrowserUtilTest, GetCorruptDataVer) {
+  base::DictionaryValue dictionary_value;
+  std::string user_id_hash = "1234";
+  dictionary_value.SetString(user_id_hash, "corrupted");
+  pref_service_.Set(browser_util::kDataVerPref, dictionary_value);
+  base::Version version =
+      browser_util::GetDataVer(&pref_service_, user_id_hash);
+  EXPECT_FALSE(version.IsValid());
+}
+
+TEST_F(BrowserUtilTest, GetDataVer) {
+  base::DictionaryValue dictionary_value;
+  std::string user_id_hash = "1234";
+  base::Version version{"1.1.1.1"};
+  dictionary_value.SetString(user_id_hash, version.GetString());
+  pref_service_.Set(browser_util::kDataVerPref, dictionary_value);
+
+  base::Version result_version =
+      browser_util::GetDataVer(&pref_service_, user_id_hash);
+  EXPECT_EQ(version, result_version);
+}
+
+TEST_F(BrowserUtilTest, RecordDataVer) {
+  std::string user_id_hash = "1234";
+  base::Version version{"1.1.1.1"};
+  browser_util::RecordDataVer(&pref_service_, user_id_hash, version);
+
+  base::DictionaryValue expected;
+  expected.SetString(user_id_hash, version.GetString());
+  const base::DictionaryValue* dict =
+      pref_service_.GetDictionary(browser_util::kDataVerPref);
+  EXPECT_TRUE(dict->Equals(&expected));
+}
+
+TEST_F(BrowserUtilTest, RecordDataVerOverrides) {
+  std::string user_id_hash = "1234";
+
+  base::Version version1{"1.1.1.1"};
+  base::Version version2{"1.1.1.2"};
+  browser_util::RecordDataVer(&pref_service_, user_id_hash, version1);
+  browser_util::RecordDataVer(&pref_service_, user_id_hash, version2);
+
+  base::DictionaryValue expected;
+  expected.SetString(user_id_hash, version2.GetString());
+
+  const base::DictionaryValue* dict =
+      pref_service_.GetDictionary(browser_util::kDataVerPref);
+  EXPECT_TRUE(dict->Equals(&expected));
+}
+
+TEST_F(BrowserUtilTest, RecordDataVerWithMultipleUsers) {
+  std::string user_id_hash_1 = "1234";
+  std::string user_id_hash_2 = "2345";
+  base::Version version1{"1.1.1.1"};
+  base::Version version2{"1.1.1.2"};
+  browser_util::RecordDataVer(&pref_service_, user_id_hash_1, version1);
+  browser_util::RecordDataVer(&pref_service_, user_id_hash_2, version2);
+
+  EXPECT_EQ(version1, browser_util::GetDataVer(&pref_service_, user_id_hash_1));
+  EXPECT_EQ(version2, browser_util::GetDataVer(&pref_service_, user_id_hash_2));
+
+  base::Version version3{"3.3.3.3"};
+  browser_util::RecordDataVer(&pref_service_, user_id_hash_1, version3);
+
+  base::DictionaryValue expected;
+  expected.SetString(user_id_hash_1, version3.GetString());
+  expected.SetString(user_id_hash_2, version2.GetString());
+
+  const base::DictionaryValue* dict =
+      pref_service_.GetDictionary(browser_util::kDataVerPref);
+  EXPECT_TRUE(dict->Equals(&expected));
+}
+
 }  // namespace crosapi
diff --git a/chrome/browser/ash/login/login_browsertest.cc b/chrome/browser/ash/login/login_browsertest.cc
index e3b76a9..58318bb 100644
--- a/chrome/browser/ash/login/login_browsertest.cc
+++ b/chrome/browser/ash/login/login_browsertest.cc
@@ -240,7 +240,7 @@
   Profile* profile = browser()->profile();
   std::string profile_base_path("hash");
   profile_base_path.insert(0, chrome::kProfileDirPrefix);
-  EXPECT_EQ(profile_base_path, profile->GetPath().BaseName().value());
+  EXPECT_EQ(profile_base_path, profile->GetBaseName().value());
   EXPECT_FALSE(profile->IsOffTheRecord());
 
   TestSystemTrayIsVisible();
diff --git a/chrome/browser/ash/login/login_utils_browsertest.cc b/chrome/browser/ash/login/login_utils_browsertest.cc
index e874589..6d343b0 100644
--- a/chrome/browser/ash/login/login_utils_browsertest.cc
+++ b/chrome/browser/ash/login/login_utils_browsertest.cc
@@ -67,8 +67,8 @@
   {
     base::RunLoop loop;
     WizardController::SkipPostLoginScreensForTesting();
-    EXPECT_FALSE(UserSessionInitializer::Get()->get_inited_for_testing());
-    UserSessionInitializer::Get()->set_init_rlz_impl_closure_for_testing(
+    EXPECT_FALSE(ash::UserSessionInitializer::Get()->get_inited_for_testing());
+    ash::UserSessionInitializer::Get()->set_init_rlz_impl_closure_for_testing(
         loop.QuitClosure());
 
     login_manager_.LoginAsNewRegularUser();
diff --git a/chrome/browser/ash/login/oobe_localization_browsertest.cc b/chrome/browser/ash/login/oobe_localization_browsertest.cc
index 1a2b574..295d7e6 100644
--- a/chrome/browser/ash/login/oobe_localization_browsertest.cc
+++ b/chrome/browser/ash/login/oobe_localization_browsertest.cc
@@ -374,8 +374,7 @@
   EXPECT_EQ(expected_keyboard_select, DumpOptions(kKeyboardSelect));
 }
 
-// TODO(crbug.com/1201546): Fix flakiness.
-IN_PROC_BROWSER_TEST_P(OobeLocalizationTest, DISABLED_LocalizationTest) {
+IN_PROC_BROWSER_TEST_P(OobeLocalizationTest, LocalizationTest) {
   RunLocalizationTest();
 }
 
diff --git a/chrome/browser/ash/profiles/profile_helper.cc b/chrome/browser/ash/profiles/profile_helper.cc
index 8ec22ac7..5ae9ba0 100644
--- a/chrome/browser/ash/profiles/profile_helper.cc
+++ b/chrome/browser/ash/profiles/profile_helper.cc
@@ -277,7 +277,7 @@
   if (!profile)
     return std::string();
 
-  std::string profile_dir = profile->GetPath().BaseName().value();
+  std::string profile_dir = profile->GetBaseName().value();
 
   // Don't strip prefix if the dir is not supposed to be prefixed.
   if (!ShouldAddProfileDirPrefix(profile_dir))
@@ -304,7 +304,7 @@
 
 // static
 bool ProfileHelper::IsSigninProfile(const Profile* profile) {
-  return profile && IsSigninProfilePath(profile->GetPath().BaseName());
+  return profile && IsSigninProfilePath(profile->GetBaseName());
 }
 
 // static
@@ -316,7 +316,7 @@
 
 // static
 bool ProfileHelper::IsLockScreenAppProfile(const Profile* profile) {
-  return profile && IsLockScreenAppProfilePath(profile->GetPath().BaseName());
+  return profile && IsLockScreenAppProfilePath(profile->GetBaseName());
 }
 
 // static
@@ -341,7 +341,7 @@
 
 // static
 bool ProfileHelper::IsLockScreenProfile(const Profile* profile) {
-  return profile && IsLockScreenProfilePath(profile->GetPath().BaseName());
+  return profile && IsLockScreenProfilePath(profile->GetBaseName());
 }
 
 // static
diff --git a/chrome/browser/ash/system/procfs_util.h b/chrome/browser/ash/system/procfs_util.h
index 8ef2ede..46fda76c 100644
--- a/chrome/browser/ash/system/procfs_util.h
+++ b/chrome/browser/ash/system/procfs_util.h
@@ -6,7 +6,7 @@
 #define CHROME_BROWSER_ASH_SYSTEM_PROCFS_UTIL_H_
 
 #include "base/files/file_path.h"
-#include "base/values.h"
+#include "base/optional.h"
 
 namespace ash {
 namespace system {
diff --git a/chrome/browser/chromeos/crostini/crostini_util.cc b/chrome/browser/chromeos/crostini/crostini_util.cc
index f2d05c3f..37f2a26 100644
--- a/chrome/browser/chromeos/crostini/crostini_util.cc
+++ b/chrome/browser/chromeos/crostini/crostini_util.cc
@@ -61,9 +61,6 @@
 const char kCrostiniDefaultVmName[] = "termina";
 const char kCrostiniDefaultContainerName[] = "penguin";
 const char kCrostiniDefaultUsername[] = "emperor";
-// In order to be compatible with sync folder id must match standard.
-// Generated using crx_file::id_util::GenerateId("LinuxAppsFolder")
-const char kCrostiniFolderId[] = "ddolnhmblagmcagkedkbfejapapdimlk";
 const char kCrostiniDefaultImageServerUrl[] =
     "https://storage.googleapis.com/cros-containers/%d";
 const char kCrostiniStretchImageAlias[] = "debian/stretch";
diff --git a/chrome/browser/chromeos/crostini/crostini_util.h b/chrome/browser/chromeos/crostini/crostini_util.h
index dd7c1c8..2db46f6 100644
--- a/chrome/browser/chromeos/crostini/crostini_util.h
+++ b/chrome/browser/chromeos/crostini/crostini_util.h
@@ -45,9 +45,6 @@
 extern const char kCrostiniDefaultVmName[];
 extern const char kCrostiniDefaultContainerName[];
 extern const char kCrostiniDefaultUsername[];
-// In order to be compatible with sync folder id must match standard.
-// Generated using crx_file::id_util::GenerateId("LinuxAppsFolder")
-extern const char kCrostiniFolderId[];
 extern const char kCrostiniDefaultImageServerUrl[];
 extern const char kCrostiniStretchImageAlias[];
 extern const char kCrostiniBusterImageAlias[];
diff --git a/chrome/browser/chromeos/file_manager/external_filesystem_apitest.cc b/chrome/browser/chromeos/file_manager/external_filesystem_apitest.cc
index 6bca2c9a..6b978f4 100644
--- a/chrome/browser/chromeos/file_manager/external_filesystem_apitest.cc
+++ b/chrome/browser/chromeos/file_manager/external_filesystem_apitest.cc
@@ -604,7 +604,7 @@
     base::FilePath drivefs_dir;
     base::CreateTemporaryDirInDir(tmp_dir_.GetPath(),
                                   base::FilePath::StringType(), &drivefs_dir);
-    auto profile_name_storage = profile->GetPath().BaseName().value();
+    auto profile_name_storage = profile->GetBaseName().value();
     base::StringPiece profile_name = profile_name_storage;
     if (base::StartsWith(profile_name, "u-")) {
       profile_name = profile_name.substr(2);
diff --git a/chrome/browser/chromeos/file_manager/file_manager_browsertest_base.cc b/chrome/browser/chromeos/file_manager/file_manager_browsertest_base.cc
index 2b9a48e7..8dbb430 100644
--- a/chrome/browser/chromeos/file_manager/file_manager_browsertest_base.cc
+++ b/chrome/browser/chromeos/file_manager/file_manager_browsertest_base.cc
@@ -2764,7 +2764,7 @@
   drive_volumes_[profile->GetOriginalProfile()] =
       std::make_unique<DriveFsTestVolume>(profile->GetOriginalProfile());
   if (options.guest_mode != IN_INCOGNITO && options.mount_volumes &&
-      profile->GetPath().BaseName().value() == "user") {
+      profile->GetBaseName().value() == "user") {
     base::ThreadTaskRunnerHandle::Get()->PostTask(
         FROM_HERE,
         base::BindOnce(base::IgnoreResult(&LocalTestVolume::Mount),
diff --git a/chrome/browser/chromeos/file_manager/path_util.cc b/chrome/browser/chromeos/file_manager/path_util.cc
index fbff76b1..6049a9b 100644
--- a/chrome/browser/chromeos/file_manager/path_util.cc
+++ b/chrome/browser/chromeos/file_manager/path_util.cc
@@ -766,8 +766,7 @@
   if (ReplacePrefix(&result, "/home/chronos/user/Downloads",
                     kFolderNameDownloads)) {
   } else if (ReplacePrefix(&result,
-                           "/home/chronos/" +
-                               profile->GetPath().BaseName().value() +
+                           "/home/chronos/" + profile->GetBaseName().value() +
                                "/Downloads",
                            kFolderNameDownloads)) {
   } else if (ReplacePrefix(
@@ -775,9 +774,8 @@
                  std::string("/home/chronos/user/") + kFolderNameMyFiles,
                  "My files")) {
   } else if (ReplacePrefix(&result,
-                           "/home/chronos/" +
-                               profile->GetPath().BaseName().value() + "/" +
-                               kFolderNameMyFiles,
+                           "/home/chronos/" + profile->GetBaseName().value() +
+                               "/" + kFolderNameMyFiles,
                            "My files")) {
   } else if (drive_integration_service &&
              ReplacePrefix(&result,
diff --git a/chrome/browser/devtools/chrome_devtools_session.h b/chrome/browser/devtools/chrome_devtools_session.h
index 1678e52..7f4072fee 100644
--- a/chrome/browser/devtools/chrome_devtools_session.h
+++ b/chrome/browser/devtools/chrome_devtools_session.h
@@ -6,10 +6,9 @@
 #define CHROME_BROWSER_DEVTOOLS_CHROME_DEVTOOLS_SESSION_H_
 
 #include <memory>
-#include <string>
-#include <utility>
 
-#include "base/values.h"
+#include "base/containers/flat_map.h"
+#include "base/containers/span.h"
 #include "build/chromeos_buildflags.h"
 #include "chrome/browser/devtools/protocol/forward.h"
 #include "chrome/browser/devtools/protocol/protocol.h"
diff --git a/chrome/browser/enterprise/reporting/extension_request/extension_request_report_generator.cc b/chrome/browser/enterprise/reporting/extension_request/extension_request_report_generator.cc
index 86dda26..3567153 100644
--- a/chrome/browser/enterprise/reporting/extension_request/extension_request_report_generator.cc
+++ b/chrome/browser/enterprise/reporting/extension_request/extension_request_report_generator.cc
@@ -10,9 +10,12 @@
 #include "base/time/time.h"
 #include "base/util/values/values_util.h"
 #include "base/values.h"
+#include "chrome/browser/browser_process.h"
+#include "chrome/browser/enterprise/reporting/extension_request/extension_request_report_throttler.h"
 #include "chrome/browser/enterprise/reporting/prefs.h"
 #include "chrome/browser/extensions/extension_management.h"
 #include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/profiles/profile_manager.h"
 #include "chrome/common/extensions/extension_constants.h"
 #include "chrome/common/pref_names.h"
 #include "components/enterprise/common/proto/extensions_workflow_events.pb.h"
@@ -37,10 +40,12 @@
   auto report = std::make_unique<ExtensionsWorkflowEvent>();
   report->set_id(extension_id);
   if (request_data) {
-    base::Optional<base::Time> timestamp = ::util::ValueToTime(
-        request_data->FindKey(extension_misc::kExtensionRequestTimestamp));
-    if (timestamp)
-      report->set_request_timestamp_millis(timestamp->ToJavaTime());
+    if (request_data->is_dict()) {
+      base::Optional<base::Time> timestamp = ::util::ValueToTime(
+          request_data->FindKey(extension_misc::kExtensionRequestTimestamp));
+      if (timestamp)
+        report->set_request_timestamp_millis(timestamp->ToJavaTime());
+    }
     report->set_removed(false);
   } else {
     report->set_removed(true);
@@ -71,7 +76,32 @@
 ExtensionRequestReportGenerator::~ExtensionRequestReportGenerator() = default;
 
 std::vector<std::unique_ptr<ExtensionsWorkflowEvent>>
-ExtensionRequestReportGenerator::Generate(Profile* profile) {
+ExtensionRequestReportGenerator::Generate() {
+  auto* throttler = ExtensionRequestReportThrottler::Get();
+
+  // Returns empty list if real time extension request uploading is not enabled.
+  if (!throttler->IsEnabled()) {
+    return std::vector<std::unique_ptr<ExtensionsWorkflowEvent>>();
+  }
+
+  ProfileManager* profile_manager = g_browser_process->profile_manager();
+  std::vector<std::unique_ptr<ExtensionsWorkflowEvent>> reports;
+  for (auto& profile_path : throttler->GetProfiles()) {
+    Profile* profile = profile_manager->GetProfileByPath(profile_path);
+    if (!profile)
+      continue;
+    std::vector<std::unique_ptr<ExtensionsWorkflowEvent>> profile_reports =
+        GenerateForProfile(profile);
+    reports.insert(reports.end(),
+                   std::make_move_iterator(profile_reports.begin()),
+                   std::make_move_iterator(profile_reports.end()));
+  }
+  throttler->ResetProfiles();
+  return reports;
+}
+
+std::vector<std::unique_ptr<ExtensionsWorkflowEvent>>
+ExtensionRequestReportGenerator::GenerateForProfile(Profile* profile) {
   DCHECK(profile);
 
   std::vector<std::unique_ptr<ExtensionsWorkflowEvent>> reports;
diff --git a/chrome/browser/enterprise/reporting/extension_request/extension_request_report_generator.h b/chrome/browser/enterprise/reporting/extension_request/extension_request_report_generator.h
index b11504d..cea49e67 100644
--- a/chrome/browser/enterprise/reporting/extension_request/extension_request_report_generator.h
+++ b/chrome/browser/enterprise/reporting/extension_request/extension_request_report_generator.h
@@ -36,8 +36,10 @@
       const ExtensionRequestReportGenerator&) = delete;
   ~ExtensionRequestReportGenerator();
 
+  std::vector<std::unique_ptr<ExtensionsWorkflowEvent>> Generate();
+
   // Uploads extension request update for |profile|.
-  std::vector<std::unique_ptr<ExtensionsWorkflowEvent>> Generate(
+  std::vector<std::unique_ptr<ExtensionsWorkflowEvent>> GenerateForProfile(
       Profile* profile);
 };
 
diff --git a/chrome/browser/enterprise/reporting/extension_request/extension_request_report_generator_unittest.cc b/chrome/browser/enterprise/reporting/extension_request/extension_request_report_generator_unittest.cc
index 77f2fe3..0347141 100644
--- a/chrome/browser/enterprise/reporting/extension_request/extension_request_report_generator_unittest.cc
+++ b/chrome/browser/enterprise/reporting/extension_request/extension_request_report_generator_unittest.cc
@@ -7,6 +7,7 @@
 #include "base/json/json_reader.h"
 #include "base/time/time.h"
 #include "base/util/values/values_util.h"
+#include "chrome/browser/enterprise/reporting/extension_request/extension_request_report_throttler_test.h"
 #include "chrome/browser/enterprise/reporting/prefs.h"
 #include "chrome/common/extensions/extension_constants.h"
 #include "chrome/common/pref_names.h"
@@ -23,9 +24,11 @@
 namespace {
 
 constexpr int kTimeStamp = 42;
-constexpr char kProfileName[] = "profile";
+constexpr char kProfileName[] = "profile-1";
+constexpr char kAnotherProfileName[] = "profile-2";
 constexpr char kExtensionId1[] = "abcdefghijklmnopabcdefghijklmnop";
 constexpr char kExtensionId2[] = "abcdefghijklmnopabcdefghijklmnpo";
+constexpr char kExtensionId3[] = "abcdefghijklmnopabcdefghijklmonp";
 
 constexpr char kAllowedExtensionSettings[] = R"({
   "abcdefghijklmnopabcdefghijklmnop" : {
@@ -49,31 +52,40 @@
   void SetUp() override { ASSERT_TRUE(profile_manager_.SetUp()); }
 
   void SetExtensionRequestsList(const std::vector<std::string>& pendings,
-                                const std::vector<std::string>& uploadeds) {
+                                const std::vector<std::string>& uploadeds,
+                                TestingProfile* profile) {
     SetRequestPrefs(pendings, prefs::kCloudExtensionRequestIds,
-                    extension_misc::kExtensionRequestTimestamp);
+                    extension_misc::kExtensionRequestTimestamp, profile);
     SetRequestPrefs(uploadeds, kCloudExtensionRequestUploadedIds,
-                    "upload_timestamp");
+                    "upload_timestamp", profile);
   }
 
-  void SetExtensionSettings(const std::string& settings_string) {
+  void SetExtensionSettings(const std::string& settings_string,
+                            TestingProfile* profile) {
     base::Optional<base::Value> settings =
         base::JSONReader::Read(settings_string);
     ASSERT_TRUE(settings.has_value());
-    profile_->GetTestingPrefService()->SetManagedPref(
+    profile->GetTestingPrefService()->SetManagedPref(
         extensions::pref_names::kExtensionManagement,
         base::Value::ToUniquePtrValue(std::move(*settings)));
   }
 
-  std::vector<std::unique_ptr<ExtensionsWorkflowEvent>> GenerateReports() {
-    return generator_.Generate(profile_);
+  std::vector<std::unique_ptr<ExtensionsWorkflowEvent>> GenerateReports(
+      Profile* profile) {
+    return generator_.GenerateForProfile(profile);
   }
 
-  void CreateProfile() {
-    profile_ = profile_manager_.CreateTestingProfile(kProfileName);
-    profile_->GetTestingPrefService()->SetManagedPref(
+  std::vector<std::unique_ptr<ExtensionsWorkflowEvent>> GenerateReports() {
+    return generator_.Generate();
+  }
+
+  TestingProfile* CreateProfile(const std::string& profile_name) {
+    TestingProfile* profile =
+        profile_manager_.CreateTestingProfile(profile_name);
+    profile->GetTestingPrefService()->SetManagedPref(
         prefs::kCloudExtensionRequestEnabled,
         std::make_unique<base::Value>(true));
+    return profile;
   }
 
   void VerifyReport(ExtensionsWorkflowEvent* actual_report,
@@ -92,10 +104,15 @@
 #endif  // BUILDFLAG(IS_CHROMEOS_ASH)
   }
 
+  void AddProfileToThrottler(Profile* profile) {
+    throttler_.Get()->AddProfile(profile->GetPath());
+  }
+
  private:
   void SetRequestPrefs(const std::vector<std::string>& ids,
                        const std::string& pref_name,
-                       const std::string& timestamp_name) {
+                       const std::string& timestamp_name,
+                       TestingProfile* profile) {
     std::unique_ptr<base::Value> id_values =
         std::make_unique<base::Value>(base::Value::Type::DICTIONARY);
     for (const auto& id : ids) {
@@ -106,64 +123,85 @@
       id_values->SetKey(id, std::move(request_data));
     }
 
-    profile_->GetTestingPrefService()->SetUserPref(pref_name,
-                                                   std::move(id_values));
+    profile->GetTestingPrefService()->SetUserPref(pref_name,
+                                                  std::move(id_values));
   }
 
   content::BrowserTaskEnvironment task_environment_;
   ExtensionRequestReportGenerator generator_;
   TestingProfileManager profile_manager_;
-  TestingProfile* profile_;
+  ScopedExtensionRequestReportThrottler throttler_;
 };
 
 TEST_F(ExtensionRequestReportGeneratorTest, AddRequests) {
-  CreateProfile();
-  SetExtensionRequestsList({kExtensionId1, kExtensionId2}, {});
+  auto* profile = CreateProfile(kProfileName);
+  SetExtensionRequestsList({kExtensionId1, kExtensionId2}, {}, profile);
 
-  auto reports = GenerateReports();
+  auto reports = GenerateReports(profile);
 
   EXPECT_EQ(2u, reports.size());
   VerifyReport(reports[0].get(), kExtensionId1, /*is_removed=*/false);
   VerifyReport(reports[1].get(), kExtensionId2, /*is_removed=*/false);
 
-  reports = GenerateReports();
+  reports = GenerateReports(profile);
 
   EXPECT_EQ(0u, reports.size());
 }
 
 TEST_F(ExtensionRequestReportGeneratorTest, RemovalRequest) {
-  CreateProfile();
-  SetExtensionRequestsList({}, {kExtensionId1, kExtensionId2});
+  auto* profile = CreateProfile(kProfileName);
+  SetExtensionRequestsList({}, {kExtensionId1, kExtensionId2}, profile);
 
-  auto reports = GenerateReports();
+  auto reports = GenerateReports(profile);
 
   EXPECT_EQ(2u, reports.size());
   VerifyReport(reports[0].get(), kExtensionId1, /*is_removed=*/true);
   VerifyReport(reports[1].get(), kExtensionId2, /*is_removed=*/true);
 
-  reports = GenerateReports();
+  reports = GenerateReports(profile);
 
   EXPECT_EQ(0u, reports.size());
 }
 
 TEST_F(ExtensionRequestReportGeneratorTest, ApprovedRequest) {
-  CreateProfile();
-  SetExtensionRequestsList({kExtensionId1}, {});
-  SetExtensionSettings(kAllowedExtensionSettings);
+  auto* profile = CreateProfile(kProfileName);
+  SetExtensionRequestsList({kExtensionId1}, {}, profile);
+  SetExtensionSettings(kAllowedExtensionSettings, profile);
 
-  auto reports = GenerateReports();
+  auto reports = GenerateReports(profile);
 
   EXPECT_EQ(0u, reports.size());
 }
 
 TEST_F(ExtensionRequestReportGeneratorTest, RejectedRequest) {
-  CreateProfile();
-  SetExtensionRequestsList({kExtensionId1}, {});
-  SetExtensionSettings(kBlockedExtensionSettings);
+  auto* profile = CreateProfile(kProfileName);
+  SetExtensionRequestsList({kExtensionId1}, {}, profile);
+  SetExtensionSettings(kBlockedExtensionSettings, profile);
+
+  auto reports = GenerateReports(profile);
+
+  EXPECT_EQ(0u, reports.size());
+}
+
+TEST_F(ExtensionRequestReportGeneratorTest, MultipleProfiles) {
+  auto* profile = CreateProfile(kProfileName);
+  auto* another_profile = CreateProfile(kAnotherProfileName);
+
+  SetExtensionRequestsList({kExtensionId1, kExtensionId2}, {}, profile);
+  SetExtensionRequestsList({kExtensionId1, kExtensionId3}, {}, another_profile);
+
+  AddProfileToThrottler(profile);
+  AddProfileToThrottler(another_profile);
 
   auto reports = GenerateReports();
 
-  EXPECT_EQ(0u, reports.size());
+  EXPECT_EQ(4u, reports.size());
+  VerifyReport(reports[0].get(), kExtensionId1, /*is_removed=*/false);
+  VerifyReport(reports[1].get(), kExtensionId2, /*is_removed=*/false);
+  VerifyReport(reports[2].get(), kExtensionId1, /*is_removed=*/false);
+  VerifyReport(reports[3].get(), kExtensionId3, /*is_removed=*/false);
+
+  EXPECT_EQ(0u, GenerateReports().size());
 }
 
 }  // namespace enterprise_reporting
diff --git a/chrome/browser/enterprise/reporting/real_time_report_generator_desktop.cc b/chrome/browser/enterprise/reporting/real_time_report_generator_desktop.cc
new file mode 100644
index 0000000..52c8596
--- /dev/null
+++ b/chrome/browser/enterprise/reporting/real_time_report_generator_desktop.cc
@@ -0,0 +1,27 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/enterprise/reporting/real_time_report_generator_desktop.h"
+#include "base/logging.h"
+#include "components/enterprise/browser/reporting/report_type.h"
+#include "components/enterprise/common/proto/extensions_workflow_events.pb.h"
+
+namespace enterprise_reporting {
+
+RealTimeReportGeneratorDesktop::RealTimeReportGeneratorDesktop() = default;
+RealTimeReportGeneratorDesktop::~RealTimeReportGeneratorDesktop() = default;
+
+std::vector<std::unique_ptr<google::protobuf::MessageLite>>
+RealTimeReportGeneratorDesktop::Generate(
+    RealTimeReportGenerator::ReportType type) {
+  std::vector<std::unique_ptr<google::protobuf::MessageLite>> reports;
+  switch (type) {
+    case RealTimeReportGenerator::kExtensionRequest:
+      for (auto& report : extension_request_report_generator_.Generate())
+        reports.push_back(std::move(report));
+  }
+  return reports;
+}
+
+}  // namespace enterprise_reporting
diff --git a/chrome/browser/enterprise/reporting/real_time_report_generator_desktop.h b/chrome/browser/enterprise/reporting/real_time_report_generator_desktop.h
new file mode 100644
index 0000000..b8f7cdc9
--- /dev/null
+++ b/chrome/browser/enterprise/reporting/real_time_report_generator_desktop.h
@@ -0,0 +1,33 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_ENTERPRISE_REPORTING_REAL_TIME_REPORT_GENERATOR_DESKTOP_H_
+#define CHROME_BROWSER_ENTERPRISE_REPORTING_REAL_TIME_REPORT_GENERATOR_DESKTOP_H_
+
+#include "chrome/browser/enterprise/reporting/extension_request/extension_request_report_generator.h"
+#include "components/enterprise/browser/reporting/real_time_report_generator.h"
+
+namespace enterprise_reporting {
+
+class RealTimeReportGeneratorDesktop
+    : public RealTimeReportGenerator::Delegate {
+ public:
+  RealTimeReportGeneratorDesktop();
+  RealTimeReportGeneratorDesktop(const RealTimeReportGeneratorDesktop&) =
+      delete;
+  RealTimeReportGeneratorDesktop& operator=(
+      const RealTimeReportGeneratorDesktop&) = delete;
+  ~RealTimeReportGeneratorDesktop() override;
+
+  // RealTimeReportGenerator::Delegate
+  std::vector<std::unique_ptr<google::protobuf::MessageLite>> Generate(
+      RealTimeReportGenerator::ReportType type) override;
+
+ private:
+  ExtensionRequestReportGenerator extension_request_report_generator_;
+};
+
+}  // namespace enterprise_reporting
+
+#endif  // CHROME_BROWSER_ENTERPRISE_REPORTING_REAL_TIME_REPORT_GENERATOR_DESKTOP_H_
diff --git a/chrome/browser/enterprise/reporting/real_time_report_generator_unittest.cc b/chrome/browser/enterprise/reporting/real_time_report_generator_unittest.cc
new file mode 100644
index 0000000..8cf15088
--- /dev/null
+++ b/chrome/browser/enterprise/reporting/real_time_report_generator_unittest.cc
@@ -0,0 +1,67 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/enterprise/browser/reporting/real_time_report_generator.h"
+#include "base/util/values/values_util.h"
+#include "chrome/browser/enterprise/reporting/extension_request/extension_request_report_throttler_test.h"
+#include "chrome/browser/enterprise/reporting/prefs.h"
+#include "chrome/browser/enterprise/reporting/real_time_report_generator_desktop.h"
+#include "chrome/browser/enterprise/reporting/reporting_delegate_factory_desktop.h"
+#include "chrome/common/extensions/extension_constants.h"
+#include "chrome/common/pref_names.h"
+#include "chrome/test/base/testing_browser_process.h"
+#include "chrome/test/base/testing_profile_manager.h"
+#include "components/enterprise/common/proto/extensions_workflow_events.pb.h"
+#include "components/sync_preferences/testing_pref_service_syncable.h"
+#include "content/public/test/browser_task_environment.h"
+#include "extensions/browser/pref_names.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace enterprise_reporting {
+
+class RealTimeReportGeneratorTest : public ::testing::Test {
+ public:
+  RealTimeReportGeneratorTest()
+      : profile_manager_(TestingBrowserProcess::GetGlobal()) {}
+
+  void SetUp() override { ASSERT_TRUE(profile_manager_.SetUp()); }
+
+  TestingProfileManager* profile_manager() { return &profile_manager_; }
+
+ private:
+  content::BrowserTaskEnvironment task_environment_;
+  TestingProfileManager profile_manager_;
+};
+
+TEST_F(RealTimeReportGeneratorTest, ExtensionRequest) {
+  const std::string extension_id = "abcdefghijklmnopabcdefghijklmnop";
+
+  TestingProfile* profile = profile_manager()->CreateTestingProfile("profile");
+
+  profile->GetTestingPrefService()->SetManagedPref(
+      prefs::kCloudExtensionRequestEnabled,
+      std::make_unique<base::Value>(true));
+
+  std::unique_ptr<base::Value> requests =
+      std::make_unique<base::Value>(base::Value::Type::DICTIONARY);
+  requests->SetKey(extension_id, base::Value());
+  profile->GetTestingPrefService()->SetUserPref(
+      prefs::kCloudExtensionRequestIds, std::move(requests));
+
+  ScopedExtensionRequestReportThrottler throttler;
+
+  throttler.Get()->AddProfile(profile->GetPath());
+
+  ReportingDelegateFactoryDesktop factory;
+  RealTimeReportGenerator generator{&factory};
+
+  std::vector<std::unique_ptr<google::protobuf::MessageLite>> reports =
+      generator.Generate(RealTimeReportGenerator::kExtensionRequest);
+  EXPECT_EQ(1u, reports.size());
+
+  EXPECT_EQ(extension_id,
+            static_cast<ExtensionsWorkflowEvent*>(reports[0].get())->id());
+}
+
+}  // namespace enterprise_reporting
diff --git a/chrome/browser/enterprise/reporting/reporting_delegate_factory_desktop.cc b/chrome/browser/enterprise/reporting/reporting_delegate_factory_desktop.cc
index bc6652f..2c67467f 100644
--- a/chrome/browser/enterprise/reporting/reporting_delegate_factory_desktop.cc
+++ b/chrome/browser/enterprise/reporting/reporting_delegate_factory_desktop.cc
@@ -6,6 +6,7 @@
 
 #include "chrome/browser/enterprise/reporting/browser_report_generator_desktop.h"
 #include "chrome/browser/enterprise/reporting/profile_report_generator_desktop.h"
+#include "chrome/browser/enterprise/reporting/real_time_report_generator_desktop.h"
 #include "chrome/browser/enterprise/reporting/report_generator_desktop.h"
 #include "chrome/browser/enterprise/reporting/report_scheduler_desktop.h"
 
@@ -31,4 +32,9 @@
   return std::make_unique<ReportSchedulerDesktop>();
 }
 
+std::unique_ptr<RealTimeReportGenerator::Delegate>
+ReportingDelegateFactoryDesktop::GetRealTimeReportGeneratorDelegate() {
+  return std::make_unique<RealTimeReportGeneratorDesktop>();
+}
+
 }  // namespace enterprise_reporting
diff --git a/chrome/browser/enterprise/reporting/reporting_delegate_factory_desktop.h b/chrome/browser/enterprise/reporting/reporting_delegate_factory_desktop.h
index fbd05ef00..13604c8 100644
--- a/chrome/browser/enterprise/reporting/reporting_delegate_factory_desktop.h
+++ b/chrome/browser/enterprise/reporting/reporting_delegate_factory_desktop.h
@@ -11,6 +11,7 @@
 
 #include "components/enterprise/browser/reporting/browser_report_generator.h"
 #include "components/enterprise/browser/reporting/profile_report_generator.h"
+#include "components/enterprise/browser/reporting/real_time_report_generator.h"
 #include "components/enterprise/browser/reporting/report_generator.h"
 #include "components/enterprise/browser/reporting/report_scheduler.h"
 
@@ -38,6 +39,9 @@
 
   std::unique_ptr<ReportScheduler::Delegate> GetReportSchedulerDelegate()
       override;
+
+  std::unique_ptr<RealTimeReportGenerator::Delegate>
+  GetRealTimeReportGeneratorDelegate() override;
 };
 
 }  // namespace enterprise_reporting
diff --git a/chrome/browser/extensions/BUILD.gn b/chrome/browser/extensions/BUILD.gn
index 655f917..0b14145d 100644
--- a/chrome/browser/extensions/BUILD.gn
+++ b/chrome/browser/extensions/BUILD.gn
@@ -817,6 +817,7 @@
     "//chrome/services/file_util/public/mojom:mojom",
     "//chrome/services/removable_storage_writer/public/mojom",
     "//components/autofill/content/browser",
+    "//components/back_forward_cache",
     "//components/bookmarks/browser",
     "//components/bookmarks/managed",
     "//components/browser_sync",
diff --git a/chrome/browser/extensions/api/declarative_net_request/declarative_net_request_browsertest.cc b/chrome/browser/extensions/api/declarative_net_request/declarative_net_request_browsertest.cc
index d37e67bd..b2c86bd5 100644
--- a/chrome/browser/extensions/api/declarative_net_request/declarative_net_request_browsertest.cc
+++ b/chrome/browser/extensions/api/declarative_net_request/declarative_net_request_browsertest.cc
@@ -6075,7 +6075,8 @@
   DeclarativeNetRequestBackForwardCacheBrowserTest() {
     feature_list_.InitWithFeaturesAndParameters(
         {{features::kBackForwardCache,
-          {{"TimeToLiveInBackForwardCacheInSeconds", "3600"}}}},
+          {{"TimeToLiveInBackForwardCacheInSeconds", "3600"},
+           {"all_extensions_allowed", "true"}}}},
         {features::kBackForwardCacheMemoryControls});
   }
 
diff --git a/chrome/browser/extensions/api/messaging/native_message_process_host_unittest.cc b/chrome/browser/extensions/api/messaging/native_message_process_host_unittest.cc
index e256103..d56e0775 100644
--- a/chrome/browser/extensions/api/messaging/native_message_process_host_unittest.cc
+++ b/chrome/browser/extensions/api/messaging/native_message_process_host_unittest.cc
@@ -377,7 +377,7 @@
       cmd_line.GetSwitchValueASCII(switches::kNativeMessagingConnectExtension));
   EXPECT_EQ(features::kOnConnectNative.name,
             cmd_line.GetSwitchValueASCII(switches::kEnableFeatures));
-  EXPECT_EQ(profile_.GetPath().BaseName(),
+  EXPECT_EQ(profile_.GetBaseName(),
             cmd_line.GetSwitchValuePath(switches::kProfileDirectory));
   EXPECT_EQ(profile_.GetPath().DirName(),
             cmd_line.GetSwitchValuePath(switches::kUserDataDir));
diff --git a/chrome/browser/extensions/api/web_navigation/web_navigation_apitest.cc b/chrome/browser/extensions/api/web_navigation/web_navigation_apitest.cc
index db6af8b..780c4abf 100644
--- a/chrome/browser/extensions/api/web_navigation/web_navigation_apitest.cc
+++ b/chrome/browser/extensions/api/web_navigation/web_navigation_apitest.cc
@@ -225,7 +225,8 @@
   WebNavigationApiBackForwardCacheTest() {
     feature_list_.InitWithFeaturesAndParameters(
         {{features::kBackForwardCache,
-          {{"content_injection_supported", "true"}}}},
+          {{"content_injection_supported", "true"},
+           {"all_extensions_allowed", "true"}}}},
         {features::kBackForwardCacheMemoryControls});
   }
   ~WebNavigationApiBackForwardCacheTest() override = default;
diff --git a/chrome/browser/extensions/back_forward_cache_browsertest.cc b/chrome/browser/extensions/back_forward_cache_browsertest.cc
index c1d1369..5680db9e 100644
--- a/chrome/browser/extensions/back_forward_cache_browsertest.cc
+++ b/chrome/browser/extensions/back_forward_cache_browsertest.cc
@@ -14,12 +14,18 @@
 class ExtensionBackForwardCacheBrowserTest : public ExtensionBrowserTest {
  public:
   explicit ExtensionBackForwardCacheBrowserTest(
+      bool all_extensions_allowed = true,
       bool allow_content_scripts = true) {
+    // If `allow_content_scripts` is true then `all_extensions_allowed` must
+    // also be true.
+    DCHECK(!(allow_content_scripts && !all_extensions_allowed));
     feature_list_.InitWithFeaturesAndParameters(
         {{features::kBackForwardCache,
           {{"content_injection_supported",
             allow_content_scripts ? "true" : "false"},
-           {"TimeToLiveInBackForwardCacheInSeconds", "3600"}}}},
+           {"TimeToLiveInBackForwardCacheInSeconds", "3600"},
+           {"all_extensions_allowed",
+            all_extensions_allowed ? "true" : "false"}}}},
         {features::kBackForwardCacheMemoryControls});
   }
 
@@ -32,18 +38,30 @@
   base::test::ScopedFeatureList feature_list_;
 };
 
+// Test that does not allow content scripts to be injected.
 class ExtensionBackForwardCacheContentScriptDisabledBrowserTest
     : public ExtensionBackForwardCacheBrowserTest {
  public:
   ExtensionBackForwardCacheContentScriptDisabledBrowserTest()
-      : ExtensionBackForwardCacheBrowserTest(/*allow_content_scripts=*/false) {}
+      : ExtensionBackForwardCacheBrowserTest(/*all_extensions_allowed*/ true,
+                                             /*allow_content_scripts=*/false) {}
 };
 
-IN_PROC_BROWSER_TEST_F(
-    ExtensionBackForwardCacheContentScriptDisabledBrowserTest,
-    ScriptDisallowed) {
-  ASSERT_TRUE(LoadExtension(test_data_dir_.AppendASCII("back_forward_cache")
-                                .AppendASCII("content_script")));
+// Test that causes non-component extensions to disable back forward cache.
+class ExtensionBackForwardCacheExtensionsDisabledBrowserTest
+    : public ExtensionBackForwardCacheBrowserTest {
+ public:
+  ExtensionBackForwardCacheExtensionsDisabledBrowserTest()
+      : ExtensionBackForwardCacheBrowserTest(/*all_extensions_allowed*/ false,
+                                             /*allow_content_scripts*/ false) {}
+};
+
+// Tests that a non-component extension that is installed prevents back forward
+// cache.
+IN_PROC_BROWSER_TEST_F(ExtensionBackForwardCacheExtensionsDisabledBrowserTest,
+                       ScriptDisallowed) {
+  ASSERT_TRUE(LoadExtension(test_data_dir_.AppendASCII("trivial_extension")
+                                .AppendASCII("extension")));
 
   ASSERT_TRUE(embedded_test_server()->Start());
   GURL url_a(embedded_test_server()->GetURL("a.com", "/title1.html"));
@@ -59,7 +77,39 @@
       ui_test_utils::NavigateToURL(browser(), url_b);
   content::RenderFrameDeletedObserver delete_observer_rfh_b(rfh_b);
 
-  // Expect that |rfh_a| is destroyed as it wouldn't be placed in the cache.
+  // Expect that `rfh_a` is destroyed as it wouldn't be placed in the cache
+  // since there is an active non-component loaded extension.
+  EXPECT_TRUE(delete_observer_rfh_a.deleted());
+}
+
+// Test content script injection disallow the back forward cache.
+IN_PROC_BROWSER_TEST_F(
+    ExtensionBackForwardCacheContentScriptDisabledBrowserTest,
+    ScriptDisallowed) {
+  ASSERT_TRUE(LoadExtension(test_data_dir_.AppendASCII("back_forward_cache")
+                                .AppendASCII("content_script")));
+
+  ASSERT_TRUE(embedded_test_server()->Start());
+  GURL url_a(embedded_test_server()->GetURL("a.com", "/title1.html"));
+  GURL url_b(embedded_test_server()->GetURL("b.com", "/title1.html"));
+
+  std::u16string expected_title = u"modified";
+  content::TitleWatcher title_watcher(
+      browser()->tab_strip_model()->GetActiveWebContents(), expected_title);
+
+  // 1) Navigate to A.
+  content::RenderFrameHost* rfh_a =
+      ui_test_utils::NavigateToURL(browser(), url_a);
+  content::RenderFrameDeletedObserver delete_observer_rfh_a(rfh_a);
+  EXPECT_EQ(expected_title, title_watcher.WaitAndGetTitle());
+
+  // 2) Navigate to B.
+  content::RenderFrameHost* rfh_b =
+      ui_test_utils::NavigateToURL(browser(), url_b);
+  content::RenderFrameDeletedObserver delete_observer_rfh_b(rfh_b);
+
+  // Expect that `rfh_a` is destroyed as it wouldn't be placed in the cache
+  // since the active extension injected content_scripts.
   delete_observer_rfh_a.WaitUntilDeleted();
 }
 
@@ -81,7 +131,7 @@
       ui_test_utils::NavigateToURL(browser(), url_b);
   content::RenderFrameDeletedObserver delete_observer_rfh_b(rfh_b);
 
-  // Ensure that |rfh_a| is in the cache.
+  // Ensure that `rfh_a` is in the cache.
   EXPECT_FALSE(delete_observer_rfh_a.deleted());
   EXPECT_NE(rfh_a, rfh_b);
   EXPECT_EQ(rfh_a->GetLifecycleState(),
@@ -108,7 +158,7 @@
       ui_test_utils::NavigateToURL(browser(), url_b);
   content::RenderFrameDeletedObserver delete_observer_rfh_b(rfh_b);
 
-  // Expect that |rfh_a| is destroyed as it wouldn't be placed in the cache.
+  // Expect that `rfh_a` is destroyed as it wouldn't be placed in the cache.
   delete_observer_rfh_a.WaitUntilDeleted();
 }
 
@@ -130,7 +180,7 @@
       ui_test_utils::NavigateToURL(browser(), url_b);
   content::RenderFrameDeletedObserver delete_observer_rfh_b(rfh_b);
 
-  // Ensure that |rfh_a| is in the cache.
+  // Ensure that `rfh_a` is in the cache.
   EXPECT_FALSE(delete_observer_rfh_a.deleted());
   EXPECT_NE(rfh_a, rfh_b);
   EXPECT_EQ(rfh_a->GetLifecycleState(),
@@ -159,7 +209,7 @@
       ui_test_utils::NavigateToURL(browser(), url_b);
   content::RenderFrameDeletedObserver delete_observer_rfh_b(rfh_b);
 
-  // Ensure that |rfh_a| is in the cache.
+  // Ensure that `rfh_a` is in the cache.
   EXPECT_FALSE(delete_observer_rfh_a.deleted());
   EXPECT_NE(rfh_a, rfh_b);
   EXPECT_EQ(rfh_a->GetLifecycleState(),
@@ -168,7 +218,7 @@
   // Now unload the extension after something is in the cache.
   UnloadExtension(extension->id());
 
-  // Expect that |rfh_a| is destroyed as it should be cleared from the cache.
+  // Expect that `rfh_a` is destroyed as it should be cleared from the cache.
   delete_observer_rfh_a.WaitUntilDeleted();
 }
 
@@ -188,7 +238,7 @@
       ui_test_utils::NavigateToURL(browser(), url_b);
   content::RenderFrameDeletedObserver delete_observer_rfh_b(rfh_b);
 
-  // Ensure that |rfh_a| is in the cache.
+  // Ensure that `rfh_a` is in the cache.
   EXPECT_FALSE(delete_observer_rfh_a.deleted());
   EXPECT_NE(rfh_a, rfh_b);
   EXPECT_EQ(rfh_a->GetLifecycleState(),
@@ -198,7 +248,7 @@
   ASSERT_TRUE(LoadExtension(test_data_dir_.AppendASCII("back_forward_cache")
                                 .AppendASCII("content_css")));
 
-  // Expect that |rfh_a| is destroyed as it should be cleared from the cache.
+  // Expect that `rfh_a` is destroyed as it should be cleared from the cache.
   delete_observer_rfh_a.WaitUntilDeleted();
 }
 
diff --git a/chrome/browser/extensions/tab_helper.cc b/chrome/browser/extensions/tab_helper.cc
index bb6309a..8d6a451 100644
--- a/chrome/browser/extensions/tab_helper.cc
+++ b/chrome/browser/extensions/tab_helper.cc
@@ -33,6 +33,7 @@
 #include "chrome/common/extensions/extension_constants.h"
 #include "chrome/common/extensions/manifest_handlers/app_launch_info.h"
 #include "chrome/common/url_constants.h"
+#include "components/back_forward_cache/back_forward_cache_disable.h"
 #include "components/sessions/content/session_tab_helper.h"
 #include "content/public/browser/back_forward_cache.h"
 #include "content/public/browser/invalidate_type.h"
@@ -45,6 +46,7 @@
 #include "content/public/browser/render_process_host.h"
 #include "content/public/browser/render_view_host.h"
 #include "content/public/browser/web_contents.h"
+#include "content/public/common/content_features.h"
 #include "extensions/browser/api/declarative/rules_registry_service.h"
 #include "extensions/browser/api/declarative_net_request/web_contents_helper.h"
 #include "extensions/browser/disable_reason.h"
@@ -59,6 +61,7 @@
 #include "extensions/common/extension_resource.h"
 #include "extensions/common/extension_urls.h"
 #include "extensions/common/feature_switch.h"
+#include "extensions/common/manifest.h"
 #include "extensions/common/manifest_handlers/icons_handler.h"
 #include "extensions/common/permissions/api_permission.h"
 #include "services/service_manager/public/cpp/interface_provider.h"
@@ -78,6 +81,9 @@
 
 namespace {
 
+// User data key for caching if bfcache is disabled.
+const char kIsBFCacheDisabledKey[] = "extensions.backforward.browsercontext";
+
 // These values are persisted to logs. Entries should not be renumbered and
 // numeric values should never be reused.
 enum class ExtensionPermissionsOnLoad {
@@ -148,6 +154,58 @@
   RecordPermission(ExtensionPermissionsOnLoad::kTotal);
 }
 
+bool AreAllExtensionsAllowedForBFCache() {
+  // If back forward cache is disabled, indicate we accept everything.
+  if (!content::BackForwardCache::IsBackForwardCacheFeatureEnabled())
+    return true;
+
+  static base::FeatureParam<bool> all_extensions_allowed(
+      &features::kBackForwardCache, "all_extensions_allowed", false);
+  return all_extensions_allowed.Get();
+}
+
+void DisableBackForwardCacheIfNecessary(
+    const ExtensionSet& enabled_extensions,
+    content::BrowserContext* context,
+    content::NavigationHandle* navigation_handle) {
+  // If we allow all extensions for bfcache then just return.
+  if (AreAllExtensionsAllowedForBFCache())
+    return;
+
+  bool disable_bfcache = false;
+  // If the user data exists we know we are disabled.
+  if (context->GetUserData(kIsBFCacheDisabledKey)) {
+    disable_bfcache = true;
+  } else {
+    // Compute whether we need to disable it.
+    for (const auto& extension : enabled_extensions) {
+      // Skip component extensions.
+      if (Manifest::IsComponentLocation(extension->location())) {
+        continue;
+      }
+      if (util::IsExtensionVisibleToContext(*extension, context)) {
+        // Set a user data key indicating we've disabled disabled bfcache for
+        // this context.
+        context->SetUserData(kIsBFCacheDisabledKey,
+                             std::make_unique<base::SupportsUserData::Data>());
+
+        disable_bfcache = true;
+        break;
+      }
+    }
+  }
+
+  if (disable_bfcache) {
+    // We do not care if GetPreviousRenderFrameHostId returns a reused
+    // RenderFrameHost since disabling the cache multiple times has no side
+    // effects.
+    content::BackForwardCache::DisableForRenderFrameHost(
+        navigation_handle->GetPreviousRenderFrameHostId(),
+        back_forward_cache::DisabledReason(
+            back_forward_cache::DisabledReasonId::kExtensions));
+  }
+}
+
 }  // namespace
 
 TabHelper::~TabHelper() = default;
@@ -287,6 +345,9 @@
       sessions::SessionTabHelper::IdForTab(web_contents()).id(),
       navigation_handle->GetURL());
 
+  DisableBackForwardCacheIfNecessary(enabled_extensions, context,
+                                     navigation_handle);
+
   Browser* browser = chrome::FindBrowserWithWebContents(web_contents());
   if (browser && browser->deprecated_is_app()) {
     const Extension* extension = registry->GetExtensionById(
diff --git a/chrome/browser/feed/android/java/src/org/chromium/chrome/browser/feed/feedmanagement/FeedManagementCoordinator.java b/chrome/browser/feed/android/java/src/org/chromium/chrome/browser/feed/feedmanagement/FeedManagementCoordinator.java
index 7fd63fa..737b179 100644
--- a/chrome/browser/feed/android/java/src/org/chromium/chrome/browser/feed/feedmanagement/FeedManagementCoordinator.java
+++ b/chrome/browser/feed/android/java/src/org/chromium/chrome/browser/feed/feedmanagement/FeedManagementCoordinator.java
@@ -4,10 +4,11 @@
 
 package org.chromium.chrome.browser.feed.feedmanagement;
 
-import android.content.Context;
+import android.app.Activity;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.widget.ListView;
+import android.widget.TextView;
 
 import org.chromium.chrome.browser.feed.feedmanagement.FeedManagementMediator.FollowManagementLauncher;
 import org.chromium.chrome.browser.feed.webfeed.R;
@@ -20,10 +21,12 @@
  */
 public class FeedManagementCoordinator {
     private FeedManagementMediator mMediator;
+    private Activity mActivity;
     private final View mView;
 
     public FeedManagementCoordinator(
-            Context context, FollowManagementLauncher followManagementLauncher) {
+            Activity activity, FollowManagementLauncher followManagementLauncher) {
+        mActivity = activity;
         ModelList listItems = new ModelList();
 
         // Once this is attached to the ListView, there is no need to hold a reference to it.
@@ -33,14 +36,23 @@
                 FeedManagementItemViewBinder::bind);
 
         // Inflate the XML.
-        mView = LayoutInflater.from(context).inflate(R.layout.feed_management_activity, null);
+        mView = LayoutInflater.from(mActivity).inflate(R.layout.feed_management_activity, null);
         ListView listView = (ListView) mView.findViewById(R.id.feed_management_menu);
         listView.setAdapter(adapter);
 
-        mMediator = new FeedManagementMediator(context, listItems, followManagementLauncher);
+        // Set up a handler for the header to act as a back button.
+        TextView headerView = (TextView) mView.findViewById(R.id.feed_management_page_title);
+        headerView.setOnClickListener(this::handleHeaderClick);
+
+        mMediator = new FeedManagementMediator(mActivity, listItems, followManagementLauncher);
     }
 
     public View getView() {
         return mView;
     }
+
+    private void handleHeaderClick(View view) {
+        // Navigate back.
+        mActivity.finish();
+    }
 }
diff --git a/chrome/browser/feed/android/java/src/org/chromium/chrome/browser/feed/followmanagement/FollowManagementCoordinator.java b/chrome/browser/feed/android/java/src/org/chromium/chrome/browser/feed/followmanagement/FollowManagementCoordinator.java
index 27f90e1..4f258cd2 100644
--- a/chrome/browser/feed/android/java/src/org/chromium/chrome/browser/feed/followmanagement/FollowManagementCoordinator.java
+++ b/chrome/browser/feed/android/java/src/org/chromium/chrome/browser/feed/followmanagement/FollowManagementCoordinator.java
@@ -4,9 +4,10 @@
 
 package org.chromium.chrome.browser.feed.followmanagement;
 
-import android.content.Context;
+import android.app.Activity;
 import android.view.LayoutInflater;
 import android.view.View;
+import android.widget.TextView;
 
 import androidx.recyclerview.widget.LinearLayoutManager;
 import androidx.recyclerview.widget.RecyclerView;
@@ -23,9 +24,11 @@
  */
 public class FollowManagementCoordinator {
     private FollowManagementMediator mMediator;
+    private Activity mActivity;
     private final View mView;
 
-    public FollowManagementCoordinator(Context context) {
+    public FollowManagementCoordinator(Activity activity) {
+        mActivity = activity;
         ModelList listItems = new ModelList();
 
         SimpleRecyclerViewAdapter adapter = new SimpleRecyclerViewAdapter(listItems);
@@ -34,18 +37,27 @@
                 FollowManagementItemViewBinder::bind);
 
         // Inflate the XML for the activity.
-        mView = LayoutInflater.from(context).inflate(R.layout.follow_management_activity, null);
+        mView = LayoutInflater.from(activity).inflate(R.layout.follow_management_activity, null);
         RecyclerView recyclerView = (RecyclerView) mView.findViewById(R.id.follow_management_list);
         // With the recycler view, we need to explicitly set a layout manager.
-        LinearLayoutManager manager = new LinearLayoutManager(context);
+        LinearLayoutManager manager = new LinearLayoutManager(activity);
         recyclerView.setLayoutManager(manager);
         recyclerView.setAdapter(adapter);
 
-        mMediator = new FollowManagementMediator(context, listItems, adapter,
+        // Set up a handler for the header to act as a back button.
+        TextView headerView = (TextView) mView.findViewById(R.id.follow_management_page_title);
+        headerView.setOnClickListener(this::handleHeaderClick);
+
+        mMediator = new FollowManagementMediator(activity, listItems, adapter,
                 new LargeIconBridge(Profile.getLastUsedRegularProfile()));
     }
 
     public View getView() {
         return mView;
     }
+
+    private void handleHeaderClick(View view) {
+        // Navigate back.
+        mActivity.finish();
+    }
 }
diff --git a/chrome/browser/flag-metadata.json b/chrome/browser/flag-metadata.json
index 2329d35..52a52d9 100644
--- a/chrome/browser/flag-metadata.json
+++ b/chrome/browser/flag-metadata.json
@@ -1306,11 +1306,6 @@
     "expiry_milestone": 88
   },
   {
-    "name": "enable-app-list-search-autocomplete",
-    "owners": [ "newcomer" ],
-    "expiry_milestone": 75
-  },
-  {
     "name": "enable-arc-unified-audio-focus",
     "owners": [ "beccahughes", "media-dev" ],
     "expiry_milestone": 82
@@ -4850,11 +4845,6 @@
     "expiry_milestone" : 92
   },
   {
-    "name" : "side-panel-prototype",
-    "owners": [ "chrome-desktop-ui-sea@google.com", "johntlee" ],
-    "expiry_milestone" : 92
-  },
-  {
     "name": "silent-debugger-extension-api",
     "owners": [ "//extensions/OWNERS" ],
     "expiry_milestone": 77
diff --git a/chrome/browser/flag_descriptions.cc b/chrome/browser/flag_descriptions.cc
index 5e18961..0d87173 100644
--- a/chrome/browser/flag_descriptions.cc
+++ b/chrome/browser/flag_descriptions.cc
@@ -2201,10 +2201,6 @@
 const char kSidePanelName[] = "Side panel";
 const char kSidePanelDescription[] = "Host some content in a side panel.";
 
-const char kSidePanelPrototypeName[] = "Side panel prototype";
-const char kSidePanelPrototypeDescription[] =
-    "Display a prototype of the side panel.";
-
 const char kServiceWorkerSubresourceFilterName[] =
     "ServiceWorker subresource filter";
 const char kServiceWorkerSubresourceFilterDescription[] =
@@ -4178,12 +4174,6 @@
 const char kEnableAppGridGhostDescription[] =
     "Enables ghosting during an item drag in launcher.";
 
-const char kEnableAppListSearchAutocompleteName[] =
-    "App List Search Autocomplete";
-const char kEnableAppListSearchAutocompleteDescription[] =
-    "Allow App List search box to autocomplete queries for Google searches and "
-    "apps.";
-
 const char kEnableArcUnifiedAudioFocusName[] =
     "Enable unified audio focus on ARC";
 const char kEnableArcUnifiedAudioFocusDescription[] =
diff --git a/chrome/browser/flag_descriptions.h b/chrome/browser/flag_descriptions.h
index 8f95f36..b4d8a5a 100644
--- a/chrome/browser/flag_descriptions.h
+++ b/chrome/browser/flag_descriptions.h
@@ -1273,9 +1273,6 @@
 extern const char kSidePanelName[];
 extern const char kSidePanelDescription[];
 
-extern const char kSidePanelPrototypeName[];
-extern const char kSidePanelPrototypeDescription[];
-
 extern const char kServiceWorkerSubresourceFilterName[];
 extern const char kServiceWorkerSubresourceFilterDescription[];
 
@@ -2408,9 +2405,6 @@
 extern const char kEnableAppGridGhostName[];
 extern const char kEnableAppGridGhostDescription[];
 
-extern const char kEnableAppListSearchAutocompleteName[];
-extern const char kEnableAppListSearchAutocompleteDescription[];
-
 extern const char kEnableAppReinstallZeroStateName[];
 extern const char kEnableAppReinstallZeroStateDescription[];
 
diff --git a/chrome/browser/media/cast_mirroring_service_host.cc b/chrome/browser/media/cast_mirroring_service_host.cc
index c0c1667..251a9dc 100644
--- a/chrome/browser/media/cast_mirroring_service_host.cc
+++ b/chrome/browser/media/cast_mirroring_service_host.cc
@@ -263,8 +263,8 @@
 
 void CastMirroringServiceHost::BindGpu(
     mojo::PendingReceiver<viz::mojom::Gpu> receiver) {
-  gpu_client_ = content::CreateGpuClient(std::move(receiver), base::DoNothing(),
-                                         content::GetIOThreadTaskRunner({}));
+  gpu_client_ =
+      content::CreateGpuClient(std::move(receiver), base::DoNothing());
 }
 
 void CastMirroringServiceHost::GetVideoCaptureHost(
diff --git a/chrome/browser/media/encrypted_media_browsertest.cc b/chrome/browser/media/encrypted_media_browsertest.cc
index d21475a..fda084e3 100644
--- a/chrome/browser/media/encrypted_media_browsertest.cc
+++ b/chrome/browser/media/encrypted_media_browsertest.cc
@@ -469,14 +469,11 @@
     // TODO(xhwang): Even when config change or playback is not supported we
     // still start Chrome only to return directly here. We probably should not
     // run these test cases at all. See http://crbug.com/693288
-    if (CurrentSourceType() != SrcType::MSE) {
-      DVLOG(0) << "Config change only happens when using MSE.";
-      return;
-    }
-    if (!IsPlayBackPossible(CurrentKeySystem())) {
-      DVLOG(0) << "Skipping test - ConfigChange test requires video playback.";
-      return;
-    }
+    if (CurrentSourceType() != SrcType::MSE)
+      GTEST_SKIP() << "Config change only happens when using MSE.";
+
+    if (!IsPlayBackPossible(CurrentKeySystem()))
+      GTEST_SKIP() << "ConfigChange test requires video playback.";
 
     base::StringPairs query_params;
     query_params.emplace_back("keySystem", CurrentKeySystem());
@@ -505,10 +502,8 @@
   void TestDifferentContainers(const std::string& video_media_file,
                                const std::string& audio_media_file) {
     // MP4 without MSE is not support yet, http://crbug.com/170793.
-    if (CurrentSourceType() != SrcType::MSE) {
-      DVLOG(0) << "Skipping test; Can only play MP4 encrypted streams by MSE.";
-      return;
-    }
+    if (CurrentSourceType() != SrcType::MSE)
+      GTEST_SKIP() << "Can only play MP4 encrypted streams by MSE.";
 
     RunEncryptedMediaMultipleFileTest(CurrentKeySystem(), video_media_file,
                                       audio_media_file, media::kEnded);
@@ -590,28 +585,25 @@
 }
 
 IN_PROC_BROWSER_TEST_P(EncryptedMediaTest, Playback_Multiple_VideoAudio_WebM) {
-  if (!IsPlayBackPossible(CurrentKeySystem())) {
-    DVLOG(0) << "Skipping test - Playback_Multiple test requires playback.";
-    return;
-  }
+  if (!IsPlayBackPossible(CurrentKeySystem()))
+    GTEST_SKIP() << "Playback_Multiple test requires playback.";
+
   TestMultiplePlayback("bear-320x240-av_enc-av.webm");
 }
 
 IN_PROC_BROWSER_TEST_P(EncryptedMediaTest, Playback_AudioOnly_MP4_FLAC) {
   // MP4 without MSE is not support yet, http://crbug.com/170793.
-  if (CurrentSourceType() != SrcType::MSE) {
-    DVLOG(0) << "Skipping test; Can only play MP4 encrypted streams by MSE.";
-    return;
-  }
+  if (CurrentSourceType() != SrcType::MSE)
+    GTEST_SKIP() << "Can only play MP4 encrypted streams by MSE.";
+
   TestSimplePlayback("bear-flac-cenc.mp4");
 }
 
 IN_PROC_BROWSER_TEST_P(EncryptedMediaTest, Playback_AudioOnly_MP4_OPUS) {
   // MP4 without MSE is not support yet, http://crbug.com/170793.
-  if (CurrentSourceType() != SrcType::MSE) {
-    DVLOG(0) << "Skipping test; Can only play MP4 encrypted streams by MSE.";
-    return;
-  }
+  if (CurrentSourceType() != SrcType::MSE)
+    GTEST_SKIP() << "Can only play MP4 encrypted streams by MSE.";
+
   TestSimplePlayback("bear-opus-cenc.mp4");
 }
 
@@ -619,10 +611,9 @@
 IN_PROC_BROWSER_TEST_P(EncryptedMediaTest,
                        DISABLED_Playback_VideoOnly_MP4_VP9) {
   // MP4 without MSE is not support yet, http://crbug.com/170793.
-  if (CurrentSourceType() != SrcType::MSE) {
-    DVLOG(0) << "Skipping test; Can only play MP4 encrypted streams by MSE.";
-    return;
-  }
+  if (CurrentSourceType() != SrcType::MSE)
+    GTEST_SKIP() << "Can only play MP4 encrypted streams by MSE.";
+
   TestSimplePlayback("bear-320x240-v_frag-vp9-cenc.mp4");
 }
 
@@ -633,10 +624,9 @@
 
 IN_PROC_BROWSER_TEST_P(EncryptedMediaTest, Playback_VideoOnly_MP4_VP9Profile2) {
   // MP4 without MSE is not support yet, http://crbug.com/170793.
-  if (CurrentSourceType() != SrcType::MSE) {
-    DVLOG(0) << "Skipping test; Can only play MP4 encrypted streams by MSE.";
-    return;
-  }
+  if (CurrentSourceType() != SrcType::MSE)
+    GTEST_SKIP() << "Can only play MP4 encrypted streams by MSE.";
+
   TestSimplePlayback("bear-320x240-v-vp9_profile2_subsample_cenc-v.mp4");
 }
 
@@ -651,19 +641,17 @@
 
 IN_PROC_BROWSER_TEST_P(EncryptedMediaTest, Playback_VideoOnly_MP4_AV1) {
   // MP4 without MSE is not support yet, http://crbug.com/170793.
-  if (CurrentSourceType() != SrcType::MSE) {
-    DVLOG(0) << "Skipping test; Can only play MP4 encrypted streams by MSE.";
-    return;
-  }
+  if (CurrentSourceType() != SrcType::MSE)
+    GTEST_SKIP() << "Can only play MP4 encrypted streams by MSE.";
+
   TestSimplePlayback("bear-av1-cenc.mp4");
 }
 
 IN_PROC_BROWSER_TEST_P(EncryptedMediaTest, Playback_VideoOnly_MP4_AV1_10bit) {
   // MP4 without MSE is not support yet, http://crbug.com/170793.
-  if (CurrentSourceType() != SrcType::MSE) {
-    DVLOG(0) << "Skipping test; Can only play MP4 encrypted streams by MSE.";
-    return;
-  }
+  if (CurrentSourceType() != SrcType::MSE)
+    GTEST_SKIP() << "Can only play MP4 encrypted streams by MSE.";
+
   TestSimplePlayback("bear-av1-320x180-10bit-cenc.mp4");
 }
 #endif  // BUILDFLAG(ENABLE_AV1_DECODER)
@@ -694,36 +682,28 @@
 }
 
 IN_PROC_BROWSER_TEST_P(EncryptedMediaTest, FrameSizeChangeVideo) {
-  if (!IsPlayBackPossible(CurrentKeySystem())) {
-    DVLOG(0) << "Skipping test - FrameSizeChange test requires video playback.";
-    return;
-  }
+  if (!IsPlayBackPossible(CurrentKeySystem()))
+    GTEST_SKIP() << "FrameSizeChange test requires video playback.";
+
   TestFrameSizeChange();
 }
 
 IN_PROC_BROWSER_TEST_P(EncryptedMediaTest, PolicyCheck) {
   // There is no need to run this test twice for the same key system.
-  if (CurrentSourceType() != SrcType::MSE) {
-    DVLOG(0) << "Skipping test.";
-    return;
-  }
+  if (CurrentSourceType() != SrcType::MSE)
+    GTEST_SKIP();
 
   TestPolicyCheck();
 }
 
 IN_PROC_BROWSER_TEST_P(EncryptedMediaTest, RemoveTemporarySession) {
-  if (!IsPlayBackPossible(CurrentKeySystem())) {
-    DVLOG(0) << "Skipping test - RemoveTemporarySession test requires license "
-                "server.";
-    return;
-  }
+  if (!IsPlayBackPossible(CurrentKeySystem()))
+    GTEST_SKIP() << "RemoveTemporarySession test requires license server.";
 
   // Although this test doesn't play anything, there is no need to run it
   // twice for the same key system.
-  if (CurrentSourceType() != SrcType::MSE) {
-    DVLOG(0) << "Skipping test.";
-    return;
-  }
+  if (CurrentSourceType() != SrcType::MSE)
+    GTEST_SKIP();
 
   base::StringPairs query_params{{"keySystem", CurrentKeySystem()}};
   RunEncryptedMediaTestPage("eme_remove_session_test.html", CurrentKeySystem(),
@@ -747,27 +727,23 @@
 #if BUILDFLAG(USE_PROPRIETARY_CODECS)
 IN_PROC_BROWSER_TEST_P(EncryptedMediaTest, Playback_VideoOnly_MP4) {
   // MP4 without MSE is not support yet, http://crbug.com/170793.
-  if (CurrentSourceType() != SrcType::MSE) {
-    DVLOG(0) << "Skipping test; Can only play MP4 encrypted streams by MSE.";
-    return;
-  }
+  if (CurrentSourceType() != SrcType::MSE)
+    GTEST_SKIP() << "Can only play MP4 encrypted streams by MSE.";
+
   TestSimplePlayback("bear-640x360-v_frag-cenc.mp4");
 }
 
 IN_PROC_BROWSER_TEST_P(EncryptedMediaTest, Playback_VideoOnly_MP4_MDAT) {
   // MP4 without MSE is not support yet, http://crbug.com/170793.
-  if (CurrentSourceType() != SrcType::MSE) {
-    DVLOG(0) << "Skipping test; Can only play MP4 encrypted streams by MSE.";
-    return;
-  }
+  if (CurrentSourceType() != SrcType::MSE)
+    GTEST_SKIP() << "Can only play MP4 encrypted streams by MSE.";
+
   TestSimplePlayback("bear-640x360-v_frag-cenc-mdat.mp4");
 }
 
 IN_PROC_BROWSER_TEST_P(EncryptedMediaTest, Playback_Encryption_CBCS) {
-  if (CurrentSourceType() != SrcType::MSE) {
-    DVLOG(0) << "Skipping test; Can only play MP4 encrypted streams by MSE.";
-    return;
-  }
+  if (CurrentSourceType() != SrcType::MSE)
+    GTEST_SKIP() << "Can only play MP4 encrypted streams by MSE.";
 
   TestSimplePlayback("bear-640x360-v_frag-cbcs.mp4");
 }
diff --git a/chrome/browser/media/webrtc/media_stream_permission_browsertest.cc b/chrome/browser/media/webrtc/media_stream_permission_browsertest.cc
index 98f0dc71..ea7864ae 100644
--- a/chrome/browser/media/webrtc/media_stream_permission_browsertest.cc
+++ b/chrome/browser/media/webrtc/media_stream_permission_browsertest.cc
@@ -59,6 +59,44 @@
     return LoadTestPageInBrowser(CreateIncognitoBrowser());
   }
 
+  void TestPermissionDenialEffectOnStream(std::string constraints,
+                                          ContentSettingsType setting_to_clear,
+                                          bool should_video_stop) {
+    HostContentSettingsMap* settings_map =
+        HostContentSettingsMapFactory::GetForProfile(browser()->profile());
+
+    content::WebContents* tab_contents = LoadTestPageInTab();
+
+    EXPECT_TRUE(GetUserMediaWithSpecificConstraintsAndAcceptIfPrompted(
+        tab_contents, constraints));
+
+    StartDetectingVideo(tab_contents, "local-view");
+    {
+      SCOPED_TRACE("Wait for video to play");
+      EXPECT_TRUE(WaitForVideoToPlay(tab_contents));
+    }
+
+    settings_map->ClearSettingsForOneType(setting_to_clear);
+
+    // Let all the cross-thread tasks do their work.
+    base::RunLoop().RunUntilIdle();
+
+    StartDetectingVideo(tab_contents, "local-view");
+
+    if (should_video_stop) {
+      SCOPED_TRACE("Wait for video to stop");
+      EXPECT_TRUE(WaitForVideoToStop(tab_contents));
+    } else {
+      SCOPED_TRACE("Wait for video to play 2");
+      EXPECT_TRUE(WaitForVideoToPlay(tab_contents));
+    }
+
+    // Clean up settings.
+    settings_map->ClearSettingsForOneType(ContentSettingsType::MEDIASTREAM_MIC);
+    settings_map->ClearSettingsForOneType(
+        ContentSettingsType::MEDIASTREAM_CAMERA);
+  }
+
   // Returns the URL of the main test page.
   GURL test_page_url() const {
     const char kMainWebrtcTestHtmlPage[] = "/webrtc/webrtc_jsep01_test.html";
@@ -174,56 +212,29 @@
 // due to a bug in the code. Now that the bug is fixed, the test is not passing
 // anymore.
 IN_PROC_BROWSER_TEST_F(MediaStreamPermissionTest,
-                       DISABLED_DenyingPermissionStopsStreamWhenRelevant) {
-  struct {
-    std::string constraints;
-    ContentSettingsType setting_to_clear;
-    bool should_video_stop;
-  } kTests[] = {
-      {kAudioVideoCallConstraints, ContentSettingsType::MEDIASTREAM_CAMERA,
-       true},
-      {kAudioVideoCallConstraints, ContentSettingsType::MEDIASTREAM_MIC, true},
-      {kVideoOnlyCallConstraints, ContentSettingsType::MEDIASTREAM_CAMERA,
-       true},
-      {kVideoOnlyCallConstraints, ContentSettingsType::MEDIASTREAM_MIC, false},
-  };
+                       DenyingCameraPermissionStopsAVStream) {
+  TestPermissionDenialEffectOnStream(kAudioVideoCallConstraints,
+                                     ContentSettingsType::MEDIASTREAM_CAMERA,
+                                     true /* should_video_stop */);
+}
 
-  HostContentSettingsMap* settings_map =
-      HostContentSettingsMapFactory::GetForProfile(browser()->profile());
+IN_PROC_BROWSER_TEST_F(MediaStreamPermissionTest,
+                       DenyingMicPermissionStopsAVStream) {
+  TestPermissionDenialEffectOnStream(kAudioVideoCallConstraints,
+                                     ContentSettingsType::MEDIASTREAM_MIC,
+                                     true /* should_video_stop */);
+}
 
-  for (size_t test_number = 0; test_number < base::size(kTests);
-       ++test_number) {
-    const auto& kTest = kTests[test_number];
-    SCOPED_TRACE(testing::Message() << "Test: " << test_number);
-    content::WebContents* tab_contents = LoadTestPageInTab();
+IN_PROC_BROWSER_TEST_F(MediaStreamPermissionTest,
+                       DenyingCameraPermissionStopsVideoOnlyStream) {
+  TestPermissionDenialEffectOnStream(kVideoOnlyCallConstraints,
+                                     ContentSettingsType::MEDIASTREAM_CAMERA,
+                                     true /* should_video_stop */);
+}
 
-    EXPECT_TRUE(GetUserMediaWithSpecificConstraintsAndAcceptIfPrompted(
-        tab_contents, kTest.constraints));
-
-    StartDetectingVideo(tab_contents, "local-view");
-    {
-      SCOPED_TRACE("Wait for video to play");
-      EXPECT_TRUE(WaitForVideoToPlay(tab_contents));
-    }
-
-    settings_map->ClearSettingsForOneType(kTest.setting_to_clear);
-
-    // Let all the cross-thread tasks do their work.
-    base::RunLoop().RunUntilIdle();
-
-    StartDetectingVideo(tab_contents, "local-view");
-
-    if (kTest.should_video_stop) {
-      SCOPED_TRACE("Wait for video to stop");
-      EXPECT_TRUE(WaitForVideoToStop(tab_contents));
-    } else {
-      SCOPED_TRACE("Wait for video to play 2");
-      EXPECT_TRUE(WaitForVideoToPlay(tab_contents));
-    }
-
-    // Clean up settings for the following tests.
-    settings_map->ClearSettingsForOneType(ContentSettingsType::MEDIASTREAM_MIC);
-    settings_map->ClearSettingsForOneType(
-        ContentSettingsType::MEDIASTREAM_CAMERA);
-  }
+IN_PROC_BROWSER_TEST_F(MediaStreamPermissionTest,
+                       DenyingMicPermissionDoesntStopVideoOnlyStream) {
+  TestPermissionDenialEffectOnStream(kVideoOnlyCallConstraints,
+                                     ContentSettingsType::MEDIASTREAM_MIC,
+                                     false /* should_video_stop */);
 }
diff --git a/chrome/browser/nearby_sharing/client/nearby_share_api_call_flow_impl.cc b/chrome/browser/nearby_sharing/client/nearby_share_api_call_flow_impl.cc
index dfb9082..2fabc75d 100644
--- a/chrome/browser/nearby_sharing/client/nearby_share_api_call_flow_impl.cc
+++ b/chrome/browser/nearby_sharing/client/nearby_share_api_call_flow_impl.cc
@@ -21,6 +21,8 @@
 const char kProtobufContentType[] = "application/x-protobuf";
 const char kQueryParameterAlternateOutputKey[] = "alt";
 const char kQueryParameterAlternateOutputProto[] = "proto";
+const char kPlatformTypeHeaderName[] = "X-Sharing-Platform-Type";
+const char kPlatformTypeHeaderValue[] = "OSType.CHROME_OS";
 
 }  // namespace
 
@@ -97,6 +99,14 @@
   return request_url_;
 }
 
+net::HttpRequestHeaders NearbyShareApiCallFlowImpl::CreateApiCallHeaders() {
+  // Inform the server that Chrome OS is making the request; this helps with
+  // diagnostics.
+  net::HttpRequestHeaders headers;
+  headers.SetHeader(kPlatformTypeHeaderName, kPlatformTypeHeaderValue);
+  return headers;
+}
+
 std::string NearbyShareApiCallFlowImpl::CreateApiCallBody() {
   return serialized_request_.value_or(std::string());
 }
diff --git a/chrome/browser/nearby_sharing/client/nearby_share_api_call_flow_impl.h b/chrome/browser/nearby_sharing/client/nearby_share_api_call_flow_impl.h
index 5662114..1f915a2 100644
--- a/chrome/browser/nearby_sharing/client/nearby_share_api_call_flow_impl.h
+++ b/chrome/browser/nearby_sharing/client/nearby_share_api_call_flow_impl.h
@@ -66,6 +66,7 @@
 
   // google_apis::OAuth2ApiCallFlow:
   GURL CreateApiCallUrl() override;
+  net::HttpRequestHeaders CreateApiCallHeaders() override;
   std::string CreateApiCallBody() override;
   std::string CreateApiCallBodyContentType() override;
   std::string GetRequestTypeForBody(const std::string& body) override;
diff --git a/chrome/browser/nearby_sharing/client/nearby_share_api_call_flow_impl_unittest.cc b/chrome/browser/nearby_sharing/client/nearby_share_api_call_flow_impl_unittest.cc
index c92f282..98763e1 100644
--- a/chrome/browser/nearby_sharing/client/nearby_share_api_call_flow_impl_unittest.cc
+++ b/chrome/browser/nearby_sharing/client/nearby_share_api_call_flow_impl_unittest.cc
@@ -137,6 +137,12 @@
     network_error_ = std::make_unique<NearbyShareHttpError>(network_error);
   }
 
+  void CheckPlatformTypeHeader(const net::HttpRequestHeaders& headers) {
+    std::string platform_type;
+    EXPECT_TRUE(headers.GetHeader("X-Sharing-Platform-Type", &platform_type));
+    EXPECT_EQ("OSType.CHROME_OS", platform_type);
+  }
+
   void CheckNearbySharingClientHttpPostRequest(
       const std::string& serialized_request) {
     const std::vector<network::TestURLLoaderFactory::PendingRequest>& pending =
@@ -144,6 +150,8 @@
     ASSERT_EQ(1u, pending.size());
     const network::ResourceRequest& request = pending[0].request;
 
+    CheckPlatformTypeHeader(request.headers);
+
     EXPECT_EQ(UrlWithQueryParameters(
                   kRequestUrl, base::nullopt /* request_as_query_parameters */),
               request.url);
@@ -165,6 +173,8 @@
     ASSERT_EQ(1u, pending.size());
     const network::ResourceRequest& request = pending[0].request;
 
+    CheckPlatformTypeHeader(request.headers);
+
     EXPECT_EQ(UrlWithQueryParameters(
                   kRequestUrl, base::nullopt /* request_as_query_parameters */),
               request.url);
@@ -187,6 +197,8 @@
     ASSERT_EQ(1u, pending.size());
     const network::ResourceRequest& request = pending[0].request;
 
+    CheckPlatformTypeHeader(request.headers);
+
     EXPECT_EQ(UrlWithQueryParameters(kRequestUrl, request_as_query_parameters),
               request.url);
 
diff --git a/chrome/browser/net/profile_network_context_service_browsertest.cc b/chrome/browser/net/profile_network_context_service_browsertest.cc
index 465a09f..627530b7 100644
--- a/chrome/browser/net/profile_network_context_service_browsertest.cc
+++ b/chrome/browser/net/profile_network_context_service_browsertest.cc
@@ -473,7 +473,7 @@
   // Cache directory should now exist.
   base::FilePath expected_cache_path =
       TempPath()
-          .Append(browser()->profile()->GetPath().BaseName())
+          .Append(browser()->profile()->GetBaseName())
           .Append(chrome::kCacheDirname);
   base::ScopedAllowBlockingForTesting allow_blocking;
   EXPECT_TRUE(base::PathExists(expected_cache_path));
diff --git a/chrome/browser/net/stub_resolver_config_reader.cc b/chrome/browser/net/stub_resolver_config_reader.cc
index 677f6c50..c5c6ef45 100644
--- a/chrome/browser/net/stub_resolver_config_reader.cc
+++ b/chrome/browser/net/stub_resolver_config_reader.cc
@@ -170,6 +170,9 @@
   pref_change_registrar_.Add(prefs::kDnsOverHttpsMode, pref_callback);
   pref_change_registrar_.Add(prefs::kDnsOverHttpsTemplates, pref_callback);
 
+  // TODO(crbug.com/1203427): Watch for `prefs::kAdditionalDnsQueryTypesEnabled`
+  // changes.
+
   parental_controls_delay_timer_.Start(
       FROM_HERE, kParentalControlsCheckDelay,
       base::BindOnce(&StubResolverConfigReader::OnParentalControlsDelayTimer,
@@ -196,6 +199,7 @@
   registry->RegisterBooleanPref(prefs::kBuiltInDnsClientEnabled, false);
   registry->RegisterStringPref(prefs::kDnsOverHttpsMode, std::string());
   registry->RegisterStringPref(prefs::kDnsOverHttpsTemplates, std::string());
+  registry->RegisterBooleanPref(prefs::kAdditionalDnsQueryTypesEnabled, true);
 }
 
 SecureDnsConfig StubResolverConfigReader::GetSecureDnsConfiguration(
@@ -382,6 +386,8 @@
   }
 
   if (update_network_service) {
+    // TODO(crbug.com/1203427): Configure for
+    // `prefs::kAdditionalDnsQueryTypesEnabled` value.
     content::GetNetworkService()->ConfigureStubHostResolver(
         GetInsecureStubResolverEnabled(), secure_dns_mode,
         std::move(servers_mojo));
diff --git a/chrome/browser/notifications/notification_platform_bridge.cc b/chrome/browser/notifications/notification_platform_bridge.cc
index f47b6e66..1b95b3d 100644
--- a/chrome/browser/notifications/notification_platform_bridge.cc
+++ b/chrome/browser/notifications/notification_platform_bridge.cc
@@ -16,9 +16,9 @@
   if (!profile)
     return "";
 #if defined(OS_WIN)
-  return base::WideToUTF8(profile->GetPath().BaseName().value());
+  return base::WideToUTF8(profile->GetBaseName().value());
 #elif defined(OS_POSIX)
-  return profile->GetPath().BaseName().value();
+  return profile->GetBaseName().value();
 #else
 #error "Not implemented for !OS_WIN && !OS_POSIX."
 #endif
diff --git a/chrome/browser/optimization_guide/page_content_annotations_service_browsertest.cc b/chrome/browser/optimization_guide/page_content_annotations_service_browsertest.cc
index 368550a8..7dc545094 100644
--- a/chrome/browser/optimization_guide/page_content_annotations_service_browsertest.cc
+++ b/chrome/browser/optimization_guide/page_content_annotations_service_browsertest.cc
@@ -119,7 +119,7 @@
  private:
   base::test::ScopedFeatureList scoped_feature_list_;
 };
-#endif
+#endif  // BUILDFLAG(BUILD_WITH_TFLITE_LIB)
 
 IN_PROC_BROWSER_TEST_F(PageContentAnnotationsServiceDisabledBrowserTest,
                        KeyedServiceEnabledButFeaturesDisabled) {
@@ -140,6 +140,10 @@
   }
   ~PageContentAnnotationsServiceBrowserTest() override = default;
 
+  void set_model_is_lazily_loaded(bool model_is_lazily_loaded) {
+    model_is_lazily_loaded_ = model_is_lazily_loaded;
+  }
+
   void SetUpOnMainThread() override {
     host_resolver()->AddRule("*", "127.0.0.1");
     InProcessBrowserTest::SetUpOnMainThread();
@@ -179,18 +183,25 @@
         ->OverrideTargetModelFileForTesting(
             proto::OPTIMIZATION_TARGET_PAGE_TOPICS, any_metadata,
             model_file_path);
+
 #if BUILDFLAG(BUILD_WITH_TFLITE_LIB)
-    RetryForHistogramUntilCountReached(
-        histogram_tester,
-        "OptimizationGuide.ModelExecutor.ModelLoadingResult.PageTopics", 1);
-    histogram_tester.ExpectUniqueSample(
-        "OptimizationGuide.ModelExecutor.ModelLoadingResult.PageTopics",
-        ModelExecutorLoadingState::kModelFileValidAndMemoryMapped, 1);
-    histogram_tester.ExpectTotalCount(
-        "OptimizationGuide.ModelExecutor.ModelLoadingDuration.PageTopics", 1);
+    bool expect_model_loaded = !model_is_lazily_loaded_;
 #else
-    base::RunLoop().RunUntilIdle();
+    bool expect_model_loaded = false;
 #endif
+
+    if (expect_model_loaded) {
+      RetryForHistogramUntilCountReached(
+          histogram_tester,
+          "OptimizationGuide.ModelExecutor.ModelLoadingResult.PageTopics", 1);
+      histogram_tester.ExpectUniqueSample(
+          "OptimizationGuide.ModelExecutor.ModelLoadingResult.PageTopics",
+          ModelExecutorLoadingState::kModelFileValidAndMemoryMapped, 1);
+      histogram_tester.ExpectTotalCount(
+          "OptimizationGuide.ModelExecutor.ModelLoadingDuration.PageTopics", 1);
+    } else {
+      base::RunLoop().RunUntilIdle();
+    }
   }
 
 #if BUILDFLAG(BUILD_WITH_TFLITE_LIB)
@@ -224,10 +235,11 @@
     run_loop->Run();
     return got_content_annotations;
   }
-#endif
+#endif  // BUILDFLAG(BUILD_WITH_TFLITE_LIB)
 
  private:
   base::test::ScopedFeatureList scoped_feature_list_;
+  bool model_is_lazily_loaded_ = false;
 };
 
 IN_PROC_BROWSER_TEST_F(PageContentAnnotationsServiceBrowserTest,
@@ -288,7 +300,7 @@
   EXPECT_EQ(
       123,
       got_content_annotations->model_annotations.page_topics_model_version);
-#endif
+#endif  // BUILDFLAG(BUILD_WITH_TFLITE_LIB)
 }
 
 #if BUILDFLAG(BUILD_WITH_TFLITE_LIB)
@@ -368,6 +380,79 @@
   EXPECT_FALSE(GetContentAnnotationsForURL(url).has_value());
 }
 
-#endif
+class PageContentAnnotationsServiceLoadEachExecutionTest
+    : public PageContentAnnotationsServiceBrowserTest {
+ public:
+  PageContentAnnotationsServiceLoadEachExecutionTest() {
+    scoped_feature_list_.InitWithFeatures(
+        /*enabled_features=*/{features::kOptimizationHints,
+                              features::kPageContentAnnotations,
+                              features::kLoadModelFileForEachExecution},
+        /*disabled_features=*/{});
+    set_model_is_lazily_loaded(true);
+  }
+  ~PageContentAnnotationsServiceLoadEachExecutionTest() override = default;
+
+ private:
+  base::test::ScopedFeatureList scoped_feature_list_;
+};
+
+// Regression test for crbug/1204162.
+IN_PROC_BROWSER_TEST_F(PageContentAnnotationsServiceLoadEachExecutionTest,
+                       ModelLoadsAndExecutes) {
+  base::HistogramTester histogram_tester;
+
+  GURL url(embedded_test_server()->GetURL("a.com", "/hello.html"));
+  ui_test_utils::NavigateToURL(browser(), url);
+
+  RetryForHistogramUntilCountReached(
+      histogram_tester,
+      "OptimizationGuide.ModelExecutor.ModelLoadingResult.PageTopics", 1);
+  histogram_tester.ExpectUniqueSample(
+      "OptimizationGuide.ModelExecutor.ModelLoadingResult.PageTopics",
+      ModelExecutorLoadingState::kModelFileValidAndMemoryMapped, 1);
+  histogram_tester.ExpectTotalCount(
+      "OptimizationGuide.ModelExecutor.ModelLoadingDuration.PageTopics", 1);
+
+  RetryForHistogramUntilCountReached(
+      histogram_tester,
+      "OptimizationGuide.PageContentAnnotationsService.ContentAnnotated", 1);
+
+  histogram_tester.ExpectTotalCount(
+      "OptimizationGuide.PageContentAnnotationsService.ContentAnnotated", 1);
+  histogram_tester.ExpectUniqueSample(
+      "OptimizationGuide.PageContentAnnotationsService.ContentAnnotated", true,
+      1);
+
+  PageContentAnnotationsService* service =
+      PageContentAnnotationsServiceFactory::GetForProfile(browser()->profile());
+
+  base::Optional<int64_t> model_version = service->GetPageTopicsModelVersion();
+  EXPECT_TRUE(model_version.has_value());
+  EXPECT_EQ(123, *model_version);
+
+  RetryForHistogramUntilCountReached(
+      histogram_tester,
+      "OptimizationGuide.PageContentAnnotationsService."
+      "ContentAnnotationsStorageStatus",
+      1);
+
+  histogram_tester.ExpectUniqueSample(
+      "OptimizationGuide.PageContentAnnotationsService."
+      "ContentAnnotationsStorageStatus",
+      PageContentAnnotationsStorageStatus::kSuccess, 1);
+
+  base::Optional<history::VisitContentAnnotations> got_content_annotations =
+      GetContentAnnotationsForURL(url);
+  ASSERT_TRUE(got_content_annotations.has_value());
+  EXPECT_NE(-1.0,
+            got_content_annotations->model_annotations.floc_protected_score);
+  EXPECT_FALSE(got_content_annotations->model_annotations.categories.empty());
+  EXPECT_EQ(
+      123,
+      got_content_annotations->model_annotations.page_topics_model_version);
+}
+
+#endif  // BUILDFLAG(BUILD_WITH_TFLITE_LIB)
 
 }  // namespace optimization_guide
diff --git a/chrome/browser/password_manager/android/password_ui_view_android_unittest.cc b/chrome/browser/password_manager/android/password_ui_view_android_unittest.cc
index 46022c71..b5a4b12 100644
--- a/chrome/browser/password_manager/android/password_ui_view_android_unittest.cc
+++ b/chrome/browser/password_manager/android/password_ui_view_android_unittest.cc
@@ -108,7 +108,7 @@
     testing_profile_ =
         testing_profile_manager_.CreateTestingProfile("TestProfile");
     profiles::SetLastUsedProfile(
-        testing_profile_->GetPath().BaseName().MaybeAsASCII());
+        testing_profile_->GetBaseName().MaybeAsASCII());
 
     store_ = CreateAndUseTestPasswordStore(testing_profile_);
     ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
diff --git a/chrome/browser/policy/configuration_policy_handler_list_factory.cc b/chrome/browser/policy/configuration_policy_handler_list_factory.cc
index bf6fedc..b1cbd89 100644
--- a/chrome/browser/policy/configuration_policy_handler_list_factory.cc
+++ b/chrome/browser/policy/configuration_policy_handler_list_factory.cc
@@ -188,6 +188,9 @@
   { key::kBuiltInDnsClientEnabled,
     prefs::kBuiltInDnsClientEnabled,
     base::Value::Type::BOOLEAN },
+  { key::kAdditionalDnsQueryTypesEnabled,
+    prefs::kAdditionalDnsQueryTypesEnabled,
+    base::Value::Type::BOOLEAN },
   { key::kWPADQuickCheckEnabled,
     prefs::kQuickCheckEnabled,
     base::Value::Type::BOOLEAN },
diff --git a/chrome/browser/prefs/browser_prefs.cc b/chrome/browser/prefs/browser_prefs.cc
index a35dd84..0c32d858 100644
--- a/chrome/browser/prefs/browser_prefs.cc
+++ b/chrome/browser/prefs/browser_prefs.cc
@@ -287,6 +287,7 @@
 #include "chrome/browser/ash/account_manager/account_manager_edu_coexistence_controller.h"
 #include "chrome/browser/ash/borealis/borealis_prefs.h"
 #include "chrome/browser/ash/child_accounts/secondary_account_consent_logger.h"
+#include "chrome/browser/ash/crosapi/browser_util.h"
 #include "chrome/browser/ash/guest_os/guest_os_pref_names.h"
 #include "chrome/browser/ash/lock_screen_apps/state_controller.h"
 #include "chrome/browser/ash/login/demo_mode/demo_mode_detector.h"
@@ -785,6 +786,7 @@
   chromeos::CellularESimProfileHandlerImpl::RegisterLocalStatePrefs(registry);
   chromeos::CellularMetricsLogger::RegisterLocalStatePrefs(registry);
   ash::ChromeUserManagerImpl::RegisterPrefs(registry);
+  crosapi::browser_util::RegisterLocalStatePrefs(registry);
   chromeos::CupsPrintersManager::RegisterLocalStatePrefs(registry);
   chromeos::DemoModeDetector::RegisterPrefs(registry);
   chromeos::DemoModeResourcesRemover::RegisterLocalStatePrefs(registry);
diff --git a/chrome/browser/profiles/profile.cc b/chrome/browser/profiles/profile.cc
index 9b87c0c..ee48dfa 100644
--- a/chrome/browser/profiles/profile.cc
+++ b/chrome/browser/profiles/profile.cc
@@ -250,6 +250,15 @@
   observers_.RemoveObserver(observer);
 }
 
+base::FilePath Profile::GetBaseName() const {
+  return GetPath().BaseName();
+}
+
+std::string Profile::GetDebugName() const {
+  std::string name = GetBaseName().MaybeAsASCII();
+  return name.empty() ? "UnknownProfile" : name;
+}
+
 TestingProfile* Profile::AsTestingProfile() {
   return nullptr;
 }
@@ -362,11 +371,6 @@
   // chrome/browser/prefs/browser_prefs.cc.
 }
 
-std::string Profile::GetDebugName() const {
-  std::string name = GetPath().BaseName().MaybeAsASCII();
-  return name.empty() ? "UnknownProfile" : name;
-}
-
 bool Profile::IsRegularProfile() const {
   return profile_metrics::GetBrowserProfileType(this) ==
          profile_metrics::BrowserProfileType::kRegular;
diff --git a/chrome/browser/profiles/profile.h b/chrome/browser/profiles/profile.h
index 4107fad..92baa51c 100644
--- a/chrome/browser/profiles/profile.h
+++ b/chrome/browser/profiles/profile.h
@@ -227,6 +227,13 @@
   base::FilePath GetPath() override = 0;
   virtual base::FilePath GetPath() const = 0;
 
+  // Returns the base name of the profile, which is the profile directory name
+  // within the user data directory, e.g. "Default", "Profile 1", "Profile 2".
+  base::FilePath GetBaseName() const;
+
+  // Similar to GetBaseName(), but returns a string for debugging.
+  std::string GetDebugName() const;
+
   // Return whether this context is off the record. Default is false.
   // Note that for Chrome this covers BOTH Incognito mode and Guest sessions.
   bool IsOffTheRecord() override = 0;
@@ -406,8 +413,6 @@
   // more recent (or equal to) the one specified.
   virtual bool WasCreatedByVersionOrLater(const std::string& version) = 0;
 
-  std::string GetDebugName() const;
-
   // IsRegularProfile(), IsSystemProfile(), IsIncognitoProfile(),
   // IsGuestSession(), and IsEphemeralGuestProfile are mutually exclusive.
   //
diff --git a/chrome/browser/profiles/profile_impl.cc b/chrome/browser/profiles/profile_impl.cc
index 3eec90cb..b8063de 100644
--- a/chrome/browser/profiles/profile_impl.cc
+++ b/chrome/browser/profiles/profile_impl.cc
@@ -939,7 +939,7 @@
 #if BUILDFLAG(IS_CHROMEOS_LACROS)
 bool ProfileImpl::IsMainProfile() const {
   // Profile must be at "Default" path.
-  if (GetPath().BaseName().value() != chrome::kInitialProfile)
+  if (GetBaseName().value() != chrome::kInitialProfile)
     return false;
 
   // Until go/cros-dent-1-lacros is launched, the user could have signed into
diff --git a/chrome/browser/profiles/profile_list_desktop_unittest.cc b/chrome/browser/profiles/profile_list_desktop_unittest.cc
index ddac9799..ca59722 100644
--- a/chrome/browser/profiles/profile_list_desktop_unittest.cc
+++ b/chrome/browser/profiles/profile_list_desktop_unittest.cc
@@ -80,12 +80,9 @@
     ProfileAttributesInitParams params;
     params.profile_path = profile_path;
     params.profile_name = ASCIIToUTF16(name);
+    params.is_ephemeral = true;
+    params.is_omitted = true;
     storage->AddProfile(std::move(params));
-    ProfileAttributesEntry* entry =
-        storage->GetProfileAttributesWithPath(profile_path);
-    ASSERT_NE(entry, nullptr);
-    entry->SetIsEphemeral(true);
-    entry->SetIsOmitted(true);
   }
 
   int change_count() const { return mock_observer_->change_count(); }
diff --git a/chrome/browser/profiles/profile_manager.cc b/chrome/browser/profiles/profile_manager.cc
index d77f0ae..4e06d60 100644
--- a/chrome/browser/profiles/profile_manager.cc
+++ b/chrome/browser/profiles/profile_manager.cc
@@ -862,6 +862,7 @@
 base::FilePath ProfileManager::CreateMultiProfileAsync(
     const std::u16string& name,
     size_t icon_index,
+    bool is_hidden,
     const CreateCallback& callback) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
   DCHECK(!name.empty());
@@ -893,6 +894,8 @@
     init_params.profile_path = new_path;
     init_params.profile_name = name;
     init_params.icon_index = icon_index;
+    init_params.is_ephemeral = is_hidden;
+    init_params.is_omitted = is_hidden;
     storage.AddProfile(std::move(init_params));
   }
 
@@ -2060,7 +2063,7 @@
   profile_list->Clear();
 
   // crbug.com/120112 -> several non-off-the-record profiles might have the same
-  // GetPath().BaseName(). In that case, we cannot restore both
+  // GetBaseName(). In that case, we cannot restore both
   // profiles. Include each base name only once in the last active profile
   // list.
   std::set<std::string> profile_paths;
@@ -2072,7 +2075,7 @@
         << "Guest profiles shouldn't be saved as active profiles";
     CHECK(!(*it)->IsOffTheRecord())
         << "OTR profiles shouldn't be saved as active profiles";
-    std::string profile_path = (*it)->GetPath().BaseName().MaybeAsASCII();
+    std::string profile_path = (*it)->GetBaseName().MaybeAsASCII();
     // Some profiles might become ephemeral after they are created.
     // Don't persist the System Profile as one of the last actives, it should
     // never get a browser.
@@ -2180,8 +2183,7 @@
   // Also never consider the SystemProfile as "active".
   if (profiles_info_.find(last_active->GetPath()) != profiles_info_.end() &&
       !last_active->IsSystemProfile()) {
-    std::string profile_path_base =
-        last_active->GetPath().BaseName().MaybeAsASCII();
+    std::string profile_path_base = last_active->GetBaseName().MaybeAsASCII();
     if (profile_path_base != GetLastUsedProfileBaseName())
       profiles::SetLastUsedProfile(profile_path_base);
 
diff --git a/chrome/browser/profiles/profile_manager.h b/chrome/browser/profiles/profile_manager.h
index 649bf27..e457f7d 100644
--- a/chrome/browser/profiles/profile_manager.h
+++ b/chrome/browser/profiles/profile_manager.h
@@ -177,8 +177,12 @@
   // and CREATE_STATUS_CREATED) so binding parameters with bind::Passed() is
   // prohibited. Returns the file path to the profile that will be created
   // asynchronously.
+  // If |is_hidden| is true, the new profile will be created as ephemeral
+  // (removed on the next startup) and omitted (not visible in the list of
+  // profiles).
   static base::FilePath CreateMultiProfileAsync(const std::u16string& name,
                                                 size_t icon_index,
+                                                bool is_hidden,
                                                 const CreateCallback& callback);
 
   // Returns the full path to be used for guest profiles.
diff --git a/chrome/browser/profiles/profile_manager_browsertest.cc b/chrome/browser/profiles/profile_manager_browsertest.cc
index 2052329..96d6c48 100644
--- a/chrome/browser/profiles/profile_manager_browsertest.cc
+++ b/chrome/browser/profiles/profile_manager_browsertest.cc
@@ -309,8 +309,7 @@
 
   // Make sure the last used profile was set correctly before the notification
   // was sent.
-  std::string last_used_profile_name =
-      last_used->GetPath().BaseName().MaybeAsASCII();
+  std::string last_used_profile_name = last_used->GetBaseName().MaybeAsASCII();
   EXPECT_EQ(last_used_profile_name, observer.last_used_profile_name());
 }
 
@@ -507,7 +506,7 @@
   base::RunLoop run_loop;
   ProfileManager::CreateMultiProfileAsync(
       u"New Profile",
-      /*icon_index=*/0,
+      /*icon_index=*/0, /*is_hidden=*/false,
       base::BindRepeating(&ProfileCreationComplete,
                           run_loop.QuitWhenIdleClosure()));
   run_loop.Run();
diff --git a/chrome/browser/profiles/profile_manager_unittest.cc b/chrome/browser/profiles/profile_manager_unittest.cc
index a2e899c..a5a63877 100644
--- a/chrome/browser/profiles/profile_manager_unittest.cc
+++ b/chrome/browser/profiles/profile_manager_unittest.cc
@@ -109,8 +109,7 @@
 
   // Create a profile on the fly so the the same comparison
   // can be used in Windows and other platforms.
-  EXPECT_EQ(base::FilePath().AppendASCII(profile_name),
-            profile->GetPath().BaseName());
+  EXPECT_EQ(base::FilePath().AppendASCII(profile_name), profile->GetBaseName());
   std::move(closure).Run();
 }
 
@@ -186,7 +185,7 @@
                                const std::string& name,
                                MockObserver* mock_observer) {
     ProfileManager::CreateMultiProfileAsync(
-        base::UTF8ToUTF16(name), 0,
+        base::UTF8ToUTF16(name), /*icon_index=*/0, /*is_hidden=*/false,
         base::BindRepeating(&MockObserver::OnProfileCreated,
                             base::Unretained(mock_observer)));
   }
@@ -583,6 +582,31 @@
   EXPECT_NE(profile1, profile3);
   EXPECT_NE(profile2, profile3);
 }
+
+TEST_F(ProfileManagerTest, CreateHiddenProfileAsync) {
+  Profile* profile = nullptr;
+  MockObserver mock_observer;
+  EXPECT_CALL(mock_observer, OnProfileCreated(testing::NotNull(), NotFail()))
+      .Times(testing::AtLeast(2))
+      .WillRepeatedly(testing::SaveArg<0>(&profile));
+
+  ProfileManager* profile_manager = g_browser_process->profile_manager();
+
+  profile_manager->CreateMultiProfileAsync(
+      u"New Profile", 0, /*is_hidden=*/true,
+      base::BindRepeating(&MockObserver::OnProfileCreated,
+                          base::Unretained(&mock_observer)));
+
+  content::RunAllTasksUntilIdle();
+  ASSERT_NE(profile, nullptr);
+
+  ProfileAttributesEntry* entry =
+      profile_manager->GetProfileAttributesStorage()
+          .GetProfileAttributesWithPath(profile->GetPath());
+  ASSERT_NE(entry, nullptr);
+  EXPECT_TRUE(entry->IsOmitted());
+  EXPECT_TRUE(entry->IsEphemeral());
+}
 #endif  // !defined(OS_ANDROID)
 
 // Checks that the supervised profiles no longer marked as omitted on creation.
@@ -1405,6 +1429,12 @@
   ASSERT_EQ(0u, final_last_active_profile_list->GetSize());
 }
 
+#if defined(OS_WIN)
+#define MAYBE_CleanUpGuestEphemeralProfile DISABLED_CleanUpGuestEphemeralProfile
+#else
+#define MAYBE_CleanUpGuestEphemeralProfile CleanUpGuestEphemeralProfile
+#endif
+// TODO(crbug.com/1203621) Disabled for flakiness.
 TEST_P(ProfileManagerGuestTest, CleanUpGuestEphemeralProfile) {
   // Create two profiles, one of them is guest.
   ProfileManager* profile_manager = g_browser_process->profile_manager();
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/BUILD.gn b/chrome/browser/resources/chromeos/accessibility/chromevox/BUILD.gn
index ec1bd63..09f8f59 100644
--- a/chrome/browser/resources/chromeos/accessibility/chromevox/BUILD.gn
+++ b/chrome/browser/resources/chromeos/accessibility/chromevox/BUILD.gn
@@ -44,7 +44,6 @@
   "background/custom_automation_event.js",
   "background/desktop_automation_handler.js",
   "background/download_handler.js",
-  "background/earcon_engine.js",
   "background/editing/editable_line.js",
   "background/editing/editing.js",
   "background/editing/intent_handler.js",
@@ -120,11 +119,12 @@
 # ES6 modules.
 chromevox_es6_modules = [
   "background/background.js",
-  "background/live_regions.js",
+  "background/earcon_engine.js",
+  "background/earcons.js",
   "background/es6_loader.js",
   "background/find_handler.js",
+  "background/live_regions.js",
   "background/media_automation_handler.js",
-  "background/earcons.js",
   "background/range_automation_handler.js",
 ]
 
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/background/background_test.js b/chrome/browser/resources/chromeos/accessibility/chromevox/background/background_test.js
index 1121635..1afdb96 100644
--- a/chrome/browser/resources/chromeos/accessibility/chromevox/background/background_test.js
+++ b/chrome/browser/resources/chromeos/accessibility/chromevox/background/background_test.js
@@ -3401,3 +3401,45 @@
         .replay();
   });
 });
+
+SYNC_TEST_F('ChromeVoxBackgroundTest', 'EarconPlayback', function() {
+  const engine = ChromeVoxState.instance.earcons_.engine_;
+  assertTrue(engine !== undefined);
+
+  // We only test a few earcons here. Not all earcons prevent parallel playback
+  // or have mappings into the earcon engine.
+
+  // There are no tracked sources yet.
+  assertEquals(0, Object.keys(engine.lastEarconSources_).length);
+
+  // Note that alert modal vs nonmodal would be allowed to play in parallel (as
+  // do wrap / wrap edge) because they are different events even though they
+  // really play the same sound.
+  ChromeVox.earcons.playEarcon(Earcon.ALERT_MODAL);
+  assertEquals(1, Object.keys(engine.lastEarconSources_).length);
+  const lastAlertSource = engine.lastEarconSources_[Earcon.ALERT_MODAL];
+  assertTrue(lastAlertSource !== undefined);
+
+  ChromeVox.earcons.playEarcon(Earcon.ALERT_MODAL);
+  assertEquals(1, Object.keys(engine.lastEarconSources_).length);
+
+  // The earcon for this stayed the same (above), so there's no duplicate
+  // playback of two alerts.
+  assertEquals(lastAlertSource, engine.lastEarconSources_[Earcon.ALERT_MODAL]);
+
+  // This simulates a parallel playback of the button earcon which is allowed.
+  ChromeVox.earcons.playEarcon(Earcon.BUTTON);
+  assertEquals(2, Object.keys(engine.lastEarconSources_).length);
+  assertTrue(engine.lastEarconSources_[Earcon.BUTTON] !== undefined);
+
+  // This gets called by web audio when the earcon finishes.
+  lastAlertSource.onended();
+
+  // The button earcon is still playing.
+  assertEquals(1, Object.keys(engine.lastEarconSources_).length);
+  assertTrue(engine.lastEarconSources_[Earcon.BUTTON] !== undefined);
+
+  // Finish up the button earcon, too.
+  engine.lastEarconSources_[Earcon.BUTTON].onended();
+  assertEquals(0, Object.keys(engine.lastEarconSources_).length);
+});
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/background/earcon_engine.js b/chrome/browser/resources/chromeos/accessibility/chromevox/background/earcon_engine.js
index eceaf39..409f74b 100644
--- a/chrome/browser/resources/chromeos/accessibility/chromevox/background/earcon_engine.js
+++ b/chrome/browser/resources/chromeos/accessibility/chromevox/background/earcon_engine.js
@@ -8,12 +8,10 @@
  * rest of the code.
  */
 
-goog.provide('EarconEngine');
-
 /**
  * EarconEngine generates ChromeVox's earcons using the web audio API.
  */
-EarconEngine = class {
+export class EarconEngine {
   constructor() {
     // Public control parameters. All of these are meant to be adjustable.
 
@@ -121,6 +119,16 @@
     /** @private {boolean} */
     this.persistProgressTicks_ = false;
 
+    /**
+     * Maps a earcon name to the last source input audio for that
+     * earcon.
+     * @private {!Object<!Earcon, !AudioNode|undefined>}
+     */
+    this.lastEarconSources_ = {};
+
+    /** @private {!Earcon|undefined} */
+    this.currentTrackedEarcon_;
+
     // Initialization: load the base sound data files asynchronously.
     const allSoundFilesToLoad =
         EarconEngine.SOUNDS.concat(EarconEngine.REVERBS);
@@ -132,6 +140,113 @@
   }
 
   /**
+   * A high-level way to ask the engine to play a specific earcon.
+   * @param {!Earcon} earcon The earcon to play.
+   */
+  playEarcon(earcon) {
+    // These earcons are not tracked by the engine via their audio sources.
+    switch (earcon) {
+      case Earcon.CHROMEVOX_LOADED:
+        this.cancelProgressPersistent();
+        return;
+      case Earcon.CHROMEVOX_LOADING:
+        this.startProgressPersistent();
+        return;
+      case Earcon.PAGE_FINISH_LOADING:
+        this.cancelProgress();
+        return;
+      case Earcon.PAGE_START_LOADING:
+        this.startProgress();
+        return;
+      case Earcon.POP_UP_BUTTON:
+        this.onPopUpButton();
+        return;
+
+      // TODO(dmazzoni): decide if we want new earcons for these
+      // or not. We may choose to not have earcons for some of these.
+      case Earcon.LIST_ITEM:
+      case Earcon.LONG_DESC:
+      case Earcon.MATH:
+      case Earcon.OBJECT_CLOSE:
+      case Earcon.OBJECT_ENTER:
+      case Earcon.OBJECT_EXIT:
+      case Earcon.OBJECT_OPEN:
+      case Earcon.OBJECT_SELECT:
+      case Earcon.RECOVER_FOCUS:
+        return;
+    }
+
+    // These earcons are tracked by the engine via their audio sources.
+    if (this.lastEarconSources_[earcon] !== undefined) {
+      // Playback of |earcon| is in progress.
+      return;
+    }
+
+    this.currentTrackedEarcon_ = earcon;
+    switch (earcon) {
+      case Earcon.ALERT_MODAL:
+      case Earcon.ALERT_NONMODAL:
+        this.onAlert();
+        break;
+      case Earcon.BUTTON:
+        this.onButton();
+        break;
+      case Earcon.CHECK_OFF:
+        this.onCheckOff();
+        break;
+      case Earcon.CHECK_ON:
+        this.onCheckOn();
+        break;
+      case Earcon.EDITABLE_TEXT:
+        this.onTextField();
+        break;
+      case Earcon.INVALID_KEYPRESS:
+        this.onWrap();
+        break;
+      case Earcon.LINK:
+        this.onLink();
+        break;
+      case Earcon.LISTBOX:
+        this.onSelect();
+        break;
+      case Earcon.SELECTION:
+        this.onSelection();
+        break;
+      case Earcon.SELECTION_REVERSE:
+        this.onSelectionReverse();
+        break;
+      case Earcon.SKIP:
+        this.onSkim();
+        break;
+      case Earcon.SLIDER:
+        this.onSlider();
+        break;
+      case Earcon.SMART_STICKY_MODE_OFF:
+        this.onSmartStickyModeOff();
+        break;
+      case Earcon.SMART_STICKY_MODE_ON:
+        this.onSmartStickyModeOn();
+        break;
+      case Earcon.NO_POINTER_ANCHOR:
+        this.onNoPointerAnchor();
+        break;
+      case Earcon.WRAP:
+      case Earcon.WRAP_EDGE:
+        this.onWrap();
+        break;
+    }
+    this.currentTrackedEarcon_ = undefined;
+
+    // Clear source once it finishes playing.
+    const source = this.lastEarconSources_[earcon];
+    if (source !== undefined) {
+      source.onended = () => {
+        delete this.lastEarconSources_[earcon];
+      };
+    }
+  }
+
+  /**
    * Fetches a sound asynchronously and loads its data into an AudioBuffer.
    *
    * @param {string} name The name of the sound to load.
@@ -259,7 +374,9 @@
 
     const destination = this.createCommonFilters(opt_properties);
     source.connect(destination);
-
+    if (this.currentTrackedEarcon_) {
+      this.lastEarconSources_[this.currentTrackedEarcon_] = source;
+    }
     if (opt_properties.time) {
       source.start(this.context_.currentTime + opt_properties.time);
     } else {
@@ -461,6 +578,9 @@
     let gain = properties.gain;
     for (let i = 0; i < properties.overtones; i++) {
       const osc = this.context_.createOscillator();
+      if (this.currentTrackedEarcon_) {
+        this.lastEarconSources_[this.currentTrackedEarcon_] = osc;
+      }
       osc.frequency.value = properties.freq * (i + 1);
 
       if (properties.endFreq) {
@@ -602,6 +722,8 @@
       overtones: 3,
       overtoneFactor: 0.1
     });
+
+    this.currentTrackedEarcon_ = undefined;
   }
 
   /**
@@ -762,7 +884,7 @@
   resetPan() {
     this.basePan = EarconEngine.CENTER_PAN_;
   }
-};
+}
 
 /**
  * @type {Array<string>} The list of sound data files to load.
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/background/earcons.js b/chrome/browser/resources/chromeos/accessibility/chromevox/background/earcons.js
index d358663..a775645 100644
--- a/chrome/browser/resources/chromeos/accessibility/chromevox/background/earcons.js
+++ b/chrome/browser/resources/chromeos/accessibility/chromevox/background/earcons.js
@@ -7,6 +7,8 @@
  * auditory cues.
  */
 
+import {EarconEngine} from './earcon_engine.js';
+
 export class Earcons extends AbstractEarcons {
   constructor() {
     super();
@@ -64,87 +66,8 @@
         this.engine_.resetPan();
       }
     }
-    switch (earcon) {
-      case Earcon.ALERT_MODAL:
-      case Earcon.ALERT_NONMODAL:
-        this.engine_.onAlert();
-        break;
-      case Earcon.BUTTON:
-        this.engine_.onButton();
-        break;
-      case Earcon.CHECK_OFF:
-        this.engine_.onCheckOff();
-        break;
-      case Earcon.CHECK_ON:
-        this.engine_.onCheckOn();
-        break;
-      case Earcon.CHROMEVOX_LOADED:
-        this.engine_.cancelProgressPersistent();
-        break;
-      case Earcon.CHROMEVOX_LOADING:
-        this.engine_.startProgressPersistent();
-        break;
-      case Earcon.EDITABLE_TEXT:
-        this.engine_.onTextField();
-        break;
-      case Earcon.INVALID_KEYPRESS:
-        this.engine_.onWrap();
-        break;
-      case Earcon.LINK:
-        this.engine_.onLink();
-        break;
-      case Earcon.LISTBOX:
-        this.engine_.onSelect();
-        break;
-      case Earcon.LIST_ITEM:
-      case Earcon.LONG_DESC:
-      case Earcon.MATH:
-      case Earcon.OBJECT_CLOSE:
-      case Earcon.OBJECT_ENTER:
-      case Earcon.OBJECT_EXIT:
-      case Earcon.OBJECT_OPEN:
-      case Earcon.OBJECT_SELECT:
-        // TODO(dmazzoni): decide if we want new earcons for these
-        // or not. We may choose to not have earcons for some of these.
-        break;
-      case Earcon.PAGE_FINISH_LOADING:
-        this.engine_.cancelProgress();
-        break;
-      case Earcon.PAGE_START_LOADING:
-        this.engine_.startProgress();
-        break;
-      case Earcon.POP_UP_BUTTON:
-        this.engine_.onPopUpButton();
-        break;
-      case Earcon.RECOVER_FOCUS:
-        // TODO(dmazzoni): decide if we want new earcons for this.
-        break;
-      case Earcon.SELECTION:
-        this.engine_.onSelection();
-        break;
-      case Earcon.SELECTION_REVERSE:
-        this.engine_.onSelectionReverse();
-        break;
-      case Earcon.SKIP:
-        this.engine_.onSkim();
-        break;
-      case Earcon.SLIDER:
-        this.engine_.onSlider();
-        break;
-      case Earcon.SMART_STICKY_MODE_OFF:
-        this.engine_.onSmartStickyModeOff();
-        break;
-      case Earcon.SMART_STICKY_MODE_ON:
-        this.engine_.onSmartStickyModeOn();
-        break;
-      case Earcon.NO_POINTER_ANCHOR:
-        this.engine_.onNoPointerAnchor();
-        break;
-      case Earcon.WRAP:
-      case Earcon.WRAP_EDGE:
-        this.engine_.onWrap();
-        break;
-    }
+
+    this.engine_.playEarcon(earcon);
   }
 
   /**
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/background/output/output.js b/chrome/browser/resources/chromeos/accessibility/chromevox/background/output/output.js
index c50b917..9d52e64 100644
--- a/chrome/browser/resources/chromeos/accessibility/chromevox/background/output/output.js
+++ b/chrome/browser/resources/chromeos/accessibility/chromevox/background/output/output.js
@@ -11,7 +11,6 @@
 goog.require('AbstractEarcons');
 goog.require('AutomationTreeWalker');
 goog.require('ChromeVox');
-goog.require('EarconEngine');
 goog.require('EventSourceState');
 goog.require('LocaleOutputHelper');
 goog.require('LogStore');
diff --git a/chrome/browser/resources/memories/BUILD.gn b/chrome/browser/resources/memories/BUILD.gn
index e9722d80..2e7bf938 100644
--- a/chrome/browser/resources/memories/BUILD.gn
+++ b/chrome/browser/resources/memories/BUILD.gn
@@ -25,6 +25,9 @@
     "//third_party/polymer/v3_0/components-chromium/iron-list",
     "//third_party/polymer/v3_0/components-chromium/iron-scroll-threshold",
     "//third_party/polymer/v3_0/components-chromium/polymer:polymer_bundled",
+    "//ui/webui/resources/cr_elements/cr_button:cr_button.m",
+    "//ui/webui/resources/cr_elements/cr_dialog:cr_dialog.m",
+    "//ui/webui/resources/cr_elements/cr_lazy_render:cr_lazy_render.m",
     "//ui/webui/resources/cr_elements/cr_toolbar:cr_toolbar",
     "//ui/webui/resources/cr_elements/cr_toolbar:cr_toolbar_search_field",
     "//ui/webui/resources/js:assert.m",
@@ -113,6 +116,9 @@
     ":utils",
     "//components/history_clusters/core:mojo_bindings_webui_js",
     "//third_party/polymer/v3_0/components-chromium/polymer:polymer_bundled",
+    "//ui/webui/resources/cr_elements/cr_action_menu:cr_action_menu.m",
+    "//ui/webui/resources/cr_elements/cr_lazy_render:cr_lazy_render.m",
+    "//ui/webui/resources/js:load_time_data.m",
     "//url/mojom:url_mojom_origin_webui_js",
   ]
 }
diff --git a/chrome/browser/resources/memories/app.html b/chrome/browser/resources/memories/app.html
index 10eadd8..bbe3bb0 100644
--- a/chrome/browser/resources/memories/app.html
+++ b/chrome/browser/resources/memories/app.html
@@ -67,7 +67,8 @@
       <span id="text">[[result_.title]]</span>
     </div>
   </div>
-  <div id="memories">
+  <div id="memories" on-remove-visits="onRemoveVisits_"
+      on-remove-empty-memory-element="onRemoveEmptyMemoryEement_">
     <template is="dom-repeat" items="[[result_.memories]]">
       <memory-card memory="[[item]]"></memory-card>
     </template>
@@ -76,3 +77,20 @@
 <iron-scroll-threshold id="scroll-threshold" scroll-target="container"
     on-lower-threshold="onScrolledToBottom_">
 </iron-scroll-threshold>
+
+<cr-lazy-render id="confirmationDialog">
+  <template>
+    <cr-dialog consume-keydown-event on-cancel="onConfirmationDialogCancel_">
+      <div slot="title">$i18n{removeSelected}</div>
+      <div slot="body">$i18n{removeWarning}</div>
+      <div slot="button-container">
+        <cr-button class="cancel-button" on-click="onCancelButtonTap_">
+          $i18n{cancel}
+        </cr-button>
+        <cr-button class="action-button" on-click="onRemoveButtonTap_">
+          $i18n{remove}
+        </cr-button>
+      </div>
+    </cr-dialog>
+  </template>
+</cr-lazy-render>
diff --git a/chrome/browser/resources/memories/app.js b/chrome/browser/resources/memories/app.js
index c52c730..6c82768 100644
--- a/chrome/browser/resources/memories/app.js
+++ b/chrome/browser/resources/memories/app.js
@@ -6,13 +6,18 @@
 import './page_thumbnail.js';
 import './router.js';
 import './shared_vars.js';
+import 'chrome://resources/cr_elements/cr_button/cr_button.m.js';
+import 'chrome://resources/cr_elements/cr_dialog/cr_dialog.m.js';
+import 'chrome://resources/cr_elements/cr_lazy_render/cr_lazy_render.m.js';
 import 'chrome://resources/cr_elements/cr_toolbar/cr_toolbar.js';
 import 'chrome://resources/cr_elements/shared_style_css.m.js';
 import 'chrome://resources/polymer/v3_0/iron-list/iron-list.js';
 import 'chrome://resources/polymer/v3_0/iron-scroll-threshold/iron-scroll-threshold.js';
 
 import {MemoriesResult, PageCallbackRouter, PageHandlerRemote} from '/chrome/browser/ui/webui/memories/memories.mojom-webui.js';
+import {Visit} from '/components/history_clusters/core/memories.mojom-webui.js';
 import {assert} from 'chrome://resources/js/assert.m.js';
+import {UnguessableToken} from 'chrome://resources/mojo/mojo/public/mojom/base/unguessable_token.mojom-webui.js';
 import {Url} from 'chrome://resources/mojo/url/mojom/url.mojom-webui.js';
 import {html, PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 
@@ -42,6 +47,15 @@
       //========================================================================
 
       /**
+       * The current query for which related Memories are requested and shown.
+       * @private {string}
+       */
+      query_: {
+        type: String,
+        observer: 'onQueryChanged_',
+      },
+
+      /**
        * Contains 1) the Memories returned by the browser in response to a
        * request for the freshest Memories related to a given query until a
        * given time threshold and 2) the optional continuation query parameters
@@ -52,12 +66,13 @@
       result_: Object,
 
       /**
-       * The current query for which related Memories are requested and shown.
-       * @private {string}
+       * The list of visits to be removed. A non-empty array indicates a pending
+       * remove request to the browser.
+       * @private {!Array<!Visit>}
        */
-      query_: {
-        type: String,
-        observer: 'onQueryChanged_',
+      visitsToBeRemoved_: {
+        type: Object,
+        value: [],
       },
     };
   }
@@ -70,6 +85,8 @@
     this.callbackRouter_ = BrowserProxy.getInstance().callbackRouter;
     /** @private {?number} */
     this.onMemoriesQueryResultListenerId_ = null;
+    /** @private {?number} */
+    this.onVisitsRemovedListenerId_ = null;
   }
 
   /** @override */
@@ -78,6 +95,9 @@
     this.onMemoriesQueryResultListenerId_ =
         this.callbackRouter_.onMemoriesQueryResult.addListener(
             this.onMemoriesQueryResult_.bind(this));
+    this.onVisitsRemovedListenerId_ =
+        this.callbackRouter_.onVisitsRemoved.addListener(
+            this.onVisitsRemoved_.bind(this));
   }
 
   /** @override */
@@ -86,6 +106,9 @@
     this.callbackRouter_.removeListener(
         assert(this.onMemoriesQueryResultListenerId_));
     this.onMemoriesQueryResultListenerId_ = null;
+    this.callbackRouter_.removeListener(
+        assert(this.onVisitsRemovedListenerId_));
+    this.onVisitsRemovedListenerId_ = null;
   }
 
   //============================================================================
@@ -93,6 +116,65 @@
   //============================================================================
 
   /**
+   * @private
+   */
+  onCancelButtonTap_() {
+    this.visitsToBeRemoved_ = [];
+    this.$.confirmationDialog.get().close();
+  }
+
+  /**
+   * @private
+   */
+  onConfirmationDialogCancel_() {
+    this.visitsToBeRemoved_ = [];
+  }
+
+  /**
+   * @private
+   */
+  onRemoveButtonTap_() {
+    this.pageHandler_.removeVisits(this.visitsToBeRemoved_)
+        .then(({accepted}) => {
+          if (!accepted) {
+            this.visitsToBeRemoved_ = [];
+          }
+        });
+    this.$.confirmationDialog.get().close();
+  }
+
+  /**
+   * @param {CustomEvent<!UnguessableToken>} event Event received from an empty
+   *     Memory whose visits have been removed entirely and it should also be
+   *     removed from the page. Contains the id of the Memory to be removed.
+   * @private
+   */
+  onRemoveEmptyMemoryEement_(event) {
+    const index = this.result_.memories.findIndex((memory) => {
+      return memory.id === event.detail;
+    });
+    if (index > -1) {
+      this.splice('result_.memories', index, 1);
+    }
+  }
+
+  /**
+   * @param {CustomEvent<!Array<!Visit>>} event Event received from a visit
+   *     requesting to be removed. The array may contain the related visits of
+   *     the said visit, if applicable.
+   * @private
+   */
+  onRemoveVisits_(event) {
+    // Return early if there is a pending remove request.
+    if (this.visitsToBeRemoved_.length) {
+      return;
+    }
+
+    this.visitsToBeRemoved_ = event.detail;
+    this.$.confirmationDialog.get().showModal();
+  }
+
+  /**
    * Called when the value of the search field changes.
    * @param {!CustomEvent<string>} e
    * @private
@@ -189,6 +271,14 @@
       }
     });
   }
+
+  /**
+   * Called when the last accepted request to browser to remove visits succeeds.
+   * @private
+   */
+  onVisitsRemoved_() {
+    this.visitsToBeRemoved_ = [];
+  }
 }
 
 customElements.define(MemoriesAppElement.is, MemoriesAppElement);
diff --git a/chrome/browser/resources/memories/memory_card.js b/chrome/browser/resources/memories/memory_card.js
index 1b4958a..451e0199 100644
--- a/chrome/browser/resources/memories/memory_card.js
+++ b/chrome/browser/resources/memories/memory_card.js
@@ -10,10 +10,13 @@
 import './top_visit.js';
 import 'chrome://resources/cr_elements/shared_style_css.m.js';
 
-import {Memory} from '/components/history_clusters/core/memories.mojom-webui.js';
+import {PageCallbackRouter, PageHandlerRemote} from '/chrome/browser/ui/webui/memories/memories.mojom-webui.js';
+import {Memory, Visit} from '/components/history_clusters/core/memories.mojom-webui.js';
+import {assert} from 'chrome://resources/js/assert.m.js';
 import {Url} from 'chrome://resources/mojo/url/mojom/url.mojom-webui.js';
 import {html, PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 
+import {BrowserProxy} from './browser_proxy.js';
 import {getHostnameFromUrl} from './utils.js';
 
 /**
@@ -56,10 +59,44 @@
     };
   }
 
+  constructor() {
+    super();
+    /** @private {!PageCallbackRouter} */
+    this.callbackRouter_ = BrowserProxy.getInstance().callbackRouter;
+    /** @private {?number} */
+    this.onVisitsRemovedListenerId_ = null;
+  }
+
+  /** @override */
+  connectedCallback() {
+    super.connectedCallback();
+    this.onVisitsRemovedListenerId_ =
+        this.callbackRouter_.onVisitsRemoved.addListener(
+            this.onVisitsRemoved_.bind(this));
+  }
+
+  /** @override */
+  disconnectedCallback() {
+    super.disconnectedCallback();
+    this.callbackRouter_.removeListener(
+        assert(this.onVisitsRemovedListenerId_));
+    this.onVisitsRemovedListenerId_ = null;
+  }
+
   //============================================================================
   // Helper methods
   //============================================================================
 
+  /**
+   * @param {!Array} array
+   * @param {number} num
+   * @return {!Array} Shallow copy of the first |num| items of the input array.
+   * @private
+   */
+  arrayItems_(array, num) {
+    return array.slice(0, num);
+  }
+
   /** @private */
   computeHasRelatedTabGroupsOrBookmarks_() {
     return this.memory.relatedTabGroups.length > 0 ||
@@ -76,13 +113,48 @@
   }
 
   /**
-   * @param {!Array} array
-   * @param {number} num
-   * @return {!Array} Shallow copy of the first |num| items of the input array.
+   * Called with the original remove params when the last accepted request to
+   * browser to remove visits succeeds. Since the same visit may appear in
+   * multiple Memories, all memories receive this callback in order to get a
+   * chance to remove their matching visits.
+   * @param {!Array<!Visit>} removedVisits
    * @private
    */
-  arrayItems_(array, num) {
-    return array.slice(0, num);
+  onVisitsRemoved_(removedVisits) {
+    // A matching visit is a visit to the removed visit's URL whose timespan
+    // falls within that of the removed visit.
+    const matchingVisit = (visit) => {
+      return removedVisits.findIndex((removedVisit) => {
+        return visit.url.url === removedVisit.url.url &&
+            visit.time.internalValue <= removedVisit.time.internalValue &&
+            visit.firstVisitTime.internalValue >=
+            removedVisit.firstVisitTime.internalValue;
+      }) !== -1;
+    };
+    this.memory.topVisits.forEach((topVisit, topVisitIndex) => {
+      if (matchingVisit(topVisit)) {
+        this.splice('memory.topVisits', topVisitIndex, 1);
+        return;
+      }
+      topVisit.relatedVisits.forEach((relatedVisit, relatedVisitIndex) => {
+        if (matchingVisit(relatedVisit)) {
+          this.splice(
+              `memory.topVisits.${topVisitIndex}.relatedVisits`,
+              relatedVisitIndex, 1);
+          return;
+        }
+      });
+    });
+
+    // If no more visits are left in the Memory, notify the enclosing
+    // <memories-app> to remove this Memory element from the page.
+    if (this.memory.topVisits.length === 0) {
+      this.dispatchEvent(new CustomEvent('remove-empty-memory-element', {
+        bubbles: true,
+        composed: true,
+        detail: this.memory.id,
+      }));
+    }
   }
 }
 
diff --git a/chrome/browser/resources/memories/top_visit.html b/chrome/browser/resources/memories/top_visit.html
index fe8d4a8..0884a03 100644
--- a/chrome/browser/resources/memories/top_visit.html
+++ b/chrome/browser/resources/memories/top_visit.html
@@ -39,7 +39,7 @@
 <div id="vertical-line"></div>
 <template is="dom-if" if="[[visit.relatedVisits.length]]">
   <cr-expand-button expanded="{{expanded_}}">
-    <visit-row is-top-visit visit="[[visit]]" on-visit-click="onVisitClick_">
+    <visit-row is-top-visit visit="[[visit]]" on-visit-tap="onVisitTap_">
     </visit-row>
   </cr-expand-button>
   <iron-collapse opened="[[expanded_]]">
diff --git a/chrome/browser/resources/memories/top_visit.js b/chrome/browser/resources/memories/top_visit.js
index c3c62bc7..316e562 100644
--- a/chrome/browser/resources/memories/top_visit.js
+++ b/chrome/browser/resources/memories/top_visit.js
@@ -57,7 +57,7 @@
    * @param {!CustomEvent<{event:!MouseEvent}>} e
    * @private
    */
-  onVisitClick_(e) {
+  onVisitTap_(e) {
     // Prevent the enclosing <cr-expand-button> from receiving this event.
     e.detail.event.stopImmediatePropagation();
   }
diff --git a/chrome/browser/resources/memories/visit_row.html b/chrome/browser/resources/memories/visit_row.html
index 1672ef9..72d5fee 100644
--- a/chrome/browser/resources/memories/visit_row.html
+++ b/chrome/browser/resources/memories/visit_row.html
@@ -52,9 +52,13 @@
   #timestamp {
     color: var(--cr-secondary-text-color);
   }
+
+  .dropdown-item {
+    height: 32px;
+  }
 </style>
 <div id="header">
-  <a id="start-justtified" href="[[visit.url.url]]" on-click="onClick_">
+  <a id="start-justtified" href="[[visit.url.url]]" on-click="onTap_">
     <page-favicon url="[[visit.url]]"></page-favicon>
     <span id="title">[[visit.pageTitle]]</span>
     <span id="separator">–</span>
@@ -62,8 +66,19 @@
   </a>
   <div id="end-justified">
     <div id="timestamp">[[getTimeOfVisit_(visit)]]</div>
-    <cr-icon-button id="action" class="icon-more-vert"
-        hidden="[[visit.relatedVisits.length]]">
+    <cr-icon-button id="actionMenuButton" class="icon-more-vert"
+        on-click="onActionMenuButtonTap_">
     </cr-icon-button>
   </div>
 </div>
+
+<cr-lazy-render id="actionMenu">
+  <template>
+    <cr-action-menu role-description="$i18n{actionMenuDescription}">
+      <button id="removeButton" class="dropdown-item"
+          on-click="onRemoveButtonTap_">
+        [[removeButtonTitle_]]
+      </button>
+    </cr-action-menu>
+  </template>
+</cr-lazy-render>
diff --git a/chrome/browser/resources/memories/visit_row.js b/chrome/browser/resources/memories/visit_row.js
index 3bc7a757e..c9f52ec 100644
--- a/chrome/browser/resources/memories/visit_row.js
+++ b/chrome/browser/resources/memories/visit_row.js
@@ -5,13 +5,18 @@
 import './page_favicon.js';
 import './shared_vars.js';
 import 'chrome://resources/cr_elements/shared_style_css.m.js';
+import 'chrome://resources/cr_elements/cr_lazy_render/cr_lazy_render.m.js';
+import 'chrome://resources/cr_elements/cr_action_menu/cr_action_menu.m.js';
+import './strings.m.js';
 
 import {Visit} from '/components/history_clusters/core/memories.mojom-webui.js';
+import {loadTimeData} from 'chrome://resources/js/load_time_data.m.js';
 import {Url} from 'chrome://resources/mojo/url/mojom/url.mojom-webui.js';
 import {html, PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 
 import {getHostnameFromUrl} from './utils.js';
 
+
 /**
  * @fileoverview This file provides a custom element displaying a visit to a
  * page within a Memory. A visit features the page favicon, title, a timestamp,
@@ -47,6 +52,20 @@
         type: Boolean,
         reflectToAttribute: true,
       },
+
+      //========================================================================
+      // Private properties
+      //========================================================================
+
+      /**
+       * Title of the action menu button to remove this visit and its related
+       * visits, if applicable.
+       * @private {string}
+       */
+      removeButtonTitle_: {
+        type: String,
+        computed: 'computeRemoveButtonTitle_(visit)',
+      },
     };
   }
 
@@ -58,25 +77,74 @@
    * @param {!MouseEvent} event
    * @private
    */
-  onClick_(event) {
+  onActionMenuButtonTap_(event) {
     // Only handle main (usually the left) and auxiliary (usually the wheel or
     // the middle) button presses.
     if (event.button > 1) {
       return;
     }
 
-    this.dispatchEvent(new CustomEvent('visit-click', {
+    this.$.actionMenu.get().showAt(this.$.actionMenuButton);
+
+    // Prevent the enclosing <cr-expand-button> from receiving this event.
+    event.stopPropagation();
+  }
+
+  /**
+   * @param {!MouseEvent} event
+   * @private
+   */
+  onTap_(event) {
+    // Only handle main (usually the left) and auxiliary (usually the wheel or
+    // the middle) button presses.
+    if (event.button > 1) {
+      return;
+    }
+
+    this.dispatchEvent(new CustomEvent('visit-tap', {
       bubbles: true,
       composed: true,
       detail: {event},
     }));
   }
 
+  /**
+   * @param {!MouseEvent} event
+   * @private
+   */
+  onRemoveButtonTap_(event) {
+    // Only handle main (usually the left) and auxiliary (usually the wheel or
+    // the middle) button presses.
+    if (event.button > 1) {
+      return;
+    }
+
+    this.dispatchEvent(new CustomEvent('remove-visits', {
+      bubbles: true,
+      composed: true,
+      detail: [this.visit, ...this.visit.relatedVisits],
+    }));
+
+    this.$.actionMenu.get().close();
+
+    // Prevent the enclosing <cr-expand-button> from receiving this event.
+    event.stopPropagation();
+  }
+
   //============================================================================
   // Helper methods
   //============================================================================
 
   /**
+   * @private
+   */
+  computeRemoveButtonTitle_() {
+    return loadTimeData.getString(
+        this.visit.relatedVisits.length > 0 ? 'removeAllFromHistory' :
+                                              'removeFromHistory');
+  }
+
+  /**
    * @param {!Url} url
    * @return {string} The domain name of the URL without the leading 'www.'.
    * @private
diff --git a/chrome/browser/resources/pdf/elements/shared-css.html b/chrome/browser/resources/pdf/elements/shared-css.html
index 16e61e4..252c4162 100644
--- a/chrome/browser/resources/pdf/elements/shared-css.html
+++ b/chrome/browser/resources/pdf/elements/shared-css.html
@@ -2,6 +2,7 @@
   <style>
     cr-icon-button {
       --cr-icon-button-fill-color: var(--pdf-toolbar-text-color);
+      --cr-icon-button-focus-outline-color: var(--google-grey-300);
       margin: 0;
     }
 
diff --git a/chrome/browser/resources/pdf/pdf_viewer.html b/chrome/browser/resources/pdf/pdf_viewer.html
index 63c75b3..283310e 100644
--- a/chrome/browser/resources/pdf/pdf_viewer.html
+++ b/chrome/browser/resources/pdf/pdf_viewer.html
@@ -58,7 +58,7 @@
   }
 
   #content-focus-rectangle {
-    border: 2px solid var(--google-grey-600);
+    border: 2px solid var(--google-grey-300);
     border-radius: 2px;
     box-sizing: border-box;
     height: 100%;
diff --git a/chrome/browser/resources/read_later/app.html b/chrome/browser/resources/read_later/app.html
index 5fdee66..b8e462bc48 100644
--- a/chrome/browser/resources/read_later/app.html
+++ b/chrome/browser/resources/read_later/app.html
@@ -6,7 +6,7 @@
   }
 
   #readLaterList {
-    max-height: 800px;
+    max-height: 500px;
     overflow: auto;
   }
 
diff --git a/chrome/browser/resources/read_later/side_panel/side_panel.js b/chrome/browser/resources/read_later/side_panel/side_panel.js
index ce927b47..34fae66 100644
--- a/chrome/browser/resources/read_later/side_panel/side_panel.js
+++ b/chrome/browser/resources/read_later/side_panel/side_panel.js
@@ -2,16 +2,10 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import '../read_later_item.js';
-import 'chrome://resources/cr_elements/hidden_style_css.m.js';
-import 'chrome://resources/cr_elements/mwb_shared_style.js';
+import '../app.js'; /* <read-later-app> */
 
 import {html, PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 
-import {ReadLaterApiProxyImpl} from '../read_later_api_proxy.js';
-
-const readLaterApi = ReadLaterApiProxyImpl.getInstance();
-
 class SidePanel extends PolymerElement {
   static get is() {
     return 'side-panel';
@@ -19,318 +13,8 @@
 
   static get template() {
     return html`
-      <style include="mwb-shared-style cr-hidden-style">
-        #header {
-          align-items: center;
-          display: flex;
-          padding: 16px 0 4px 8px;
-        }
-
-        #searchContainer {
-          background: #f1f3f4;
-          border-radius: 8px;
-          display: flex;
-          flex: 1;
-          height: 36px;
-        }
-
-        cr-icon-button {
-          margin: 0;
-        }
-
-        input {
-          appearance: none;
-          background: transparent;
-          border: 0;
-          display: block;
-          flex: 1;
-          height: 100%;
-          line-height: 36px;
-          outline: none;
-        }
-
-        #tabs {
-          display: flex;
-          height: 48px;
-          position: relative;
-          width: 100%;
-        }
-
-        #tabs a {
-          cursor: pointer;
-          display: block;
-          flex: 1;
-          line-height: 48px;
-          text-align: center;
-        }
-
-        #tabs span {
-          position: relative;
-        }
-
-        #tabs a[active] span::after {
-          content: '';
-          display: block;
-          background: #1A73E8;
-          bottom: -8px;
-          border-radius: 3px 3px 0 0;
-          height: 3px;
-          left: 2px;
-          position: absolute;
-          right: 2px;
-        }
-
-        #tabsBorder {
-          background: #dadce0;
-          bottom: 8px;
-          height: 1px;
-          position: absolute;
-          width: 100%;
-        }
-
-        .tab {
-          display: none;
-        }
-
-        .tab[active] {
-          display: block;
-        }
-
-        h2 {
-          font-size: 11px;
-          font-weight: normal;
-          padding: 16px 16px 8px 16px;
-          text-transform: uppercase;
-        }
-      </style>
-
-      <div id="header">
-        <div id="searchContainer">
-          <cr-icon-button iron-icon="cr:search"></cr-icon-button>
-          <input value="{{searchQuery_::input}}">
-        </div>
-        <cr-icon-button iron-icon="cr:close" on-click="close_"></cr-icon-button>
-      </div>
-
-      <div id="tabs">
-        <template is="dom-repeat" items="[[tabs_]]">
-          <a on-click="activateTab_"
-              active$="[[isActiveTab_(item.id, activeTab_)]]">
-            <span>[[item.label]]</span>
-          </a>
-        </template>
-        <div id="tabsBorder"></div>
-      </div>
-
-      <div id="readingList" class="tab"
-          active$="[[isActiveTab_('readingList', activeTab_)]]">
-        <h2>Unread</h2>
-        <template is="dom-repeat" items="[[unreadItems_]]">
-          <read-later-item class="mwb-list-item" data="[[item]]"
-              hidden$="[[shouldHideItem_(item.title, searchQuery_)]]">
-          </read-later-item>
-        </template>
-        <h2>Read</h2>
-        <template is="dom-repeat" items="[[readItems_]]">
-          <read-later-item class="mwb-list-item" data="[[item]]"
-            hidden$="[[shouldHideItem_(item.title, searchQuery_)]]">
-          </read-later-item>
-        </template>
-      </div>
-
-      <div id="bookmarks" class="tab"
-          active$="[[isActiveTab_('bookmarks', activeTab_)]]">
-        <template is="dom-repeat" items="[[bookmarkFolders_]]">
-          <bookmark-folder folder="[[item]]"
-              search-query="[[searchQuery_]]"></bookmark-folder>
-        </template>
-      </div>
+      <read-later-app></read-later-app>
     `;
   }
-
-  static get properties() {
-    return {
-      unreadItems_: {
-        type: Array,
-        value: () => [],
-      },
-      readItems_: {
-        type: Array,
-        value: () => [],
-      },
-      bookmarkFolders_: {
-        type: Array,
-        value: () => [],
-      },
-      tabs_: {
-        type: Array,
-        value: () => {
-          return [
-            {
-              id: 'readingList',
-              label: 'Reading list',
-            },
-            {
-              id: 'bookmarks',
-              label: 'Bookmarks',
-            },
-          ];
-        },
-      },
-      activeTab_: {
-        type: Object,
-        value: () => {},
-      },
-      searchQuery_: {
-        type: String,
-        value: '',
-      }
-    };
-  }
-
-  connectedCallback() {
-    super.connectedCallback();
-    this.activeTab_ = this.tabs_[0];
-    readLaterApi.getReadLaterEntries().then(({entries}) => {
-      this.unreadItems_ = entries.unreadEntries;
-      this.readItems_ = entries.readEntries;
-    });
-    readLaterApi.getCallbackRouter().itemsChanged.addListener(
-        items => this.onReadLaterItemsUpdated_(items));
-    chrome.bookmarks.getTree(([{children}]) => {
-      this.bookmarkFolders_ = children;
-    });
-  }
-
-  activateTab_(event) {
-    this.activeTab_ = event.model.item;
-  }
-
-  close_() {
-    readLaterApi.closeUI();
-  }
-
-  isActiveTab_(tabId) {
-    return this.activeTab_.id === tabId;
-  }
-
-  onReadLaterItemsUpdated_(items) {
-    this.unreadItems_ = items.unreadEntries;
-    this.readItems_ = items.readEntries;
-  }
-
-  shouldHideItem_(title) {
-    if (this.searchQuery_.length === 0) {
-      return false;
-    }
-
-    return title.toLowerCase().indexOf(this.searchQuery_.toLowerCase()) === -1;
-  }
 }
 customElements.define(SidePanel.is, SidePanel);
-
-class BookmarkFolder extends PolymerElement {
-  static get is() {
-    return 'bookmark-folder';
-  }
-
-  static get template() {
-    return html`
-      <style include="cr-hidden-style">
-      </style>
-      <template is="dom-repeat" items="[[folder.children]]">
-        <template is="dom-if" if="[[item.children]]">
-          <bookmark-folder folder="[[item]]" search-query="[[searchQuery]]">
-          </bookmark-folder>
-        </template>
-        <template is="dom-if" if="[[!item.children]]">
-          <bookmark-item item="[[item]]"
-              hidden$="[[shouldHideItem_(item.title, searchQuery)]]">
-          </bookmark-item>
-        </template>
-      </template>
-    `;
-  }
-
-  static get properties() {
-    return {
-      folder: {
-        type: Object,
-        value: () => {},
-      },
-      searchQuery: {
-        type: String,
-        value: () => '',
-      },
-    };
-  }
-
-  shouldHideItem_(title) {
-    if (this.searchQuery.length === 0) {
-      return false;
-    }
-
-    return title.toLowerCase().indexOf(this.searchQuery.toLowerCase()) === -1;
-  }
-}
-customElements.define(BookmarkFolder.is, BookmarkFolder);
-
-class BookmarkItem extends PolymerElement {
-  static get is() {
-    return 'bookmark-item';
-  }
-
-  static get template() {
-    return html`
-      <style>
-        :host {
-          align-items: center;
-          cursor: pointer;
-          display: flex;
-          height: 40px;
-          line-height: 40px;
-          overflow: hidden;
-          padding: 0 16px;
-          white-space: nowrap;
-        }
-
-        :host(:hover) {
-          background: var(--mwb-list-item-hover-background-color);
-        }
-
-        #favicon {
-          width: 16px;
-          height: 16px;
-          margin-right: 16px;
-        }
-
-        #title {
-          overflow: hidden;
-          text-overflow: ellipsis;
-        }
-      </style>
-
-      <img id="favicon" src="chrome://favicon/[[item.url]]">
-      <div id="title">[[item.title]]</div>
-    `;
-  }
-
-  static get properties() {
-    return {
-      item: {
-        type: Object,
-        value: () => {},
-      },
-    };
-  }
-
-  constructor() {
-    super();
-    this.addEventListener('click', () => this.onClick_());
-  }
-
-  onClick_() {
-    chrome.tabs.create({url: this.item.url});
-  }
-}
-customElements.define(BookmarkItem.is, BookmarkItem);
diff --git a/chrome/browser/safe_browsing/incident_reporting/platform_state_store_win.cc b/chrome/browser/safe_browsing/incident_reporting/platform_state_store_win.cc
index 51bfc0fa..ae321de 100644
--- a/chrome/browser/safe_browsing/incident_reporting/platform_state_store_win.cc
+++ b/chrome/browser/safe_browsing/incident_reporting/platform_state_store_win.cc
@@ -28,7 +28,7 @@
 
 // Returns the name of the registry value for |profile|'s state.
 std::wstring GetValueNameForProfile(Profile* profile) {
-  return profile->GetPath().BaseName().value();
+  return profile->GetBaseName().value();
 }
 
 // Clears |profile|'s state.
diff --git a/chrome/browser/safety_check/android/BUILD.gn b/chrome/browser/safety_check/android/BUILD.gn
index 456e5314..8711026 100644
--- a/chrome/browser/safety_check/android/BUILD.gn
+++ b/chrome/browser/safety_check/android/BUILD.gn
@@ -17,9 +17,12 @@
   ]
   deps = [
     ":jni_headers",
-    "//chrome/common:buildflags",
+    "//chrome/browser/signin:identity_manager_provider",
+    "//components/embedder_support/android:browser_context",
     "//components/prefs",
     "//components/safety_check",
+    "//components/user_prefs",
+    "//content/public/browser",
   ]
 }
 
@@ -42,10 +45,12 @@
     "//chrome/browser/password_check/android:password_check_java_enums",
     "//chrome/browser/password_manager/android:java",
     "//chrome/browser/preferences:java",
+    "//chrome/browser/profiles/android:java",
     "//chrome/browser/safe_browsing/android:java",
     "//chrome/browser/settings:java",
     "//chrome/browser/signin/ui/android:java",
     "//components/browser_ui/settings/android:java",
+    "//components/embedder_support/android:browser_context_java",
     "//components/password_manager/core/browser:password_manager_java_enums",
     "//components/signin/public/android:java",
     "//content/public/android:content_java",
@@ -92,10 +97,7 @@
   # Skip platform checks since Robolectric depends on requires_android targets.
   bypass_platform_checks = true
   testonly = true
-  sources = [
-    "javatests/src/org/chromium/chrome/browser/safety_check/SafetyCheckBridgeTest.java",
-    "javatests/src/org/chromium/chrome/browser/safety_check/SafetyCheckMediatorTest.java",
-  ]
+  sources = [ "javatests/src/org/chromium/chrome/browser/safety_check/SafetyCheckMediatorTest.java" ]
   deps = [
     ":java",
     "//base:base_java",
@@ -105,6 +107,7 @@
     "//chrome/browser/flags:java",
     "//chrome/browser/password_check:public_java",
     "//chrome/browser/password_check/android:password_check_java_enums",
+    "//chrome/browser/profiles/android:java",
 
     # Robolectric needs the full PasswordCheckFactory implementation.
     "//chrome/browser/password_check/android/internal:internal_factory_java",
@@ -113,6 +116,7 @@
     "//chrome/browser/signin/ui/android:java",
     "//chrome/test/android:chrome_java_test_support",
     "//components/browser_ui/settings/android:java",
+    "//components/embedder_support/android:browser_context_java",
     "//third_party/android_deps:robolectric_all_java",
     "//third_party/junit:junit",
     "//third_party/mockito:mockito_java",
diff --git a/chrome/browser/safety_check/android/java/src/org/chromium/chrome/browser/safety_check/SafetyCheckBridge.java b/chrome/browser/safety_check/android/java/src/org/chromium/chrome/browser/safety_check/SafetyCheckBridge.java
index b3ce7b5e37..902679c2 100644
--- a/chrome/browser/safety_check/android/java/src/org/chromium/chrome/browser/safety_check/SafetyCheckBridge.java
+++ b/chrome/browser/safety_check/android/java/src/org/chromium/chrome/browser/safety_check/SafetyCheckBridge.java
@@ -6,6 +6,8 @@
 
 import org.chromium.base.annotations.CalledByNative;
 import org.chromium.base.annotations.NativeMethods;
+import org.chromium.chrome.browser.profiles.Profile;
+import org.chromium.components.embedder_support.browser_context.BrowserContextHandle;
 
 /**
  * Provides access to the C++ multi-platform Safety check code in
@@ -47,15 +49,15 @@
      * Returns whether the user is signed in for the purposes of password check.
      */
     boolean userSignedIn() {
-        return SafetyCheckBridgeJni.get().userSignedIn();
+        return SafetyCheckBridgeJni.get().userSignedIn(Profile.getLastUsedRegularProfile());
     }
 
     /**
      * Triggers the Safe Browsing check on the C++ side.
      */
     void checkSafeBrowsing() {
-        SafetyCheckBridgeJni.get().checkSafeBrowsing(
-                mNativeSafetyCheckBridge, SafetyCheckBridge.this);
+        SafetyCheckBridgeJni.get().checkSafeBrowsing(mNativeSafetyCheckBridge,
+                SafetyCheckBridge.this, Profile.getLastUsedRegularProfile());
     }
 
     /**
@@ -73,8 +75,9 @@
     @NativeMethods
     interface Natives {
         long init(SafetyCheckBridge safetyCheckBridge, SafetyCheckCommonObserver observer);
-        boolean userSignedIn();
-        void checkSafeBrowsing(long nativeSafetyCheckBridge, SafetyCheckBridge safetyCheckBridge);
+        boolean userSignedIn(BrowserContextHandle browserContext);
+        void checkSafeBrowsing(long nativeSafetyCheckBridge, SafetyCheckBridge safetyCheckBridge,
+                BrowserContextHandle browserContext);
         void destroy(long nativeSafetyCheckBridge, SafetyCheckBridge safetyCheckBridge);
     }
 }
diff --git a/chrome/browser/safety_check/android/javatests/src/org/chromium/chrome/browser/safety_check/SafetyCheckBridgeTest.java b/chrome/browser/safety_check/android/javatests/src/org/chromium/chrome/browser/safety_check/SafetyCheckBridgeTest.java
deleted file mode 100644
index 721b470c..0000000
--- a/chrome/browser/safety_check/android/javatests/src/org/chromium/chrome/browser/safety_check/SafetyCheckBridgeTest.java
+++ /dev/null
@@ -1,78 +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.
-
-package org.chromium.chrome.browser.safety_check;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyLong;
-import static org.mockito.Mockito.doAnswer;
-
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-
-import org.chromium.base.test.BaseRobolectricTestRunner;
-import org.chromium.base.test.util.JniMocker;
-import org.chromium.chrome.browser.safety_check.SafetyCheckBridge.SafetyCheckCommonObserver;
-
-/** Unit tests for {@link SafetyCheckBridge}. */
-@RunWith(BaseRobolectricTestRunner.class)
-public class SafetyCheckBridgeTest {
-    class TestObserver implements SafetyCheckCommonObserver {
-        public boolean sbCallbackInvoked;
-
-        private @SafeBrowsingStatus int mSBExpected = -1;
-
-        @Override
-        public void onSafeBrowsingCheckResult(@SafeBrowsingStatus int status) {
-            sbCallbackInvoked = true;
-            assertEquals(mSBExpected, status);
-        }
-
-        public void setSafeBrowsingExpected(@SafeBrowsingStatus int expected) {
-            mSBExpected = expected;
-        }
-    }
-
-    @Rule
-    public JniMocker mocker = new JniMocker();
-    @Mock
-    private SafetyCheckBridge.Natives mNativeMock;
-
-    private SafetyCheckBridge mSafetyCheckBridge;
-    private TestObserver mObserver;
-
-    @Before
-    public void setUp() {
-        MockitoAnnotations.initMocks(this);
-        mocker.mock(SafetyCheckBridgeJni.TEST_HOOKS, mNativeMock);
-        mObserver = new TestObserver();
-        mSafetyCheckBridge = new SafetyCheckBridge(mObserver);
-    }
-
-    @Test
-    public void testCheckSafeBrowsing() {
-        assertFalse(mObserver.sbCallbackInvoked);
-
-        @SafeBrowsingStatus
-        int expected = SafeBrowsingStatus.DISABLED_BY_ADMIN;
-        doAnswer(invocation -> {
-            mObserver.onSafeBrowsingCheckResult(expected);
-            return null;
-        })
-                .when(mNativeMock)
-                .checkSafeBrowsing(anyLong(), any(SafetyCheckBridge.class));
-        mObserver.setSafeBrowsingExpected(expected);
-
-        mSafetyCheckBridge.checkSafeBrowsing();
-
-        assertTrue(mObserver.sbCallbackInvoked);
-    }
-}
diff --git a/chrome/browser/safety_check/android/safety_check_bridge.cc b/chrome/browser/safety_check/android/safety_check_bridge.cc
index dd0b848..2e082afe 100644
--- a/chrome/browser/safety_check/android/safety_check_bridge.cc
+++ b/chrome/browser/safety_check/android/safety_check_bridge.cc
@@ -8,35 +8,33 @@
 
 #include <memory>
 
-#include "chrome/browser/profiles/profile_manager.h"
 #include "chrome/browser/safety_check/android/jni_headers/SafetyCheckBridge_jni.h"
-#include "chrome/browser/signin/identity_manager_factory.h"
+#include "chrome/browser/signin/identity_manager_provider.h"
+#include "components/embedder_support/android/browser_context/browser_context_handle.h"
 #include "components/password_manager/core/browser/leak_detection/authenticated_leak_check.h"
 #include "components/safety_check/safety_check.h"
+#include "components/user_prefs/user_prefs.h"
+#include "content/public/browser/browser_context.h"
 
 static jlong JNI_SafetyCheckBridge_Init(
     JNIEnv* env,
     const base::android::JavaParamRef<jobject>& obj,
     const base::android::JavaParamRef<jobject>& j_safety_check_observer) {
   return reinterpret_cast<intptr_t>(
-      new SafetyCheckBridge(env, j_safety_check_observer));
+      new SafetyCheckBridge(j_safety_check_observer));
 }
 
-static jboolean JNI_SafetyCheckBridge_UserSignedIn(JNIEnv* env) {
-  signin::IdentityManager* identity_manager =
-      IdentityManagerFactory::GetForProfile(
-          ProfileManager::GetLastUsedProfile());
+static jboolean JNI_SafetyCheckBridge_UserSignedIn(
+    JNIEnv* env,
+    const base::android::JavaParamRef<jobject>& jhandle) {
   return password_manager::AuthenticatedLeakCheck::HasAccountForRequest(
-      identity_manager);
+      signin::GetIdentityManagerForBrowserContext(
+          browser_context::BrowserContextFromJavaHandle(jhandle)));
 }
 
 SafetyCheckBridge::SafetyCheckBridge(
-    JNIEnv* env,
     const base::android::JavaParamRef<jobject>& j_safety_check_observer)
-    : pref_service_(ProfileManager::GetActiveUserProfile()
-                        ->GetOriginalProfile()
-                        ->GetPrefs()),
-      j_safety_check_observer_(j_safety_check_observer) {
+    : j_safety_check_observer_(j_safety_check_observer) {
   safety_check_ = std::make_unique<safety_check::SafetyCheck>(this);
 }
 
@@ -49,8 +47,10 @@
 
 void SafetyCheckBridge::CheckSafeBrowsing(
     JNIEnv* env,
-    const base::android::JavaParamRef<jobject>& obj) {
-  safety_check_->CheckSafeBrowsing(pref_service_);
+    const base::android::JavaParamRef<jobject>& jobj,
+    const base::android::JavaParamRef<jobject>& jhandle) {
+  safety_check_->CheckSafeBrowsing(user_prefs::UserPrefs::Get(
+      browser_context::BrowserContextFromJavaHandle(jhandle)));
 }
 
 void SafetyCheckBridge::OnSafeBrowsingCheckResult(
diff --git a/chrome/browser/safety_check/android/safety_check_bridge.h b/chrome/browser/safety_check/android/safety_check_bridge.h
index 046388fc..3e8168e 100644
--- a/chrome/browser/safety_check/android/safety_check_bridge.h
+++ b/chrome/browser/safety_check/android/safety_check_bridge.h
@@ -8,7 +8,6 @@
 #include <jni.h>
 
 #include "base/android/scoped_java_ref.h"
-#include "components/prefs/pref_service.h"
 #include "components/safety_check/safety_check.h"
 
 // Allows the Java code to make use of cross-platform browser safety checks in
@@ -17,8 +16,7 @@
     : public safety_check::SafetyCheck::SafetyCheckHandlerInterface {
  public:
   // Takes an observer object that will get invoked on check results.
-  SafetyCheckBridge(
-      JNIEnv* env,
+  explicit SafetyCheckBridge(
       const base::android::JavaParamRef<jobject>& j_safety_check_observer);
 
   // Destroys this bridge. Should only be invoked by the Java side.
@@ -27,7 +25,8 @@
   // Checks the status of Safe Browsing and invokes |OnSafeBrowsingCheckResult|
   // on the observer object with the result.
   void CheckSafeBrowsing(JNIEnv* env,
-                         const base::android::JavaParamRef<jobject>& obj);
+                         const base::android::JavaParamRef<jobject>& obj,
+                         const base::android::JavaParamRef<jobject>& jhandle);
 
   // safety_check::SafetyCheck::SafetyCheckHandlerInterface implementation.
   void OnSafeBrowsingCheckResult(
@@ -36,7 +35,6 @@
  private:
   virtual ~SafetyCheckBridge();
 
-  PrefService* pref_service_ = nullptr;
   std::unique_ptr<safety_check::SafetyCheck> safety_check_;
   base::android::ScopedJavaGlobalRef<jobject> j_safety_check_observer_;
 };
diff --git a/chrome/browser/search_engines/android/BUILD.gn b/chrome/browser/search_engines/android/BUILD.gn
index d2c4601..ff0be3d 100644
--- a/chrome/browser/search_engines/android/BUILD.gn
+++ b/chrome/browser/search_engines/android/BUILD.gn
@@ -8,13 +8,8 @@
 
 android_library("java") {
   sources = [
-    "java/src/org/chromium/chrome/browser/search_engines/DefaultSearchEngineDialogHelper.java",
-    "java/src/org/chromium/chrome/browser/search_engines/DefaultSearchEnginePromoDialog.java",
     "java/src/org/chromium/chrome/browser/search_engines/SearchEngineChoiceMetrics.java",
     "java/src/org/chromium/chrome/browser/search_engines/SearchEngineChoiceNotification.java",
-    "java/src/org/chromium/chrome/browser/search_engines/SearchEnginePromoState.java",
-    "java/src/org/chromium/chrome/browser/search_engines/SearchEnginePromoType.java",
-    "java/src/org/chromium/chrome/browser/search_engines/SogouPromoDialog.java",
     "java/src/org/chromium/chrome/browser/search_engines/TemplateUrlServiceFactory.java",
     "java/src/org/chromium/chrome/browser/search_engines/settings/SearchEngineAdapter.java",
     "java/src/org/chromium/chrome/browser/search_engines/settings/SearchEngineSettings.java",
@@ -34,7 +29,6 @@
     "//components/browser_ui/settings/android:java",
     "//components/browser_ui/site_settings/android:java",
     "//components/browser_ui/util/android:java",
-    "//components/browser_ui/widget/android:java",
     "//components/content_settings/android:content_settings_enums_java",
     "//components/location/android:location_java",
     "//components/search_engines/android:java",
@@ -52,14 +46,11 @@
 
 android_resources("java_resources") {
   sources = [
-    "java/res/drawable/search_sogou.xml",
     "java/res/layout/search_engine.xml",
     "java/res/layout/search_engine_recent_title.xml",
-    "java/res/values/ids.xml",
   ]
   deps = [
     "//chrome/browser/ui/android/strings:ui_strings_grd",
     "//components/browser_ui/styles/android:java_resources",
-    "//components/browser_ui/widget/android:java_resources",
   ]
 }
diff --git a/chrome/browser/search_engines/android/java/res/values/ids.xml b/chrome/browser/search_engines/android/java/res/values/ids.xml
deleted file mode 100644
index a7b19db..0000000
--- a/chrome/browser/search_engines/android/java/res/values/ids.xml
+++ /dev/null
@@ -1,11 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-
-<!-- Copyright 2021 The Chromium Authors. All rights reserved.
-
-     Use of this source code is governed by a BSD-style license that can be
-     found in the LICENSE file.
--->
-
-<resources>
-    <item name="default_search_engine_dialog_options" type="id"/>
-</resources>
diff --git a/chrome/browser/search_engines/android/java/src/org/chromium/chrome/browser/search_engines/DefaultSearchEnginePromoDialog.java b/chrome/browser/search_engines/android/java/src/org/chromium/chrome/browser/search_engines/DefaultSearchEnginePromoDialog.java
deleted file mode 100644
index 989d2db6..0000000
--- a/chrome/browser/search_engines/android/java/src/org/chromium/chrome/browser/search_engines/DefaultSearchEnginePromoDialog.java
+++ /dev/null
@@ -1,135 +0,0 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-package org.chromium.chrome.browser.search_engines;
-
-import android.annotation.SuppressLint;
-import android.app.Activity;
-import android.content.DialogInterface;
-import android.os.Bundle;
-import android.widget.Button;
-
-import androidx.annotation.Nullable;
-import androidx.annotation.VisibleForTesting;
-
-import org.chromium.base.Callback;
-import org.chromium.base.metrics.RecordUserAction;
-import org.chromium.components.browser_ui.widget.PromoDialog;
-import org.chromium.components.browser_ui.widget.RadioButtonLayout;
-
-/** A dialog that forces the user to choose a default search engine. */
-public class DefaultSearchEnginePromoDialog extends PromoDialog {
-    /** Notified about events happening to the dialog. */
-    public static interface DefaultSearchEnginePromoDialogObserver {
-        void onDialogShown(DefaultSearchEnginePromoDialog shownDialog);
-    }
-    private static DefaultSearchEnginePromoDialogObserver sObserver;
-
-    @SuppressLint("StaticFieldLeak")
-    private static DefaultSearchEnginePromoDialog sCurrentDialog;
-
-    /** Used to determine the promo dialog contents. */
-    @SearchEnginePromoType
-    private final int mDialogType;
-
-    /** Called when the dialog is dismissed after the user has chosen a search engine. */
-    private final Callback<Boolean> mOnSuccessCallback;
-
-    private DefaultSearchEngineDialogHelper.Delegate mDelegate;
-
-    /** Encapsulates most of the logic for filling the dialog and handling clicks. */
-    private DefaultSearchEngineDialogHelper mHelper;
-
-    /**
-     * Construct the default search engine promo.
-     *
-     * @param activity    Activity to build the dialog with.
-     * @param delegate    Delegate for getting search engine list/selection notification.
-     * @param dialogType  Type of dialog to show.
-     * @param onSuccessCallback Notified whether the user successfully chose a search engine and
-     *                          dismissed the dialog.
-     */
-    public DefaultSearchEnginePromoDialog(Activity activity,
-            DefaultSearchEngineDialogHelper.Delegate delegate, int dialogType,
-            @Nullable Callback<Boolean> onSuccessCallback) {
-        super(activity);
-        mDelegate = delegate;
-        mDialogType = dialogType;
-        mOnSuccessCallback = onSuccessCallback;
-        setOnDismissListener(this);
-
-        // No one should be able to bypass this dialog by clicking outside or by hitting back.
-        setCancelable(false);
-        setCanceledOnTouchOutside(false);
-
-        if (dialogType == SearchEnginePromoType.SHOW_NEW) forceOpaqueBackground();
-    }
-
-    @Override
-    protected DialogParams getDialogParams() {
-        PromoDialog.DialogParams params = new PromoDialog.DialogParams();
-        params.headerStringResource = R.string.search_engine_dialog_title;
-        params.footerStringResource = R.string.search_engine_dialog_footer;
-        params.primaryButtonStringResource = R.string.ok;
-        return params;
-    }
-
-    @Override
-    protected void onCreate(Bundle savedInstanceState) {
-        super.onCreate(savedInstanceState);
-
-        Button okButton = (Button) findViewById(R.id.button_primary);
-        okButton.setEnabled(false);
-
-        RadioButtonLayout radioButtons = new RadioButtonLayout(getContext());
-        radioButtons.setId(R.id.default_search_engine_dialog_options);
-        addControl(radioButtons);
-        mHelper = new DefaultSearchEngineDialogHelper(
-                mDialogType, mDelegate, radioButtons, okButton, this::dismiss);
-    }
-
-    @Override
-    public void show() {
-        super.show();
-        if (sCurrentDialog != null) sCurrentDialog.dismiss();
-        setCurrentDialog(this);
-
-        if (mDialogType == SearchEnginePromoType.SHOW_NEW) {
-            RecordUserAction.record("SearchEnginePromo.NewDevice.Shown.Dialog");
-        } else if (mDialogType == SearchEnginePromoType.SHOW_EXISTING) {
-            RecordUserAction.record("SearchEnginePromo.ExistingDevice.Shown.Dialog");
-        }
-        if (sObserver != null) sObserver.onDialogShown(this);
-    }
-
-    @Override
-    public void onDismiss(DialogInterface dialog) {
-        if (mHelper.getConfirmedKeyword() == null) {
-            // If no selection, finish the Activity so that the user has to respond to the dialog
-            // next time.
-            if (getOwnerActivity() != null) getOwnerActivity().finish();
-        }
-
-        if (mOnSuccessCallback != null) {
-            mOnSuccessCallback.onResult(mHelper.getConfirmedKeyword() != null);
-        }
-
-        if (sCurrentDialog == this) setCurrentDialog(null);
-    }
-
-    /** See {@link #sObserver}. */
-    @VisibleForTesting
-    public static void setObserverForTests2(DefaultSearchEnginePromoDialogObserver observer) {
-        sObserver = observer;
-    }
-
-    /** @return The current visible Default Search Engine dialog. */
-    static DefaultSearchEnginePromoDialog getCurrentDialog() {
-        return sCurrentDialog;
-    }
-
-    private static void setCurrentDialog(DefaultSearchEnginePromoDialog dialog) {
-        sCurrentDialog = dialog;
-    }
-}
diff --git a/chrome/browser/search_engines/android/java/src/org/chromium/chrome/browser/search_engines/Dummy.java b/chrome/browser/search_engines/android/java/src/org/chromium/chrome/browser/search_engines/Dummy.java
new file mode 100644
index 0000000..84ac3f8
--- /dev/null
+++ b/chrome/browser/search_engines/android/java/src/org/chromium/chrome/browser/search_engines/Dummy.java
@@ -0,0 +1,8 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.chrome.browser.search_engines;
+
+/** Dummy class for an empty target. */
+public class Dummy {}
diff --git a/chrome/browser/search_engines/android/java/src/org/chromium/chrome/browser/search_engines/SearchEnginePromoState.java b/chrome/browser/search_engines/android/java/src/org/chromium/chrome/browser/search_engines/SearchEnginePromoState.java
deleted file mode 100644
index 91ea06d0..0000000
--- a/chrome/browser/search_engines/android/java/src/org/chromium/chrome/browser/search_engines/SearchEnginePromoState.java
+++ /dev/null
@@ -1,20 +0,0 @@
-// Copyright 2021 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-package org.chromium.chrome.browser.search_engines;
-
-import androidx.annotation.IntDef;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-
-/** The current state regarding search engine promo dialogs. */
-@IntDef({SearchEnginePromoState.SHOULD_CHECK, SearchEnginePromoState.CHECKED_NOT_SHOWN,
-        SearchEnginePromoState.CHECKED_AND_SHOWN})
-@Retention(RetentionPolicy.SOURCE)
-public @interface SearchEnginePromoState {
-    int SHOULD_CHECK = -1;
-    int CHECKED_NOT_SHOWN = 0;
-    int CHECKED_AND_SHOWN = 1;
-}
diff --git a/chrome/browser/search_engines/android/java/src/org/chromium/chrome/browser/search_engines/SearchEnginePromoType.java b/chrome/browser/search_engines/android/java/src/org/chromium/chrome/browser/search_engines/SearchEnginePromoType.java
deleted file mode 100644
index 2594585f..0000000
--- a/chrome/browser/search_engines/android/java/src/org/chromium/chrome/browser/search_engines/SearchEnginePromoType.java
+++ /dev/null
@@ -1,21 +0,0 @@
-// Copyright 2021 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-package org.chromium.chrome.browser.search_engines;
-
-import androidx.annotation.IntDef;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-
-/** The different types of search engine promo dialogs. */
-@IntDef({SearchEnginePromoType.DONT_SHOW, SearchEnginePromoType.SHOW_SOGOU,
-        SearchEnginePromoType.SHOW_EXISTING, SearchEnginePromoType.SHOW_NEW})
-@Retention(RetentionPolicy.SOURCE)
-public @interface SearchEnginePromoType {
-    int DONT_SHOW = -1;
-    int SHOW_SOGOU = 0;
-    int SHOW_EXISTING = 1;
-    int SHOW_NEW = 2;
-}
diff --git a/chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/send_tab_to_self/SendTabToSelfInfoBarController.java b/chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/send_tab_to_self/SendTabToSelfInfoBarController.java
index 0ef5d89c..26ec9cc 100644
--- a/chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/send_tab_to_self/SendTabToSelfInfoBarController.java
+++ b/chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/send_tab_to_self/SendTabToSelfInfoBarController.java
@@ -6,14 +6,14 @@
 
 import org.chromium.base.supplier.Supplier;
 import org.chromium.chrome.browser.lifecycle.ActivityLifecycleDispatcher;
-import org.chromium.chrome.browser.lifecycle.Destroyable;
+import org.chromium.chrome.browser.lifecycle.DestroyObserver;
 import org.chromium.chrome.browser.tab.Tab;
 
 /**
  * This class is responsible for listening for new SendTabToSelfEntries and showing an infobar
  * to the user if the user does not have notifications enabled.
  */
-public class SendTabToSelfInfoBarController implements Destroyable {
+public class SendTabToSelfInfoBarController implements DestroyObserver {
     private final ActivityLifecycleDispatcher mActivityLifecycleDispatcher;
     private final Supplier<Tab> mTabSupplier;
 
@@ -39,9 +39,9 @@
         mTabSupplier = tabSupplier;
     }
 
-    // Destroyable implementation.
+    // DestroyObserver implementation.
     @Override
-    public void destroy() {
+    public void onDestroy() {
         mActivityLifecycleDispatcher.unregister(this);
     }
 
diff --git a/chrome/browser/signin/BUILD.gn b/chrome/browser/signin/BUILD.gn
new file mode 100644
index 0000000..43e59b04
--- /dev/null
+++ b/chrome/browser/signin/BUILD.gn
@@ -0,0 +1,11 @@
+# Copyright 2021 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD - style license that can be
+# found in the LICENSE file.
+
+source_set("identity_manager_provider") {
+  sources = [
+    "identity_manager_provider.cc",
+    "identity_manager_provider.h",
+  ]
+  deps = [ "//base" ]
+}
diff --git a/chrome/browser/signin/dice_signed_in_profile_creator.cc b/chrome/browser/signin/dice_signed_in_profile_creator.cc
index 2cf43f9..887812f 100644
--- a/chrome/browser/signin/dice_signed_in_profile_creator.cc
+++ b/chrome/browser/signin/dice_signed_in_profile_creator.cc
@@ -126,7 +126,7 @@
                               ? storage.ChooseNameForNewProfile(*icon_index)
                               : local_profile_name;
     ProfileManager::CreateMultiProfileAsync(
-        name, *icon_index,
+        name, *icon_index, /*is_hidden=*/false,
         base::BindRepeating(&DiceSignedInProfileCreator::OnNewProfileCreated,
                             weak_pointer_factory_.GetWeakPtr()));
   }
diff --git a/chrome/browser/signin/identity_manager_factory.cc b/chrome/browser/signin/identity_manager_factory.cc
index 324f929..ef00fe1 100644
--- a/chrome/browser/signin/identity_manager_factory.cc
+++ b/chrome/browser/signin/identity_manager_factory.cc
@@ -15,6 +15,7 @@
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/signin/account_consistency_mode_manager.h"
 #include "chrome/browser/signin/chrome_signin_client_factory.h"
+#include "chrome/browser/signin/identity_manager_provider.h"
 #include "components/keyed_service/content/browser_context_dependency_manager.h"
 #include "components/keyed_service/core/keyed_service.h"
 #include "components/pref_registry/pref_registry_syncable.h"
@@ -60,6 +61,11 @@
   DependsOn(WebDataServiceFactory::GetInstance());
 #endif
   DependsOn(ChromeSigninClientFactory::GetInstance());
+
+  signin::SetIdentityManagerProvider(
+      base::BindRepeating([](content::BrowserContext* context) {
+        return GetForProfile(Profile::FromBrowserContext(context));
+      }));
 }
 
 IdentityManagerFactory::~IdentityManagerFactory() {}
diff --git a/chrome/browser/signin/identity_manager_provider.cc b/chrome/browser/signin/identity_manager_provider.cc
new file mode 100644
index 0000000..f492261
--- /dev/null
+++ b/chrome/browser/signin/identity_manager_provider.cc
@@ -0,0 +1,32 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/signin/identity_manager_provider.h"
+
+#include "base/check.h"
+#include "base/no_destructor.h"
+
+namespace signin {
+
+namespace {
+
+IdentityManagerProvider& GetIdentityManagerProvider() {
+  static base::NoDestructor<IdentityManagerProvider> provider;
+  return *provider;
+}
+
+}  // namespace
+
+void SetIdentityManagerProvider(const IdentityManagerProvider& provider) {
+  IdentityManagerProvider& instance = GetIdentityManagerProvider();
+  DCHECK(!instance);
+  instance = provider;
+}
+
+IdentityManager* GetIdentityManagerForBrowserContext(
+    content::BrowserContext* context) {
+  return GetIdentityManagerProvider().Run(context);
+}
+
+}  // namespace signin
diff --git a/chrome/browser/signin/identity_manager_provider.h b/chrome/browser/signin/identity_manager_provider.h
new file mode 100644
index 0000000..9b6291d
--- /dev/null
+++ b/chrome/browser/signin/identity_manager_provider.h
@@ -0,0 +1,32 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_SIGNIN_IDENTITY_MANAGER_PROVIDER_H_
+#define CHROME_BROWSER_SIGNIN_IDENTITY_MANAGER_PROVIDER_H_
+
+#include "base/callback.h"
+
+namespace content {
+class BrowserContext;
+}
+
+namespace signin {
+
+class IdentityManager;
+
+using IdentityManagerProvider =
+    base::RepeatingCallback<IdentityManager*(content::BrowserContext*)>;
+
+// Called by IdentityManagerFactory to expose a way to retrieve the
+// IdentityManager for a specific BrowserContext/Profile. This exists so that
+// components which don't depend on //chrome/browser can still access the
+// IdentityManager.
+void SetIdentityManagerProvider(const IdentityManagerProvider& provider);
+
+IdentityManager* GetIdentityManagerForBrowserContext(
+    content::BrowserContext* context);
+
+}  // namespace signin
+
+#endif  // CHROME_BROWSER_SIGNIN_IDENTITY_MANAGER_PROVIDER_H_
diff --git a/chrome/browser/signin/signin_util_win_browsertest.cc b/chrome/browser/signin/signin_util_win_browsertest.cc
index f23e854e..f70fa7a 100644
--- a/chrome/browser/signin/signin_util_win_browsertest.cc
+++ b/chrome/browser/signin/signin_util_win_browsertest.cc
@@ -259,8 +259,7 @@
   ASSERT_EQ(1u, profile_manager->GetNumberOfProfiles());
 
   Profile* profile = profile_manager->GetLastUsedProfile();
-  ASSERT_EQ(profile_manager->GetInitialProfileDir(),
-            profile->GetPath().BaseName());
+  ASSERT_EQ(profile_manager->GetInitialProfileDir(), profile->GetBaseName());
 
   Browser* browser = chrome::FindLastActiveWithProfile(profile);
   ASSERT_NE(nullptr, browser);
@@ -452,8 +451,7 @@
   ProfileManager* profile_manager = g_browser_process->profile_manager();
   Profile* profile = profile_manager->GetLastUsedProfile();
 
-  ASSERT_EQ(profile_manager->GetInitialProfileDir(),
-            profile->GetPath().BaseName());
+  ASSERT_EQ(profile_manager->GetInitialProfileDir(), profile->GetBaseName());
 
   if (!GetParam().existing_email.empty()) {
     auto* identity_manager = IdentityManagerFactory::GetForProfile(profile);
@@ -473,8 +471,7 @@
   ASSERT_EQ(1u, profile_manager->GetNumberOfProfiles());
 
   Profile* profile = profile_manager->GetLastUsedProfile();
-  ASSERT_EQ(profile_manager->GetInitialProfileDir(),
-            profile->GetPath().BaseName());
+  ASSERT_EQ(profile_manager->GetInitialProfileDir(), profile->GetBaseName());
 
   AssertSigninStarted(GetParam().expect_is_started, profile);
 
@@ -648,8 +645,7 @@
 
   Profile* profile = profile_manager->GetLastUsedProfile();
 
-  ASSERT_EQ(profile_manager->GetInitialProfileDir(),
-            profile->GetPath().BaseName());
+  ASSERT_EQ(profile_manager->GetInitialProfileDir(), profile->GetBaseName());
 
   auto* identity_manager = IdentityManagerFactory::GetForProfile(profile);
   ASSERT_TRUE(identity_manager);
@@ -676,7 +672,7 @@
   ProfileManager* profile_manager = g_browser_process->profile_manager();
   Profile* profile = profile_manager->GetLastUsedProfile();
 
-  ASSERT_EQ(GetParam().current_profile, profile->GetPath().BaseName().value());
+  ASSERT_EQ(GetParam().current_profile, profile->GetBaseName().value());
 
   auto* identity_manager = IdentityManagerFactory::GetForProfile(profile);
   ASSERT_TRUE(identity_manager);
@@ -701,7 +697,7 @@
   ProfileManager* profile_manager = g_browser_process->profile_manager();
   Profile* profile = profile_manager->GetLastUsedProfile();
 
-  ASSERT_EQ(GetParam().current_profile, profile->GetPath().BaseName().value());
+  ASSERT_EQ(GetParam().current_profile, profile->GetBaseName().value());
   AssertSigninStarted(GetParam().expect_is_started, profile);
 }
 
diff --git a/chrome/browser/sync/chrome_sync_client.cc b/chrome/browser/sync/chrome_sync_client.cc
index 6a8e8add..02b12f2 100644
--- a/chrome/browser/sync/chrome_sync_client.cc
+++ b/chrome/browser/sync/chrome_sync_client.cc
@@ -274,7 +274,7 @@
   // should be considered roamed. For now the code assumes all profiles are
   // created in the same order on all machines.
   local_sync_backend_folder =
-      local_sync_backend_folder.Append(profile_->GetPath().BaseName());
+      local_sync_backend_folder.Append(profile_->GetBaseName());
   local_sync_backend_folder =
       local_sync_backend_folder.Append(kLoopbackServerBackendFilename);
 #endif  // defined(OS_WIN)
diff --git a/chrome/browser/sync/test/integration/single_client_nigori_sync_test.cc b/chrome/browser/sync/test/integration/single_client_nigori_sync_test.cc
index 935fefa..48dd07f 100644
--- a/chrome/browser/sync/test/integration/single_client_nigori_sync_test.cc
+++ b/chrome/browser/sync/test/integration/single_client_nigori_sync_test.cc
@@ -1118,7 +1118,7 @@
   SingleClientNigoriSyncTestWithSecurityDomainsServer()
       : SyncTest(SINGLE_CLIENT) {
     override_features_.InitAndEnableFeature(
-        switches::kFollowTrustedVaultKeyRotation);
+        switches::kSyncSupportTrustedVaultPassphraseRecovery);
   }
   SingleClientNigoriSyncTestWithSecurityDomainsServer(
       const SingleClientNigoriSyncTestWithSecurityDomainsServer& other) =
diff --git a/chrome/browser/sync/test/integration/sync_test.cc b/chrome/browser/sync/test/integration/sync_test.cc
index 323467e..e3101ff 100644
--- a/chrome/browser/sync/test/integration/sync_test.cc
+++ b/chrome/browser/sync/test/integration/sync_test.cc
@@ -1003,7 +1003,7 @@
   // the user data dir, and which we don't use otherwise).
   if (previous_profile_) {
     profiles::SetLastUsedProfile(
-        previous_profile_->GetPath().BaseName().MaybeAsASCII());
+        previous_profile_->GetBaseName().MaybeAsASCII());
   }
 
 #if !defined(OS_ANDROID)
diff --git a/chrome/browser/ui/app_list/app_list_syncable_service.cc b/chrome/browser/ui/app_list/app_list_syncable_service.cc
index 05568e3..6128ffe 100644
--- a/chrome/browser/ui/app_list/app_list_syncable_service.cc
+++ b/chrome/browser/ui/app_list/app_list_syncable_service.cc
@@ -200,7 +200,7 @@
   if (folder_item->item_type != sync_pb::AppListSpecifics::TYPE_FOLDER)
     return false;
   return (folder_item->item_id == ash::kOemFolderId ||
-          folder_item->item_id == crostini::kCrostiniFolderId);
+          folder_item->item_id == ash::kCrostiniFolderId);
 }
 
 }  // namespace
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 1f8f4d0..86f21de7 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
@@ -5,6 +5,7 @@
 #include "chrome/browser/ui/app_list/app_service/app_service_app_item.h"
 
 #include "ash/public/cpp/app_list/app_list_config.h"
+#include "ash/public/cpp/app_list/app_list_types.h"
 #include "ash/public/cpp/shelf_types.h"
 #include "base/bind.h"
 #include "base/check.h"
@@ -52,7 +53,7 @@
     if (app_type_ == apps::mojom::AppType::kCrostini ||
         id() == crostini::kCrostiniTerminalSystemAppId) {
       DCHECK(folder_id().empty());
-      SetChromeFolderId(crostini::kCrostiniFolderId);
+      SetChromeFolderId(ash::kCrostiniFolderId);
     }
   }
 
diff --git a/chrome/browser/ui/app_list/app_service/app_service_app_model_builder.cc b/chrome/browser/ui/app_list/app_service/app_service_app_model_builder.cc
index 68e3d79..2d3fcb98 100644
--- a/chrome/browser/ui/app_list/app_service/app_service_app_model_builder.cc
+++ b/chrome/browser/ui/app_list/app_service/app_service_app_model_builder.cc
@@ -4,6 +4,7 @@
 
 #include "chrome/browser/ui/app_list/app_service/app_service_app_model_builder.h"
 
+#include "ash/public/cpp/app_list/app_list_types.h"
 #include "chrome/browser/apps/app_service/app_service_proxy.h"
 #include "chrome/browser/apps/app_service/app_service_proxy_factory.h"
 #include "chrome/browser/ash/arc/arc_util.h"
@@ -50,12 +51,12 @@
   ~CrostiniFolderObserver() override = default;
 
   void OnAppListItemAdded(ChromeAppListItem* item) override {
-    if (item->id() != crostini::kCrostiniFolderId)
+    if (item->id() != ash::kCrostiniFolderId)
       return;
 
     item->SetIsPersistent(true);
 
-    if (!parent_->GetSyncItem(crostini::kCrostiniFolderId,
+    if (!parent_->GetSyncItem(ash::kCrostiniFolderId,
                               sync_pb::AppListSpecifics::TYPE_FOLDER)) {
       item->SetDefaultPositionIfApplicable(parent_->model_updater());
     }
diff --git a/chrome/browser/ui/app_list/app_service/app_service_app_model_builder_unittest.cc b/chrome/browser/ui/app_list/app_service/app_service_app_model_builder_unittest.cc
index aa91cd0d..8257c82a 100644
--- a/chrome/browser/ui/app_list/app_service/app_service_app_model_builder_unittest.cc
+++ b/chrome/browser/ui/app_list/app_service/app_service_app_model_builder_unittest.cc
@@ -9,6 +9,7 @@
 #include <string>
 
 #include "ash/public/cpp/app_list/app_list_config.h"
+#include "ash/public/cpp/app_list/app_list_types.h"
 #include "base/files/file_path.h"
 #include "base/run_loop.h"
 #include "base/strings/string_util.h"
@@ -710,7 +711,7 @@
       existing_item_ids.emplace_back(pair.first);
     }
     for (const std::string& id : existing_item_ids) {
-      if (id == crostini::kCrostiniFolderId ||
+      if (id == ash::kCrostiniFolderId ||
           id == crostini::kCrostiniTerminalSystemAppId) {
         continue;
       }
@@ -766,10 +767,9 @@
   EXPECT_EQ(0u, GetModelItemCount());
 
   CrostiniTestHelper::EnableCrostini(testing_profile());
-  EXPECT_THAT(GetAllApps(),
-              testing::UnorderedElementsAre(
-                  IsChromeApp(crostini::kCrostiniTerminalSystemAppId,
-                              TerminalAppName(), crostini::kCrostiniFolderId)));
+  EXPECT_THAT(GetAllApps(), testing::UnorderedElementsAre(IsChromeApp(
+                                crostini::kCrostiniTerminalSystemAppId,
+                                TerminalAppName(), ash::kCrostiniFolderId)));
 
   CrostiniTestHelper::DisableCrostini(testing_profile());
   EXPECT_THAT(GetAllApps(), testing::IsEmpty());
@@ -784,19 +784,19 @@
   EXPECT_THAT(GetAllApps(),
               testing::UnorderedElementsAre(
                   IsChromeApp(crostini::kCrostiniTerminalSystemAppId,
-                              TerminalAppName(), crostini::kCrostiniFolderId),
-                  IsChromeApp(_, kDummyApp1Name, crostini::kCrostiniFolderId),
-                  IsChromeApp(_, kDummyApp2Name, crostini::kCrostiniFolderId)));
+                              TerminalAppName(), ash::kCrostiniFolderId),
+                  IsChromeApp(_, kDummyApp1Name, ash::kCrostiniFolderId),
+                  IsChromeApp(_, kDummyApp2Name, ash::kCrostiniFolderId)));
 
   test_helper_->AddApp(
       CrostiniTestHelper::BasicApp(kBananaAppId, kBananaAppName));
   EXPECT_THAT(GetAllApps(),
               testing::UnorderedElementsAre(
                   IsChromeApp(crostini::kCrostiniTerminalSystemAppId,
-                              TerminalAppName(), crostini::kCrostiniFolderId),
-                  IsChromeApp(_, kDummyApp1Name, crostini::kCrostiniFolderId),
-                  IsChromeApp(_, kDummyApp2Name, crostini::kCrostiniFolderId),
-                  IsChromeApp(_, kBananaAppName, crostini::kCrostiniFolderId)));
+                              TerminalAppName(), ash::kCrostiniFolderId),
+                  IsChromeApp(_, kDummyApp1Name, ash::kCrostiniFolderId),
+                  IsChromeApp(_, kDummyApp2Name, ash::kCrostiniFolderId),
+                  IsChromeApp(_, kBananaAppName, ash::kCrostiniFolderId)));
 }
 
 // Test that the app model builder correctly picks up changes to existing apps.
@@ -856,25 +856,24 @@
 
 // Tests that the crostini folder is (re)created with the correct parameters.
 TEST_F(CrostiniAppTest, CreatesFolder) {
-  EXPECT_THAT(GetAllApps(),
-              testing::UnorderedElementsAre(
-                  IsChromeApp(crostini::kCrostiniTerminalSystemAppId,
-                              TerminalAppName(), crostini::kCrostiniFolderId)));
+  EXPECT_THAT(GetAllApps(), testing::UnorderedElementsAre(IsChromeApp(
+                                crostini::kCrostiniTerminalSystemAppId,
+                                TerminalAppName(), ash::kCrostiniFolderId)));
 
   // We simulate ash creating the crostini folder and calling back into chrome
   // (rather than use a full browser test).
   auto metadata = std::make_unique<ash::AppListItemMetadata>();
-  metadata->id = crostini::kCrostiniFolderId;
+  metadata->id = ash::kCrostiniFolderId;
   metadata->is_folder = true;
   GetModelUpdater()->OnItemAdded(std::move(metadata));
 
   EXPECT_THAT(GetAllApps(),
               testing::UnorderedElementsAre(
                   IsChromeApp(crostini::kCrostiniTerminalSystemAppId,
-                              TerminalAppName(), crostini::kCrostiniFolderId),
-                  testing::AllOf(IsChromeApp(crostini::kCrostiniFolderId,
-                                             kRootFolderName, ""),
-                                 IsPersistentApp())));
+                              TerminalAppName(), ash::kCrostiniFolderId),
+                  testing::AllOf(
+                      IsChromeApp(ash::kCrostiniFolderId, kRootFolderName, ""),
+                      IsPersistentApp())));
 }
 
 // Test that the Terminal app is removed when Crostini is disabled.
diff --git a/chrome/browser/ui/autofill/save_update_address_profile_bubble_controller_impl.cc b/chrome/browser/ui/autofill/save_update_address_profile_bubble_controller_impl.cc
index b05b959a..2e11262 100644
--- a/chrome/browser/ui/autofill/save_update_address_profile_bubble_controller_impl.cc
+++ b/chrome/browser/ui/autofill/save_update_address_profile_bubble_controller_impl.cc
@@ -46,6 +46,8 @@
     const {
   // TODO(crbug.com/1167060): Use ineternationalized string upon having final
   // strings.
+  // TODO(crbug.com/1167060): Update prompt title should reflect the fields that
+  // are being updated.
   return original_profile_ ? u"Update Address?" : u"Save Address?";
 }
 
@@ -100,9 +102,7 @@
 
 PageActionIconType
 SaveUpdateAddressProfileBubbleControllerImpl::GetPageActionIconType() {
-  // TODO(crbug.com/1167060): Switch to PageActionIconType::kSaveAutofillAddress
-  // once there are acceesible name for the page icon view.
-  return PageActionIconType::kSaveCard;
+  return PageActionIconType::kSaveAutofillAddress;
 }
 
 void SaveUpdateAddressProfileBubbleControllerImpl::DoShowBubble() {
diff --git a/chrome/browser/ui/bookmarks/bookmark_browsertest.cc b/chrome/browser/ui/bookmarks/bookmark_browsertest.cc
index 0ead13e..517db46 100644
--- a/chrome/browser/ui/bookmarks/bookmark_browsertest.cc
+++ b/chrome/browser/ui/bookmarks/bookmark_browsertest.cc
@@ -167,7 +167,7 @@
   BookmarkModel* bookmark_model1 = WaitForBookmarkModel(browser()->profile());
 
   g_browser_process->profile_manager()->CreateMultiProfileAsync(
-      u"New Profile", 0, ProfileManager::CreateCallback());
+      u"New Profile", 0, false, ProfileManager::CreateCallback());
   Browser* browser2 = ui_test_utils::WaitForBrowserToOpen();
   BookmarkModel* bookmark_model2 = WaitForBookmarkModel(browser2->profile());
 
diff --git a/chrome/browser/ui/startup/startup_browser_creator_corrupt_profiles_browsertest_win.cc b/chrome/browser/ui/startup/startup_browser_creator_corrupt_profiles_browsertest_win.cc
index f69864c..4deaf8f 100644
--- a/chrome/browser/ui/startup/startup_browser_creator_corrupt_profiles_browsertest_win.cc
+++ b/chrome/browser/ui/startup/startup_browser_creator_corrupt_profiles_browsertest_win.cc
@@ -77,7 +77,7 @@
   std::vector<std::string> actual_basepaths;
   for (const Browser* browser : *BrowserList::GetInstance()) {
     actual_basepaths.push_back(
-        browser->profile()->GetPath().BaseName().AsUTF8Unsafe());
+        browser->profile()->GetBaseName().AsUTF8Unsafe());
   }
 
   if (actual_basepaths.size() != expected_basepaths.size() ||
diff --git a/chrome/browser/ui/ui_features.cc b/chrome/browser/ui/ui_features.cc
index 7205ec2c..9240f12 100644
--- a/chrome/browser/ui/ui_features.cc
+++ b/chrome/browser/ui/ui_features.cc
@@ -67,10 +67,6 @@
 // Hosts some content in a side panel. https://crbug.com/1149995
 const base::Feature kSidePanel{"SidePanel", base::FEATURE_DISABLED_BY_DEFAULT};
 
-// Displays a prototype of the side panel. https://crbug.com/1181931
-const base::Feature kSidePanelPrototype{"SidePanelPrototype",
-                                        base::FEATURE_DISABLED_BY_DEFAULT};
-
 // Updated managed profile sign-in popup. https://crbug.com/1141224
 const base::Feature kSyncConfirmationUpdatedText{
     "SyncConfirmationUpdatedText", base::FEATURE_DISABLED_BY_DEFAULT};
diff --git a/chrome/browser/ui/ui_features.h b/chrome/browser/ui/ui_features.h
index a71c77f..7a5e3382 100644
--- a/chrome/browser/ui/ui_features.h
+++ b/chrome/browser/ui/ui_features.h
@@ -50,8 +50,6 @@
 
 extern const base::Feature kSidePanel;
 
-extern const base::Feature kSidePanelPrototype;
-
 extern const base::Feature kSyncConfirmationUpdatedText;
 
 extern const base::Feature kSignInProfileCreation;
diff --git a/chrome/browser/ui/views/autofill/save_address_profile_icon_view.cc b/chrome/browser/ui/views/autofill/save_address_profile_icon_view.cc
index 10af429..ebad3aa 100644
--- a/chrome/browser/ui/views/autofill/save_address_profile_icon_view.cc
+++ b/chrome/browser/ui/views/autofill/save_address_profile_icon_view.cc
@@ -44,7 +44,7 @@
 std::u16string SaveAddressProfileIconView::GetTextForTooltipAndAccessibleName()
     const {
   // TODO(crbug.com/1167060): Update upon having final mocks.
-  return std::u16string();
+  return u"Save Address";
 }
 
 void SaveAddressProfileIconView::OnExecuting(
diff --git a/chrome/browser/ui/views/autofill/save_address_profile_view.cc b/chrome/browser/ui/views/autofill/save_address_profile_view.cc
index fcf06df..18ef7c9 100644
--- a/chrome/browser/ui/views/autofill/save_address_profile_view.cc
+++ b/chrome/browser/ui/views/autofill/save_address_profile_view.cc
@@ -189,6 +189,10 @@
       controller_(controller) {
   DCHECK(base::FeatureList::IsEnabled(
       features::kAutofillAddressProfileSavePrompt));
+  // Since this is a save prompt, original profile must not be set. Otherwise,
+  // it would have been an update prompt.
+  DCHECK(!controller_->GetOriginalProfile());
+
   // TODO(crbug.com/1167060): Accept action should consider the selected
   // nickname when saving the address.
   SetAcceptCallback(base::BindOnce(
diff --git a/chrome/browser/ui/views/autofill/update_address_profile_view.cc b/chrome/browser/ui/views/autofill/update_address_profile_view.cc
index cfa386b..8a5e853 100644
--- a/chrome/browser/ui/views/autofill/update_address_profile_view.cc
+++ b/chrome/browser/ui/views/autofill/update_address_profile_view.cc
@@ -4,13 +4,131 @@
 
 #include "chrome/browser/ui/views/autofill/update_address_profile_view.h"
 
+#include "chrome/app/vector_icons/vector_icons.h"
+#include "chrome/browser/browser_process.h"
 #include "chrome/browser/ui/autofill/save_update_address_profile_bubble_controller.h"
 #include "chrome/browser/ui/views/accessibility/theme_tracking_non_accessible_image_view.h"
+#include "chrome/browser/ui/views/chrome_layout_provider.h"
 #include "chrome/grit/theme_resources.h"
+#include "components/autofill/core/browser/data_model/autofill_profile_comparator.h"
 #include "components/autofill/core/common/autofill_features.h"
+#include "components/vector_icons/vector_icons.h"
+#include "ui/gfx/favicon_size.h"
+#include "ui/gfx/paint_vector_icon.h"
+#include "ui/views/controls/image_view.h"
+#include "ui/views/layout/flex_layout.h"
+#include "ui/views/layout/grid_layout.h"
 
 namespace autofill {
 
+namespace {
+
+constexpr int kColumnSetId = 0;
+
+// New and old values appear in the update prompt in the same order as the order
+// of the types in this array.
+const ServerFieldType user_visibe_type[] = {NAME_HONORIFIC_PREFIX,
+                                            NAME_FULL,
+                                            ADDRESS_HOME_STREET_ADDRESS,
+                                            ADDRESS_HOME_CITY,
+                                            ADDRESS_HOME_ZIP,
+                                            ADDRESS_HOME_COUNTRY,
+                                            EMAIL_ADDRESS,
+                                            PHONE_HOME_WHOLE_NUMBER,
+                                            COMPANY_NAME};
+
+const gfx::VectorIcon& GetVectorIconForType(ServerFieldType type) {
+  // TODO(crbug.com/1167060): Update icons upon having final mocks.
+  switch (type) {
+    case NAME_FULL:
+    case NAME_HONORIFIC_PREFIX:
+      return kUserAccountAvatarIcon;
+    case EMAIL_ADDRESS:
+      return kWebIcon;
+    case PHONE_HOME_WHOLE_NUMBER:
+      return vector_icons::kCallIcon;
+    default:
+      return vector_icons::kLocationOnIcon;
+  }
+}
+
+std::unique_ptr<views::ImageView> CreateIconViewForType(ServerFieldType type,
+                                                        bool for_new_value) {
+  auto icon_view = std::make_unique<views::ImageView>();
+  icon_view->SetImage(ui::ImageModel::FromVectorIcon(
+      GetVectorIconForType(type),
+      for_new_value ? ui::NativeTheme::kColorId_ProminentButtonColor
+                    : ui::NativeTheme::kColorId_DefaultIconColor,
+      gfx::kFaviconSize));
+  return icon_view;
+}
+
+// Creates a view that displays all values in `differences`. `are_new_values`
+// decides which set of values from `differences` are displayed.
+std::unique_ptr<views::View> CreateValuesView(
+    const std::vector<ProfileValueDifference>& differences,
+    bool are_new_values) {
+  auto view = std::make_unique<views::View>();
+  view->SetLayoutManager(std::make_unique<views::FlexLayout>())
+      ->SetOrientation(views::LayoutOrientation::kVertical)
+      .SetCrossAxisAlignment(views::LayoutAlignment::kStretch)
+      .SetIgnoreDefaultMainAxisMargins(true)
+      .SetCollapseMargins(true)
+      .SetDefault(
+          views::kMarginsKey,
+          gfx::Insets(
+              /*vertical=*/ChromeLayoutProvider::Get()->GetDistanceMetric(
+                  DISTANCE_CONTROL_LIST_VERTICAL),
+              /*horizontal=*/0));
+
+  for (const ProfileValueDifference& difference : differences) {
+    views::View* value_row =
+        view->AddChildView(std::make_unique<views::View>());
+    value_row->SetLayoutManager(std::make_unique<views::FlexLayout>())
+        ->SetOrientation(views::LayoutOrientation::kHorizontal)
+        .SetCrossAxisAlignment(views::LayoutAlignment::kCenter)
+        .SetCollapseMargins(true)
+        .SetDefault(
+            views::kMarginsKey,
+            gfx::Insets(
+                /*vertical=*/0,
+                /*horizontal=*/ChromeLayoutProvider::Get()->GetDistanceMetric(
+                    views::DISTANCE_RELATED_CONTROL_HORIZONTAL)));
+
+    value_row->AddChildView(
+        CreateIconViewForType(difference.type, are_new_values));
+    value_row->AddChildView(std::make_unique<views::Label>(
+        are_new_values ? difference.first_value : difference.second_value,
+        views::style::CONTEXT_LABEL));
+  }
+  return view;
+}
+
+// Add a row in `layout` that contains a label and a view displays all the
+// values in `differences`. `are_new_values` controls the displayed label and
+// which set of values from `differences` are displayed.
+void AddValuesRow(views::GridLayout* layout,
+                  const std::vector<ProfileValueDifference>& differences,
+                  bool are_new_values) {
+  layout->StartRow(/*vertical_resize=*/views::GridLayout::kFixedSize,
+                   kColumnSetId);
+
+  // TODO(crbug.com/1167060): Use internationalized string.
+  std::unique_ptr<views::Label> label(new views::Label(
+      are_new_values ? u"New" : u"Old", views::style::CONTEXT_LABEL,
+      views::style::STYLE_PRIMARY));
+  layout->AddView(std::move(label), /*col_span=*/1, /*row_span=*/1,
+                  /*h_align=*/views::GridLayout::LEADING,
+                  /*v_align=*/views::GridLayout::LEADING);
+  layout->AddView(CreateValuesView(differences, are_new_values),
+                  /*col_span=*/1,
+                  /*row_span=*/1,
+                  /*h_align=*/views::GridLayout::FILL,
+                  /*v_align=*/views::GridLayout::FILL);
+}
+
+}  // namespace
+
 UpdateAddressProfileView::UpdateAddressProfileView(
     views::View* anchor_view,
     content::WebContents* web_contents,
@@ -19,6 +137,10 @@
       controller_(controller) {
   DCHECK(base::FeatureList::IsEnabled(
       features::kAutofillAddressProfileSavePrompt));
+  // Since this is an update prompt, original profile must be set. Otherwise, it
+  // would have been a save prompt.
+  DCHECK(controller_->GetOriginalProfile());
+
   SetAcceptCallback(base::BindOnce(
       &SaveUpdateAddressProfileBubbleController::OnUserDecision,
       base::Unretained(controller_),
@@ -27,6 +149,50 @@
       &SaveUpdateAddressProfileBubbleController::OnUserDecision,
       base::Unretained(controller_),
       AutofillClient::SaveAddressProfileOfferUserDecision::kDeclined));
+
+  base::flat_map<ServerFieldType, std::pair<std::u16string, std::u16string>>
+      differences =
+          AutofillProfileComparator::GetSettingsVisibleProfileDifferenceMap(
+              controller_->GetProfileToSave(),
+              *controller_->GetOriginalProfile(),
+              g_browser_process->GetApplicationLocale());
+  std::vector<ProfileValueDifference> diff_vector;
+  for (auto type : user_visibe_type) {
+    const auto it = differences.find(type);
+    if (it == differences.end())
+      continue;
+    diff_vector.emplace_back(
+        ProfileValueDifference{type, it->second.first, it->second.second});
+  }
+
+  // Build the GridLayout column set.
+  views::GridLayout* layout =
+      SetLayoutManager(std::make_unique<views::GridLayout>());
+  views::ColumnSet* column_set = layout->AddColumnSet(kColumnSetId);
+  const int column_divider = ChromeLayoutProvider::Get()->GetDistanceMetric(
+      views::DISTANCE_RELATED_CONTROL_HORIZONTAL);
+  column_set->AddColumn(
+      /*h_align=*/views::GridLayout::LEADING,
+      /*v_align=*/views::GridLayout::LEADING,
+      /*resize_percent=*/views::GridLayout::kFixedSize,
+      /*size_type=*/views::GridLayout::ColumnSize::kUsePreferred,
+      /*fixed_width=*/0, /*min_width=*/0);
+  column_set->AddPaddingColumn(views::GridLayout::kFixedSize, column_divider);
+  column_set->AddColumn(
+      /*h_align=*/views::GridLayout::FILL,
+      /*v_align=*/views::GridLayout::FILL,
+      /*resize_percent=*/1.0,
+      /*size_type=*/views::GridLayout::ColumnSize::kUsePreferred,
+      /*fixed_width=*/0, /*min_width=*/0);
+
+  AddValuesRow(layout, diff_vector, /*are_new_values=*/true);
+  layout->AddPaddingRow(views::GridLayout::kFixedSize,
+                        ChromeLayoutProvider::Get()->GetDistanceMetric(
+                            DISTANCE_CONTROL_LIST_VERTICAL));
+  AddValuesRow(layout, diff_vector, /*are_new_values=*/false);
+
+  // TODO(crbug.com/1167060): Add support for dark mode.
+  // TODO(crbug.com/1167060): Add entry to the edit dialog.
 }
 
 bool UpdateAddressProfileView::ShouldShowCloseButton() const {
@@ -61,16 +227,4 @@
   controller_ = nullptr;
 }
 
-void UpdateAddressProfileView::AddedToWidget() {
-  // TODO(crbug.com/1167060): Update upon having final mocks.
-  // Set the header image.
-  ui::ResourceBundle& bundle = ui::ResourceBundle::GetSharedInstance();
-  auto image_view = std::make_unique<ThemeTrackingNonAccessibleImageView>(
-      *bundle.GetImageSkiaNamed(IDR_SAVE_PASSWORD_MULTI_DEVICE),
-      *bundle.GetImageSkiaNamed(IDR_SAVE_PASSWORD_MULTI_DEVICE_DARK),
-      base::BindRepeating(&views::BubbleFrameView::GetBackgroundColor,
-                          base::Unretained(GetBubbleFrameView())));
-  GetBubbleFrameView()->SetHeaderView(std::move(image_view));
-}
-
 }  // namespace autofill
diff --git a/chrome/browser/ui/views/autofill/update_address_profile_view.h b/chrome/browser/ui/views/autofill/update_address_profile_view.h
index 0badda3..ed46b433 100644
--- a/chrome/browser/ui/views/autofill/update_address_profile_view.h
+++ b/chrome/browser/ui/views/autofill/update_address_profile_view.h
@@ -42,9 +42,6 @@
   // AutofillBubbleBase:
   void Hide() override;
 
-  // View:
-  void AddedToWidget() override;
-
  private:
   SaveUpdateAddressProfileBubbleController* controller_;
 };
diff --git a/chrome/browser/ui/views/autofill/update_address_profile_view_unittest.cc b/chrome/browser/ui/views/autofill/update_address_profile_view_unittest.cc
index c12b097f..788a9d1 100644
--- a/chrome/browser/ui/views/autofill/update_address_profile_view_unittest.cc
+++ b/chrome/browser/ui/views/autofill/update_address_profile_view_unittest.cc
@@ -63,6 +63,7 @@
   const AutofillProfile& address_profile_to_save() {
     return address_profile_to_save_;
   }
+  const AutofillProfile* original_profile() { return &original_profile_; }
   UpdateAddressProfileView* view() { return view_; }
   MockSaveUpdateAddressProfileBubbleController* mock_controller() {
     return &mock_controller_;
@@ -71,7 +72,10 @@
  private:
   base::test::ScopedFeatureList feature_list_;
   TestingProfile profile_;
-  AutofillProfile address_profile_to_save_;
+
+  AutofillProfile address_profile_to_save_ = test::GetFullProfile();
+  AutofillProfile original_profile_ = test::GetFullProfile2();
+
   // This enables uses of TestWebContents.
   content::RenderViewHostTestEnabler test_render_host_factories_;
   std::unique_ptr<content::WebContents> test_web_contents_;
@@ -86,6 +90,8 @@
       .WillByDefault(testing::Return(std::u16string()));
   ON_CALL(*mock_controller(), GetProfileToSave())
       .WillByDefault(testing::ReturnRef(address_profile_to_save()));
+  ON_CALL(*mock_controller(), GetOriginalProfile())
+      .WillByDefault(testing::Return(original_profile()));
 
   // The bubble needs the parent as an anchor.
   views::Widget::InitParams params =
diff --git a/chrome/browser/ui/views/chrome_browser_main_extra_parts_views.cc b/chrome/browser/ui/views/chrome_browser_main_extra_parts_views.cc
index 4a0e15c..a007de2f 100644
--- a/chrome/browser/ui/views/chrome_browser_main_extra_parts_views.cc
+++ b/chrome/browser/ui/views/chrome_browser_main_extra_parts_views.cc
@@ -31,8 +31,10 @@
 #include "content/public/browser/browser_context.h"
 #include "content/public/browser/browser_thread.h"
 #include "services/viz/public/cpp/gpu/gpu.h"  // nogncheck
+#if !BUILDFLAG(IS_CHROMEOS_ASH)
 #include "ui/display/screen.h"
 #include "ui/views/widget/desktop_aura/desktop_screen.h"
+#endif
 #include "ui/wm/core/wm_state.h"
 #endif  // defined(USE_AURA)
 
@@ -106,7 +108,9 @@
 
 void ChromeBrowserMainExtraPartsViews::PreCreateThreads() {
 #if defined(USE_AURA) && !BUILDFLAG(IS_CHROMEOS_ASH)
-  views::InstallDesktopScreenIfNecessary();
+  // The Screen instance may already be set in tests.
+  if (!display::Screen::GetScreen())
+    screen_ = views::CreateDesktopScreen();
 #endif
 }
 
diff --git a/chrome/browser/ui/views/chrome_browser_main_extra_parts_views.h b/chrome/browser/ui/views/chrome_browser_main_extra_parts_views.h
index 608162d..b996ec7 100644
--- a/chrome/browser/ui/views/chrome_browser_main_extra_parts_views.h
+++ b/chrome/browser/ui/views/chrome_browser_main_extra_parts_views.h
@@ -8,6 +8,7 @@
 #include <memory>
 
 #include "base/macros.h"
+#include "build/build_config.h"
 #include "chrome/browser/chrome_browser_main_extra_parts.h"
 #include "ui/views/layout/layout_provider.h"
 
@@ -20,6 +21,11 @@
 }
 
 #if defined(USE_AURA)
+#if !BUILDFLAG(IS_CHROMEOS_ASH)
+namespace display {
+class Screen;
+}
+#endif
 namespace wm {
 class WMState;
 }
@@ -57,6 +63,9 @@
   std::unique_ptr<DevtoolsProcessObserver> devtools_process_observer_;
 
 #if defined(USE_AURA)
+#if !BUILDFLAG(IS_CHROMEOS_ASH)
+  std::unique_ptr<display::Screen> screen_;
+#endif
   std::unique_ptr<wm::WMState> wm_state_;
 #endif
 
diff --git a/chrome/browser/ui/views/frame/browser_window_property_manager_browsertest_win.cc b/chrome/browser/ui/views/frame/browser_window_property_manager_browsertest_win.cc
index 0a060c4..7b63bad 100644
--- a/chrome/browser/ui/views/frame/browser_window_property_manager_browsertest_win.cc
+++ b/chrome/browser/ui/views/frame/browser_window_property_manager_browsertest_win.cc
@@ -84,7 +84,7 @@
   EXPECT_EQ(VT_LPWSTR, prop_var.get().vt);
   base::CommandLine cmd_line(
       base::CommandLine::FromString(prop_var.get().pwszVal));
-  EXPECT_EQ(browser->profile()->GetPath().BaseName().value(),
+  EXPECT_EQ(browser->profile()->GetBaseName().value(),
             cmd_line.GetSwitchValueNative(switches::kProfileDirectory));
   prop_var.Reset();
 
@@ -125,7 +125,7 @@
   EXPECT_EQ(VT_LPWSTR, prop_var.get().vt);
   base::CommandLine cmd_line(
       base::CommandLine::FromString(prop_var.get().pwszVal));
-  EXPECT_EQ(browser->profile()->GetPath().BaseName().value(),
+  EXPECT_EQ(browser->profile()->GetBaseName().value(),
             cmd_line.GetSwitchValueNative(switches::kProfileDirectory));
   EXPECT_EQ(base::UTF8ToWide(extension->id()),
             cmd_line.GetSwitchValueNative(switches::kAppId));
diff --git a/chrome/browser/ui/views/profiles/profile_picker_sign_in_flow_controller.cc b/chrome/browser/ui/views/profiles/profile_picker_sign_in_flow_controller.cc
index eec3e88..7dfffa6 100644
--- a/chrome/browser/ui/views/profiles/profile_picker_sign_in_flow_controller.cc
+++ b/chrome/browser/ui/views/profiles/profile_picker_sign_in_flow_controller.cc
@@ -175,13 +175,6 @@
     return;
   }
 
-  // Mark this profile ephemeral so that it is deleted upon next startup if the
-  // browser crashes before finishing the flow.
-  entry->SetIsEphemeral(true);
-  // Mark this profile as omitted so that it is not displayed in the list of
-  // profiles.
-  entry->SetIsOmitted(true);
-
   // Record that the sign in process starts (its end is recorded automatically
   // by the instance of DiceTurnSyncOnHelper constructed later on).
   signin_metrics::RecordSigninUserActionForAccessPoint(
diff --git a/chrome/browser/ui/views/profiles/profile_picker_view.cc b/chrome/browser/ui/views/profiles/profile_picker_view.cc
index 5996f74..bdf166b 100644
--- a/chrome/browser/ui/views/profiles/profile_picker_view.cc
+++ b/chrome/browser/ui/views/profiles/profile_picker_view.cc
@@ -550,7 +550,7 @@
       g_browser_process->profile_manager()
           ->GetProfileAttributesStorage()
           .ChooseNameForNewProfile(icon_index),
-      icon_index,
+      icon_index, /*is_hidden=*/true,
       base::BindRepeating(&ProfilePickerView::OnProfileForSigninCreated,
                           weak_ptr_factory_.GetWeakPtr(), profile_color,
                           base::AdaptCallbackForRepeating(
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 1c86aee..a737261 100644
--- a/chrome/browser/ui/views/profiles/profile_picker_view_browsertest.cc
+++ b/chrome/browser/ui/views/profiles/profile_picker_view_browsertest.cc
@@ -721,7 +721,7 @@
   base::RunLoop run_loop;
   Profile* second_profile = nullptr;
   ProfileManager::CreateMultiProfileAsync(
-      u"Joe", /*icon_index=*/0,
+      u"Joe", /*icon_index=*/0, /*is_hidden=*/false,
       base::BindLambdaForTesting(
           [&](Profile* profile, Profile::CreateStatus status) {
             if (status == Profile::CREATE_STATUS_INITIALIZED) {
diff --git a/chrome/browser/ui/views/qrcode_generator/qrcode_generator_bubble.cc b/chrome/browser/ui/views/qrcode_generator/qrcode_generator_bubble.cc
index 654949a2..8b4e9c51 100644
--- a/chrome/browser/ui/views/qrcode_generator/qrcode_generator_bubble.cc
+++ b/chrome/browser/ui/views/qrcode_generator/qrcode_generator_bubble.cc
@@ -162,8 +162,6 @@
 
 void QRCodeGeneratorBubble::UpdateQRImage(gfx::ImageSkia qr_image) {
   qr_code_image_->SetImage(qr_image);
-  qr_code_image_->SetImageSize(GetQRCodeImageSize());
-  qr_code_image_->SetPreferredSize(GetQRCodeImageSize());
   qr_code_image_->SetVisible(true);
 }
 
@@ -239,7 +237,8 @@
   qr_code_image->SetHorizontalAlignment(Alignment::kCenter);
   qr_code_image->SetVerticalAlignment(Alignment::kCenter);
   qr_code_image->SetImageSize(GetQRCodeImageSize());
-  qr_code_image->SetPreferredSize(GetQRCodeImageSize());
+  qr_code_image->SetPreferredSize(GetQRCodeImageSize() +
+                                  gfx::Size(border_radius, border_radius));
   qr_code_image->SetBackground(
       views::CreateRoundedRectBackground(SK_ColorWHITE, border_radius));
 
diff --git a/chrome/browser/ui/views/test/view_event_test_base.cc b/chrome/browser/ui/views/test/view_event_test_base.cc
index b37f8c4b..0282df40 100644
--- a/chrome/browser/ui/views/test/view_event_test_base.cc
+++ b/chrome/browser/ui/views/test/view_event_test_base.cc
@@ -112,7 +112,7 @@
         views::test::TestDesktopScreenOzone::GetInstance());
 #endif
   if (!display::Screen::GetScreen())
-    screen_.reset(views::CreateDesktopScreen());
+    screen_ = views::CreateDesktopScreen();
 #endif
 }
 
diff --git a/chrome/browser/ui/views/webid/webid_dialog_views.cc b/chrome/browser/ui/views/webid/webid_dialog_views.cc
index 69f8b1d4..8f41f140 100644
--- a/chrome/browser/ui/views/webid/webid_dialog_views.cc
+++ b/chrome/browser/ui/views/webid/webid_dialog_views.cc
@@ -7,11 +7,9 @@
 #include <memory>
 #include "base/bind.h"
 #include "base/callback.h"
-#include "base/debug/dump_without_crashing.h"
 #include "chrome/browser/ui/views/webid/webid_permission_view.h"
 #include "chrome/browser/ui/views/webid/webid_signin_page_view.h"
 #include "components/constrained_window/constrained_window_views.h"
-#include "content/public/common/content_features.h"
 #include "ui/gfx/native_widget_types.h"
 #include "ui/views/bubble/bubble_frame_view.h"
 #include "ui/views/layout/flex_layout.h"
@@ -27,11 +25,6 @@
 
 WebIdDialogViews::WebIdDialogViews(content::WebContents* rp_web_contents)
     : WebIdDialogViews(rp_web_contents, nullptr) {
-  // https://crbug.com/1195781: It appears that there are crashes in this
-  // file without the WebID flag enabled, which should be impossible.
-  if (!base::FeatureList::IsEnabled(features::kWebID)) {
-    base::debug::DumpWithoutCrashing();
-  }
 }
 
 WebIdDialogViews::WebIdDialogViews(content::WebContents* rp_web_contents,
diff --git a/chrome/browser/ui/webui/chromeos/network_element_localized_strings_provider.cc b/chrome/browser/ui/webui/chromeos/network_element_localized_strings_provider.cc
index 47dddfd..fd288f1 100644
--- a/chrome/browser/ui/webui/chromeos/network_element_localized_strings_provider.cc
+++ b/chrome/browser/ui/webui/chromeos/network_element_localized_strings_provider.cc
@@ -315,6 +315,8 @@
        IDS_SETTINGS_INTERNET_NETWORK_SIM_ENTER_PIN_TITLE},
       {"networkSimEnterPuk", IDS_SETTINGS_INTERNET_NETWORK_SIM_ENTER_PUK},
       {"networkSimLockEnable", IDS_SETTINGS_INTERNET_NETWORK_SIM_LOCK_ENABLE},
+      {"networkSimLockEnableSublabel",
+       IDS_SETTINGS_INTERNET_NETWORK_SIM_LOCK_ENABLE_SUBLABEL},
       {"networkSimLockedTitle", IDS_SETTINGS_INTERNET_NETWORK_SIM_LOCKED_TITLE},
       {"networkSimPukDialogSubtitle",
        IDS_SETTINGS_INTERNET_NETWORK_SIM_LOCKED_PUK_SUBTITLE},
diff --git a/chrome/browser/ui/webui/memories/memories.mojom b/chrome/browser/ui/webui/memories/memories.mojom
index f4e1b7fc..8c53b922 100644
--- a/chrome/browser/ui/webui/memories/memories.mojom
+++ b/chrome/browser/ui/webui/memories/memories.mojom
@@ -38,14 +38,23 @@
   // Note: Returns mock Memories in non-chrome-branded builds if no remote
   // endpoint is specified.
   QueryMemories(QueryParams query_params);
+
+  // Requests to remove all visits to the specified URLs in the specified
+  // timespan in |visits|. This includes the less recent visits to the same set
+  // of URLs whose information is preserved in |visits|. The returned Promise
+  // indicates if the request was accepted by the browser.
+  RemoveVisits(array<Visit> visits) => (bool accepted);
 };
 
 // WebUI-side handler for requests from the browser.
 interface Page {
-
   // Called with the results of the last call to |QueryMemories()|. |result|
   // contains the freshest Memories in reverse chronological order, along with
   // continuation query params meant to be used in the follow-up request to load
   // older Memories.
   OnMemoriesQueryResult(MemoriesResult result);
+
+  // Called with the set of removed visits when the last accepted call to
+  // |RemoveVisits()| succeeds. |removed_visits| will be used to update the UI.
+  OnVisitsRemoved(array<Visit> removed_visits);
 };
diff --git a/chrome/browser/ui/webui/memories/memories_handler.cc b/chrome/browser/ui/webui/memories/memories_handler.cc
index 20d7593..88cde49 100644
--- a/chrome/browser/ui/webui/memories/memories_handler.cc
+++ b/chrome/browser/ui/webui/memories/memories_handler.cc
@@ -107,6 +107,34 @@
   }
 }
 
+void MemoriesHandler::RemoveVisits(
+    std::vector<history_clusters::mojom::VisitPtr> visits,
+    RemoveVisitsCallback callback) {
+  // Reject the request if a pending task exists or the set of visits is empty.
+  if (remove_task_tracker_.HasTrackedTasks() || visits.empty()) {
+    std::move(callback).Run(false);
+    return;
+  }
+
+  std::vector<history::ExpireHistoryArgs> expire_list;
+  expire_list.reserve(visits.size());
+  for (const auto& visit_ptr : visits) {
+    expire_list.resize(expire_list.size() + 1);
+    auto& expire_args = expire_list.back();
+    // ExpireHistoryArgs::end_time is not inclusive. Make sure all visits in the
+    // given timespan are removed by adding 1 second to it.
+    expire_args.end_time = visit_ptr->time + base::TimeDelta::FromSeconds(1);
+    expire_args.begin_time = visit_ptr->first_visit_time;
+  }
+  auto* memory_service = MemoriesServiceFactory::GetForBrowserContext(profile_);
+  memory_service->RemoveVisits(
+      expire_list,
+      base::BindOnce(&MemoriesHandler::OnVisitsRemoved,
+                     weak_ptr_factory_.GetWeakPtr(), std::move(visits)),
+      &remove_task_tracker_);
+  std::move(callback).Run(true);
+}
+
 void MemoriesHandler::OnMemoriesDebugMessage(const std::string& message) {
   if (content::RenderFrameHost* rfh = web_contents_->GetMainFrame()) {
     rfh->AddMessageToConsole(blink::mojom::ConsoleMessageLevel::kInfo, message);
@@ -123,6 +151,11 @@
   page_->OnMemoriesQueryResult(std::move(result_mojom));
 }
 
+void MemoriesHandler::OnVisitsRemoved(
+    std::vector<history_clusters::mojom::VisitPtr> visits) {
+  page_->OnVisitsRemoved(std::move(visits));
+}
+
 #if !defined(CHROME_BRANDED)
 void MemoriesHandler::QueryHistoryService(
     history_clusters::mojom::QueryParamsPtr query_params,
@@ -146,7 +179,7 @@
       query_params->recency_threshold.value_or(base::Time::Now());
   // Make sure to look back far enough to find some visits.
   query_options.begin_time =
-      query_options.end_time.LocalMidnight() - base::TimeDelta::FromDays(14);
+      query_options.end_time.LocalMidnight() - base::TimeDelta::FromDays(30);
   std::u16string query = base::UTF8ToUTF16(query_params->query);
   history_service->QueryHistory(
       query, query_options,
@@ -230,6 +263,7 @@
       visit->page_title = base::UTF16ToUTF8(result.title());
       visit->thumbnail_url = GetRandomlySizedThumbnailUrl();
       visit->time = result.visit_time();
+      visit->first_visit_time = result.visit_time();
       visit->relative_date = base::UTF16ToUTF8(ui::TimeFormat::Simple(
           ui::TimeFormat::FORMAT_ELAPSED, ui::TimeFormat::LENGTH_SHORT,
           base::Time::Now() - visit->time));
@@ -246,6 +280,7 @@
             });
         if (duplicate_visit_it != visits.end()) {
           (*duplicate_visit_it)->num_duplicate_visits++;
+          (*duplicate_visit_it)->first_visit_time = visit->time;
           return;
         }
         // For the top visits, if the domain name is seen before, add |visit| to
diff --git a/chrome/browser/ui/webui/memories/memories_handler.h b/chrome/browser/ui/webui/memories/memories_handler.h
index b10d1d3..f9cc1f89 100644
--- a/chrome/browser/ui/webui/memories/memories_handler.h
+++ b/chrome/browser/ui/webui/memories/memories_handler.h
@@ -10,6 +10,7 @@
 #include "base/callback.h"
 #include "base/memory/weak_ptr.h"
 #include "base/scoped_observation.h"
+#include "base/task/cancelable_task_tracker.h"
 #include "chrome/browser/ui/webui/memories/memories.mojom.h"
 #include "components/history_clusters/core/memories.mojom.h"
 #include "components/history_clusters/core/memories_service.h"
@@ -18,10 +19,6 @@
 #include "mojo/public/cpp/bindings/receiver.h"
 #include "mojo/public/cpp/bindings/remote.h"
 
-#if !defined(CHROME_BRANDED)
-#include "base/task/cancelable_task_tracker.h"
-#endif
-
 class Profile;
 
 namespace content {
@@ -51,6 +48,8 @@
       mojo::PendingRemote<history_clusters::mojom::Page> pending_page) override;
   void QueryMemories(
       history_clusters::mojom::QueryParamsPtr query_params) override;
+  void RemoveVisits(std::vector<history_clusters::mojom::VisitPtr> visits,
+                    RemoveVisitsCallback callback) override;
 
   // history_clusters::MemoriesService::Observer:
   void OnMemoriesDebugMessage(const std::string& message) override;
@@ -65,6 +64,9 @@
       history_clusters::mojom::MemoriesResultPtr result_mojom,
       history_clusters::mojom::QueryParamsPtr continuation_query_params,
       std::vector<history_clusters::mojom::MemoryPtr> memory_mojoms);
+  // Called with the set of removed visits. Subsequently, |visits| is sent to
+  // the JS to update the UI.
+  void OnVisitsRemoved(std::vector<history_clusters::mojom::VisitPtr> visits);
 
 #if !defined(CHROME_BRANDED)
   using MemoriesQueryResultsCallback =
@@ -84,6 +86,7 @@
 
   Profile* profile_;
   content::WebContents* web_contents_;
+  base::CancelableTaskTracker remove_task_tracker_;
 
   // Used to observe the service.
   base::ScopedObservation<history_clusters::MemoriesService,
diff --git a/chrome/browser/ui/webui/memories/memories_ui.cc b/chrome/browser/ui/webui/memories/memories_ui.cc
index 55cb359..4cf3ef7 100644
--- a/chrome/browser/ui/webui/memories/memories_ui.cc
+++ b/chrome/browser/ui/webui/memories/memories_ui.cc
@@ -28,6 +28,12 @@
       content::WebUIDataSource::Create(chrome::kChromeUIMemoriesHost);
 
   static constexpr webui::LocalizedString kStrings[] = {
+      {"actionMenuDescription", IDS_HISTORY_ACTION_MENU_DESCRIPTION},
+      {"cancel", IDS_CANCEL},
+      {"remove", IDS_HISTORY_DELETE_PRIOR_VISITS_CONFIRM_BUTTON},
+      {"removeFromHistory", IDS_HISTORY_REMOVE_PAGE},
+      {"removeSelected", IDS_HISTORY_REMOVE_SELECTED_ITEMS},
+      {"removeWarning", IDS_HISTORY_DELETE_PRIOR_VISITS_WARNING},
       {"title", IDS_MEMORIES_PAGE_TITLE},
   };
   source->AddLocalizedStrings(kStrings);
@@ -42,6 +48,7 @@
                     u"From tab groups and bookmarks");
   source->AddString("tabGroupTileCaption", u"Recent tab group");
   source->AddString("relatedSearchesSectionHeader", u"Try searching for");
+  source->AddString("removeAllFromHistory", u"Remove all from history");
 
   webui::SetupWebUIDataSource(
       source, base::make_span(kMemoriesResources, kMemoriesResourcesSize),
diff --git a/chrome/browser/ui/webui/read_later/read_later_ui.cc b/chrome/browser/ui/webui/read_later/read_later_ui.cc
index b737ef9..3edf648 100644
--- a/chrome/browser/ui/webui/read_later/read_later_ui.cc
+++ b/chrome/browser/ui/webui/read_later/read_later_ui.cc
@@ -63,13 +63,12 @@
       profile, std::make_unique<FaviconSource>(
                    profile, chrome::FaviconUrlFormat::kFavicon2));
 
-  const bool show_side_panel_prototype =
-      base::FeatureList::IsEnabled(features::kSidePanel) &&
-      base::FeatureList::IsEnabled(features::kSidePanelPrototype);
+  const bool show_side_panel =
+      base::FeatureList::IsEnabled(features::kSidePanel);
   webui::SetupWebUIDataSource(
       source, base::make_span(kReadLaterResources, kReadLaterResourcesSize),
-      show_side_panel_prototype ? IDR_READ_LATER_SIDE_PANEL_SIDE_PANEL_HTML
-                                : IDR_READ_LATER_READ_LATER_HTML);
+      show_side_panel ? IDR_READ_LATER_SIDE_PANEL_SIDE_PANEL_HTML
+                      : IDR_READ_LATER_READ_LATER_HTML);
   content::WebUIDataSource::Add(web_ui->GetWebContents()->GetBrowserContext(),
                                 source);
 }
diff --git a/chrome/browser/ui/webui/signin/profile_picker_handler.cc b/chrome/browser/ui/webui/signin/profile_picker_handler.cc
index 42e6b494..2d5f54c 100644
--- a/chrome/browser/ui/webui/signin/profile_picker_handler.cc
+++ b/chrome/browser/ui/webui/signin/profile_picker_handler.cc
@@ -485,7 +485,7 @@
   ProfileMetrics::LogProfileAddNewUser(
       ProfileMetrics::ADD_NEW_PROFILE_PICKER_LOCAL);
   ProfileManager::CreateMultiProfileAsync(
-      profile_name, avatar_index,
+      profile_name, avatar_index, /*is_hidden=*/false,
       base::BindRepeating(&ProfilePickerHandler::OnProfileCreated,
                           weak_factory_.GetWeakPtr(), profile_color,
                           create_shortcut));
diff --git a/chrome/browser/video_tutorials/internal/android/java/res/layout/video_player_controls.xml b/chrome/browser/video_tutorials/internal/android/java/res/layout/video_player_controls.xml
index 5cd1557e3..ff5e683 100644
--- a/chrome/browser/video_tutorials/internal/android/java/res/layout/video_player_controls.xml
+++ b/chrome/browser/video_tutorials/internal/android/java/res/layout/video_player_controls.xml
@@ -65,14 +65,14 @@
                 android:layout_gravity="center_horizontal"
                 android:layout_marginBottom="20dp"
                 android:text="@string/video_tutorials_watch_next_video"
-                android:textAppearance="@style/TextAppearance.TextMediumThick.Primary.Light" />
+                android:textAppearance="@style/TextAppearance.Button.Text.Blue" />
 
             <TextView
                 android:id="@+id/change_language"
                 android:layout_width="wrap_content"
                 android:layout_height="wrap_content"
                 android:layout_gravity="center_horizontal"
-                android:textAppearance="@style/TextAppearance.TextMediumThick.Primary.Light"
+                android:textAppearance="@style/TextAppearance.Button.Text.Blue"
                 android:text="@string/change_chrome_lang" />
         </LinearLayout>
 
diff --git a/chrome/browser/video_tutorials/internal/android/java/src/org/chromium/chrome/browser/video_tutorials/player/VideoPlayerCoordinatorImpl.java b/chrome/browser/video_tutorials/internal/android/java/src/org/chromium/chrome/browser/video_tutorials/player/VideoPlayerCoordinatorImpl.java
index c451145c..a2e7b91f 100644
--- a/chrome/browser/video_tutorials/internal/android/java/src/org/chromium/chrome/browser/video_tutorials/player/VideoPlayerCoordinatorImpl.java
+++ b/chrome/browser/video_tutorials/internal/android/java/src/org/chromium/chrome/browser/video_tutorials/player/VideoPlayerCoordinatorImpl.java
@@ -63,8 +63,7 @@
                 new LanguagePickerCoordinator(mView.getView().findViewById(R.id.language_picker),
                         mVideoTutorialService, languageInfoProvider);
         mMediator = new VideoPlayerMediator(mContext, mModel, videoTutorialService, mLanguagePicker,
-                languageInfoProvider, mWebContents, mMediaSessionObserver, tryNowCallback,
-                closeCallback);
+                mWebContents, mMediaSessionObserver, tryNowCallback, closeCallback);
         PropertyModelChangeProcessor.create(mModel, mView, new VideoPlayerViewBinder());
     }
 
diff --git a/chrome/browser/video_tutorials/internal/android/java/src/org/chromium/chrome/browser/video_tutorials/player/VideoPlayerMediator.java b/chrome/browser/video_tutorials/internal/android/java/src/org/chromium/chrome/browser/video_tutorials/player/VideoPlayerMediator.java
index 11e853d..4bd98c29 100644
--- a/chrome/browser/video_tutorials/internal/android/java/src/org/chromium/chrome/browser/video_tutorials/player/VideoPlayerMediator.java
+++ b/chrome/browser/video_tutorials/internal/android/java/src/org/chromium/chrome/browser/video_tutorials/player/VideoPlayerMediator.java
@@ -9,7 +9,6 @@
 
 import org.chromium.base.Callback;
 import org.chromium.chrome.browser.flags.ChromeFeatureList;
-import org.chromium.chrome.browser.video_tutorials.LanguageInfoProvider;
 import org.chromium.chrome.browser.video_tutorials.PlaybackStateObserver;
 import org.chromium.chrome.browser.video_tutorials.PlaybackStateObserver.WatchStateInfo.State;
 import org.chromium.chrome.browser.video_tutorials.Tutorial;
@@ -40,21 +39,18 @@
     private Tutorial mTutorial;
     private final Callback<Tutorial> mTryNowCallback;
     private final Runnable mCloseCallback;
-    private final LanguageInfoProvider mLanguageInfoProvider;
     private final PlaybackStateObserver mPlaybackStateObserver;
     private long mVideoStartTime;
 
     /** Constructor. */
     public VideoPlayerMediator(Context context, PropertyModel model,
             VideoTutorialService videoTutorialService, LanguagePickerCoordinator languagePicker,
-            LanguageInfoProvider languageInfoProvider, WebContents webContents,
-            PlaybackStateObserver playbackStateObserver, Callback<Tutorial> tryNowCallback,
-            Runnable closeCallback) {
+            WebContents webContents, PlaybackStateObserver playbackStateObserver,
+            Callback<Tutorial> tryNowCallback, Runnable closeCallback) {
         mContext = context;
         mModel = model;
         mVideoTutorialService = videoTutorialService;
         mLanguagePicker = languagePicker;
-        mLanguageInfoProvider = languageInfoProvider;
         mWebContents = webContents;
         mTryNowCallback = tryNowCallback;
         mCloseCallback = closeCallback;
@@ -62,18 +58,12 @@
 
         mModel.set(VideoPlayerProperties.SHOW_LOADING_SCREEN, false);
         mModel.set(VideoPlayerProperties.SHOW_LANGUAGE_PICKER, false);
-        mModel.set(VideoPlayerProperties.SHOW_MEDIA_CONTROLS, false);
+        hideMediaControls();
         mModel.set(VideoPlayerProperties.CALLBACK_WATCH_NEXT, this::onWatchNextClicked);
         mModel.set(VideoPlayerProperties.CALLBACK_CHANGE_LANGUAGE, this::changeLanguage);
         mModel.set(VideoPlayerProperties.CALLBACK_TRY_NOW, this::tryNow);
         mModel.set(VideoPlayerProperties.CALLBACK_SHARE, this::share);
         mModel.set(VideoPlayerProperties.CALLBACK_CLOSE, this::close);
-
-        boolean enableShare = sEnableShareForTesting == null
-                ? ChromeFeatureList.getFieldTrialParamByFeatureAsBoolean(
-                        ChromeFeatureList.VIDEO_TUTORIALS, VARIATION_ENABLE_SHARE_BUTTON, true)
-                : sEnableShareForTesting;
-        mModel.set(VideoPlayerProperties.SHOW_SHARE, enableShare);
     }
 
     /** Called when the player is getting destroyed. */
@@ -127,29 +117,31 @@
         }
 
         mModel.set(VideoPlayerProperties.SHOW_LOADING_SCREEN, false);
-        mModel.set(VideoPlayerProperties.SHOW_MEDIA_CONTROLS, false);
+        hideMediaControls();
     }
 
     @Override
     public void onPause() {
         VideoTutorialMetrics.recordWatchStateUpdate(mTutorial.featureType, WatchState.PAUSED);
-        mModel.set(VideoPlayerProperties.SHOW_MEDIA_CONTROLS, true);
         mModel.set(VideoPlayerProperties.SHOW_WATCH_NEXT, false);
         mModel.set(VideoPlayerProperties.SHOW_CHANGE_LANGUAGE, false);
         mModel.set(VideoPlayerProperties.SHOW_TRY_NOW,
                 VideoTutorialUtils.shouldShowTryNow(mTutorial.featureType));
         mModel.set(VideoPlayerProperties.WATCH_STATE_FOR_TRY_NOW, State.PAUSED);
+        mModel.set(VideoPlayerProperties.SHOW_SHARE, enableShare());
+        mModel.set(VideoPlayerProperties.SHOW_CLOSE, true);
     }
 
     @Override
     public void onEnded() {
         VideoTutorialMetrics.recordWatchStateUpdate(mTutorial.featureType, WatchState.COMPLETED);
-        mModel.set(VideoPlayerProperties.SHOW_MEDIA_CONTROLS, true);
         mModel.set(VideoPlayerProperties.SHOW_CHANGE_LANGUAGE, areMultipleLanguagesAvailable());
         maybeShowWatchNextVideoButton();
         mModel.set(VideoPlayerProperties.SHOW_TRY_NOW,
                 VideoTutorialUtils.shouldShowTryNow(mTutorial.featureType));
         mModel.set(VideoPlayerProperties.WATCH_STATE_FOR_TRY_NOW, State.ENDED);
+        mModel.set(VideoPlayerProperties.SHOW_SHARE, enableShare());
+        mModel.set(VideoPlayerProperties.SHOW_CLOSE, true);
     }
 
     @Override
@@ -198,7 +190,7 @@
         loadUrlParams.setHasUserGesture(true);
         mWebContents.getNavigationController().loadUrl(loadUrlParams);
         mModel.set(VideoPlayerProperties.SHOW_LOADING_SCREEN, false);
-        mModel.set(VideoPlayerProperties.SHOW_MEDIA_CONTROLS, false);
+        hideMediaControls();
     }
 
     private void maybeShowWatchNextVideoButton() {
@@ -215,6 +207,21 @@
         VideoTutorialUtils.getNextTutorial(mVideoTutorialService, mTutorial, this::startVideo);
     }
 
+    private void hideMediaControls() {
+        mModel.set(VideoPlayerProperties.SHOW_TRY_NOW, false);
+        mModel.set(VideoPlayerProperties.SHOW_WATCH_NEXT, false);
+        mModel.set(VideoPlayerProperties.SHOW_CHANGE_LANGUAGE, false);
+        mModel.set(VideoPlayerProperties.SHOW_SHARE, enableShare());
+        mModel.set(VideoPlayerProperties.SHOW_CLOSE, true);
+    }
+
+    private boolean enableShare() {
+        return sEnableShareForTesting == null
+                ? ChromeFeatureList.getFieldTrialParamByFeatureAsBoolean(
+                        ChromeFeatureList.VIDEO_TUTORIALS, VARIATION_ENABLE_SHARE_BUTTON, true)
+                : sEnableShareForTesting;
+    }
+
     private boolean areMultipleLanguagesAvailable() {
         return mVideoTutorialService.getAvailableLanguagesForTutorial(mTutorial.featureType).size()
                 > 1;
diff --git a/chrome/browser/video_tutorials/internal/android/java/src/org/chromium/chrome/browser/video_tutorials/player/VideoPlayerMediatorUnitTest.java b/chrome/browser/video_tutorials/internal/android/java/src/org/chromium/chrome/browser/video_tutorials/player/VideoPlayerMediatorUnitTest.java
index 85b6de3..33c4f443 100644
--- a/chrome/browser/video_tutorials/internal/android/java/src/org/chromium/chrome/browser/video_tutorials/player/VideoPlayerMediatorUnitTest.java
+++ b/chrome/browser/video_tutorials/internal/android/java/src/org/chromium/chrome/browser/video_tutorials/player/VideoPlayerMediatorUnitTest.java
@@ -27,8 +27,6 @@
 import org.chromium.base.metrics.test.ShadowRecordHistogram;
 import org.chromium.base.test.BaseRobolectricTestRunner;
 import org.chromium.chrome.browser.video_tutorials.FeatureType;
-import org.chromium.chrome.browser.video_tutorials.Language;
-import org.chromium.chrome.browser.video_tutorials.LanguageInfoProvider;
 import org.chromium.chrome.browser.video_tutorials.PlaybackStateObserver;
 import org.chromium.chrome.browser.video_tutorials.PlaybackStateObserver.WatchStateInfo;
 import org.chromium.chrome.browser.video_tutorials.Tutorial;
@@ -69,8 +67,6 @@
     @Mock
     Callback<Tutorial> mTryNowCallback;
     @Mock
-    private LanguageInfoProvider mLanguageProvider;
-    @Mock
     PlaybackStateObserver mPlaybackStateObserver;
 
     @Before
@@ -86,8 +82,8 @@
         VideoPlayerMediator.sEnableShareForTesting = true;
         mTestVideoTutorialService = new TestVideoTutorialService();
         mMediator = new VideoPlayerMediator(mContext, mModel, mTestVideoTutorialService,
-                mLanguagePicker, mLanguageProvider, mWebContents, mPlaybackStateObserver,
-                mTryNowCallback, mCloseCallback);
+                mLanguagePicker, mWebContents, mPlaybackStateObserver, mTryNowCallback,
+                mCloseCallback);
     }
 
     @Test
@@ -133,11 +129,13 @@
         mMediator.playVideoTutorial(tutorial);
         Mockito.verify(mNavigationController).loadUrl(any());
         assertThat(mModel.get(VideoPlayerProperties.SHOW_LOADING_SCREEN), equalTo(false));
-        assertThat(mModel.get(VideoPlayerProperties.SHOW_MEDIA_CONTROLS), equalTo(false));
+        assertThat(mModel.get(VideoPlayerProperties.SHOW_SHARE), equalTo(true));
+        assertThat(mModel.get(VideoPlayerProperties.SHOW_CLOSE), equalTo(true));
 
         mMediator.onPlay();
         assertThat(mModel.get(VideoPlayerProperties.SHOW_LOADING_SCREEN), equalTo(false));
-        assertThat(mModel.get(VideoPlayerProperties.SHOW_MEDIA_CONTROLS), equalTo(false));
+        assertThat(mModel.get(VideoPlayerProperties.SHOW_SHARE), equalTo(true));
+        assertThat(mModel.get(VideoPlayerProperties.SHOW_CLOSE), equalTo(true));
     }
 
     @Test
@@ -146,7 +144,8 @@
         mMediator.playVideoTutorial(tutorial);
         mMediator.onPlay();
         mMediator.onPause();
-        assertThat(mModel.get(VideoPlayerProperties.SHOW_MEDIA_CONTROLS), equalTo(true));
+        assertThat(mModel.get(VideoPlayerProperties.SHOW_SHARE), equalTo(true));
+        assertThat(mModel.get(VideoPlayerProperties.SHOW_CLOSE), equalTo(true));
         assertThat(mModel.get(VideoPlayerProperties.SHOW_WATCH_NEXT), equalTo(false));
         assertThat(mModel.get(VideoPlayerProperties.SHOW_CHANGE_LANGUAGE), equalTo(false));
     }
@@ -171,8 +170,6 @@
         mMediator.onPlay();
         mMediator.onEnded();
 
-        Language language = new Language("en", "English", "English native");
-        Mockito.when(mLanguageProvider.getLanguageInfo("en")).thenReturn(language);
         mModel.get(VideoPlayerProperties.CALLBACK_CHANGE_LANGUAGE).run();
         Mockito.verify(mLanguagePicker, Mockito.times(1))
                 .showLanguagePicker(
diff --git a/chrome/browser/video_tutorials/internal/android/java/src/org/chromium/chrome/browser/video_tutorials/player/VideoPlayerProperties.java b/chrome/browser/video_tutorials/internal/android/java/src/org/chromium/chrome/browser/video_tutorials/player/VideoPlayerProperties.java
index 741497e9..f8e582c 100644
--- a/chrome/browser/video_tutorials/internal/android/java/src/org/chromium/chrome/browser/video_tutorials/player/VideoPlayerProperties.java
+++ b/chrome/browser/video_tutorials/internal/android/java/src/org/chromium/chrome/browser/video_tutorials/player/VideoPlayerProperties.java
@@ -16,10 +16,10 @@
  */
 interface VideoPlayerProperties {
     WritableBooleanPropertyKey SHOW_LOADING_SCREEN = new WritableBooleanPropertyKey();
-    WritableBooleanPropertyKey SHOW_MEDIA_CONTROLS = new WritableBooleanPropertyKey();
     WritableBooleanPropertyKey SHOW_LANGUAGE_PICKER = new WritableBooleanPropertyKey();
     WritableBooleanPropertyKey SHOW_TRY_NOW = new WritableBooleanPropertyKey();
     WritableBooleanPropertyKey SHOW_SHARE = new WritableBooleanPropertyKey();
+    WritableBooleanPropertyKey SHOW_CLOSE = new WritableBooleanPropertyKey();
     WritableBooleanPropertyKey SHOW_WATCH_NEXT = new WritableBooleanPropertyKey();
     WritableBooleanPropertyKey SHOW_CHANGE_LANGUAGE = new WritableBooleanPropertyKey();
     WritableObjectPropertyKey<String> CHANGE_LANGUAGE_BUTTON_TEXT =
@@ -32,8 +32,8 @@
     WritableObjectPropertyKey<Runnable> CALLBACK_CLOSE = new WritableObjectPropertyKey<>();
     WritableObjectPropertyKey<State> WATCH_STATE_FOR_TRY_NOW = new WritableObjectPropertyKey<>();
 
-    PropertyKey[] ALL_KEYS = new PropertyKey[] {SHOW_LOADING_SCREEN, SHOW_MEDIA_CONTROLS,
-            SHOW_LANGUAGE_PICKER, SHOW_TRY_NOW, SHOW_SHARE, SHOW_WATCH_NEXT, SHOW_CHANGE_LANGUAGE,
+    PropertyKey[] ALL_KEYS = new PropertyKey[] {SHOW_LOADING_SCREEN, SHOW_LANGUAGE_PICKER,
+            SHOW_TRY_NOW, SHOW_SHARE, SHOW_CLOSE, SHOW_WATCH_NEXT, SHOW_CHANGE_LANGUAGE,
             CHANGE_LANGUAGE_BUTTON_TEXT, CALLBACK_WATCH_NEXT, CALLBACK_CHANGE_LANGUAGE,
             CALLBACK_TRY_NOW, CALLBACK_SHARE, CALLBACK_CLOSE, WATCH_STATE_FOR_TRY_NOW};
 }
diff --git a/chrome/browser/video_tutorials/internal/android/java/src/org/chromium/chrome/browser/video_tutorials/player/VideoPlayerView.java b/chrome/browser/video_tutorials/internal/android/java/src/org/chromium/chrome/browser/video_tutorials/player/VideoPlayerView.java
index e0d5658..0c92999 100644
--- a/chrome/browser/video_tutorials/internal/android/java/src/org/chromium/chrome/browser/video_tutorials/player/VideoPlayerView.java
+++ b/chrome/browser/video_tutorials/internal/android/java/src/org/chromium/chrome/browser/video_tutorials/player/VideoPlayerView.java
@@ -57,10 +57,6 @@
         mLoadingView.setVisibility(show ? View.VISIBLE : View.GONE);
     }
 
-    void showMediaControls(boolean show) {
-        mControls.setVisibility(show ? View.VISIBLE : View.GONE);
-    }
-
     void showLanguagePicker(boolean show) {
         mLanguagePickerView.setVisibility(show ? View.VISIBLE : View.GONE);
     }
diff --git a/chrome/browser/video_tutorials/internal/android/java/src/org/chromium/chrome/browser/video_tutorials/player/VideoPlayerViewBinder.java b/chrome/browser/video_tutorials/internal/android/java/src/org/chromium/chrome/browser/video_tutorials/player/VideoPlayerViewBinder.java
index 0230058..ba5e711 100644
--- a/chrome/browser/video_tutorials/internal/android/java/src/org/chromium/chrome/browser/video_tutorials/player/VideoPlayerViewBinder.java
+++ b/chrome/browser/video_tutorials/internal/android/java/src/org/chromium/chrome/browser/video_tutorials/player/VideoPlayerViewBinder.java
@@ -20,8 +20,6 @@
     public void bind(PropertyModel model, VideoPlayerView view, PropertyKey propertyKey) {
         if (propertyKey == VideoPlayerProperties.SHOW_LOADING_SCREEN) {
             view.showLoadingAnimation(model.get(VideoPlayerProperties.SHOW_LOADING_SCREEN));
-        } else if (propertyKey == VideoPlayerProperties.SHOW_MEDIA_CONTROLS) {
-            view.showMediaControls(model.get(VideoPlayerProperties.SHOW_MEDIA_CONTROLS));
         } else if (propertyKey == VideoPlayerProperties.SHOW_LANGUAGE_PICKER) {
             view.showLanguagePicker(model.get(VideoPlayerProperties.SHOW_LANGUAGE_PICKER));
         } else if (propertyKey == VideoPlayerProperties.SHOW_TRY_NOW) {
@@ -34,6 +32,11 @@
                     .findViewById(R.id.share_button)
                     .setVisibility(
                             model.get(VideoPlayerProperties.SHOW_SHARE) ? View.VISIBLE : View.GONE);
+        } else if (propertyKey == VideoPlayerProperties.SHOW_CLOSE) {
+            view.getView()
+                    .findViewById(R.id.close_button)
+                    .setVisibility(
+                            model.get(VideoPlayerProperties.SHOW_CLOSE) ? View.VISIBLE : View.GONE);
         } else if (propertyKey == VideoPlayerProperties.SHOW_WATCH_NEXT) {
             view.getView()
                     .findViewById(R.id.watch_next)
diff --git a/chrome/browser/video_tutorials/internal/android/java/src/org/chromium/chrome/browser/video_tutorials/player/VideoPlayerViewBinderTest.java b/chrome/browser/video_tutorials/internal/android/java/src/org/chromium/chrome/browser/video_tutorials/player/VideoPlayerViewBinderTest.java
index 1974c4a..b83195d7 100644
--- a/chrome/browser/video_tutorials/internal/android/java/src/org/chromium/chrome/browser/video_tutorials/player/VideoPlayerViewBinderTest.java
+++ b/chrome/browser/video_tutorials/internal/android/java/src/org/chromium/chrome/browser/video_tutorials/player/VideoPlayerViewBinderTest.java
@@ -114,14 +114,6 @@
     @Test
     @UiThreadTest
     @SmallTest
-    public void testControlsVisibility() {
-        mModel.set(VideoPlayerProperties.SHOW_MEDIA_CONTROLS, true);
-        assertEquals(View.VISIBLE, mControls.getVisibility());
-    }
-
-    @Test
-    @UiThreadTest
-    @SmallTest
     public void testTryNowButton() {
         View tryNowButton = mControls.findViewById(R.id.try_now);
         mModel.set(VideoPlayerProperties.SHOW_TRY_NOW, false);
diff --git a/chrome/browser/win/jumplist.cc b/chrome/browser/win/jumplist.cc
index cc60ed0b..52cbc5a 100644
--- a/chrome/browser/win/jumplist.cc
+++ b/chrome/browser/win/jumplist.cc
@@ -935,5 +935,5 @@
                      ->GetProfileAttributesStorage()
                      .GetNumberOfProfiles() < 2
              ? base::FilePath()
-             : profile_->GetPath().BaseName();
+             : profile_->GetBaseName();
 }
diff --git a/chrome/build/win64.pgo.txt b/chrome/build/win64.pgo.txt
index 95f90996..1cd03e6 100644
--- a/chrome/build/win64.pgo.txt
+++ b/chrome/build/win64.pgo.txt
@@ -1 +1 @@
-chrome-win64-master-1619773097-34db7004d15b1254bfb47dfb9eb1f97b42488338.profdata
+chrome-win64-master-1619794704-2284bbcb0e93506f04d99b1105af1290b1aa5083.profdata
diff --git a/chrome/common/extensions/manifest_tests/extension_manifests_manifest_version_unittest.cc b/chrome/common/extensions/manifest_tests/extension_manifests_manifest_version_unittest.cc
index 119fdff..b475791 100644
--- a/chrome/common/extensions/manifest_tests/extension_manifests_manifest_version_unittest.cc
+++ b/chrome/common/extensions/manifest_tests/extension_manifests_manifest_version_unittest.cc
@@ -5,6 +5,7 @@
 #include <memory>
 
 #include "chrome/common/extensions/manifest_tests/chrome_manifest_test.h"
+#include "extensions/common/error_utils.h"
 #include "extensions/common/manifest_constants.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -14,38 +15,67 @@
 namespace errors = extensions::manifest_errors;
 
 TEST_F(ChromeManifestTest, ManifestVersionError) {
-  base::Value manifest1(base::Value::Type::DICTIONARY);
-  manifest1.SetStringKey("name", "Miles");
-  manifest1.SetStringKey("version", "0.55");
+  base::Value mv_missing(base::Value::Type::DICTIONARY);
+  mv_missing.SetStringKey("name", "Miles");
+  mv_missing.SetStringKey("version", "0.55");
 
-  base::Value manifest2 = manifest1.Clone();
-  manifest2.SetIntKey("manifest_version", 1);
+  base::Value mv0 = mv_missing.Clone();
+  mv0.SetIntKey("manifest_version", 0);
 
-  base::Value manifest3 = manifest1.Clone();
-  manifest3.SetIntKey("manifest_version", 2);
+  base::Value mv1 = mv_missing.Clone();
+  mv1.SetIntKey("manifest_version", 1);
+
+  base::Value mv2 = mv_missing.Clone();
+  mv2.SetIntKey("manifest_version", 2);
+
+  base::Value mv3 = mv_missing.Clone();
+  mv3.SetIntKey("manifest_version", 3);
+
+  base::Value mv4 = mv_missing.Clone();
+  mv4.SetIntKey("manifest_version", 4);
+
+  base::Value mv_string = mv_missing.Clone();
+  mv_string.SetStringKey("manifest_version", "2");
 
   struct {
     const char* test_name;
     bool require_modern_manifest_version;
     base::Value manifest;
-    bool expect_error;
+    std::string expected_error;
   } test_data[] = {
-      {"require_modern_with_default", true, manifest1.Clone(), true},
-      {"require_modern_with_v1", true, manifest2.Clone(), true},
-      {"require_modern_with_v2", true, manifest3.Clone(), false},
-      {"dont_require_modern_with_default", false, manifest1.Clone(), true},
-      {"dont_require_modern_with_v1", false, manifest2.Clone(), true},
-      {"dont_require_modern_with_v2", false, manifest3.Clone(), false},
+      {"require_modern_with_default", true, mv_missing.Clone(),
+       errors::kInvalidManifestVersionMissingKey},
+      {"require_modern_with_invalid_version", true, mv0.Clone(),
+       errors::kInvalidManifestVersionUnsupported},
+      {"require_modern_with_old_version", true, mv1.Clone(),
+       errors::kInvalidManifestVersionUnsupported},
+      {"require_modern_with_v2", true, mv2.Clone(), ""},
+      {"require_modern_with_v3", true, mv3.Clone(), ""},
+      {"require_modern_with_future_version", true, mv4.Clone(), ""},
+      {"require_modern_with_string", true, mv_string.Clone(),
+       errors::kInvalidManifestVersionUnsupported},
+      {"dont_require_modern_with_default", false, mv_missing.Clone(),
+       errors::kInvalidManifestVersionMissingKey},
+      {"dont_require_modern_with_invalid_version", false, mv0.Clone(),
+       errors::kInvalidManifestVersionUnsupported},
+      {"dont_require_modern_with_old_version", false, mv1.Clone(),
+       errors::kInvalidManifestVersionUnsupported},
+      {"dont_require_modern_with_v2", false, mv2.Clone(), ""},
+      {"dont_require_modern_with_v3", false, mv3.Clone(), ""},
+      {"dont_require_modern_with_future_version", false, mv4.Clone(), ""},
+      {"dont_require_modern_with_string", false, mv_string.Clone(),
+       errors::kInvalidManifestVersionUnsupported},
   };
 
   for (auto& entry : test_data) {
     int create_flags = Extension::NO_FLAGS;
     if (entry.require_modern_manifest_version)
       create_flags |= Extension::REQUIRE_MODERN_MANIFEST_VERSION;
-    if (entry.expect_error) {
+    if (!entry.expected_error.empty()) {
       LoadAndExpectError(
           ManifestData(std::move(entry.manifest), entry.test_name),
-          errors::kInvalidManifestVersionOld,
+          extensions::ErrorUtils::FormatErrorMessage(
+              entry.expected_error, "either 2 or 3", "extensions"),
           extensions::mojom::ManifestLocation::kUnpacked, create_flags);
     } else {
       LoadAndExpectSuccess(
diff --git a/chrome/common/extensions/manifest_tests/extension_manifests_platformapp_unittest.cc b/chrome/common/extensions/manifest_tests/extension_manifests_platformapp_unittest.cc
index 6cf952d..c106e73 100644
--- a/chrome/common/extensions/manifest_tests/extension_manifests_platformapp_unittest.cc
+++ b/chrome/common/extensions/manifest_tests/extension_manifests_platformapp_unittest.cc
@@ -39,11 +39,12 @@
   EXPECT_FALSE(IncognitoInfo::IsSplitMode(extension.get()));
 
   Testcase error_testcases[] = {
-    Testcase("init_invalid_platform_app_2.json",
-             errors::kBackgroundRequiredForPlatformApps),
-    Testcase("init_invalid_platform_app_3.json",
-             ErrorUtils::FormatErrorMessage(
-                 errors::kInvalidManifestVersionOld, "2", "apps")),
+      Testcase("init_invalid_platform_app_2.json",
+               errors::kBackgroundRequiredForPlatformApps),
+      Testcase("init_invalid_platform_app_3.json",
+               ErrorUtils::FormatErrorMessage(
+                   errors::kInvalidManifestVersionUnsupported, "either 2 or 3",
+                   "apps")),
   };
   RunTestcases(error_testcases, base::size(error_testcases), EXPECT_TYPE_ERROR);
 
diff --git a/chrome/common/pref_names.cc b/chrome/common/pref_names.cc
index 270854c..d678b622 100644
--- a/chrome/common/pref_names.cc
+++ b/chrome/common/pref_names.cc
@@ -2173,6 +2173,11 @@
 // insecure resolvers.
 const char kDnsOverHttpsTemplates[] = "dns_over_https.templates";
 
+// Boolean that specifies whether additional DNS query types (e.g. HTTPS) may be
+// queried alongside the traditional A and AAAA queries.
+const char kAdditionalDnsQueryTypesEnabled[] =
+    "async_dns.additional_dns_query_types_enabled";
+
 // A pref holding the value of the policy used to explicitly allow or deny
 // access to audio capture devices.  When enabled or not set, the user is
 // prompted for device access.  When disabled, access to audio capture devices
diff --git a/chrome/common/pref_names.h b/chrome/common/pref_names.h
index 4fb8e38c..8b810083 100644
--- a/chrome/common/pref_names.h
+++ b/chrome/common/pref_names.h
@@ -833,6 +833,7 @@
 extern const char kBuiltInDnsClientEnabled[];
 extern const char kDnsOverHttpsMode[];
 extern const char kDnsOverHttpsTemplates[];
+extern const char kAdditionalDnsQueryTypesEnabled[];
 
 extern const char kRegisteredProtocolHandlers[];
 extern const char kIgnoredProtocolHandlers[];
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn
index 5dfbd99c..eda76b9 100644
--- a/chrome/test/BUILD.gn
+++ b/chrome/test/BUILD.gn
@@ -4560,6 +4560,7 @@
       "../browser/enterprise/reporting/extension_request/extension_request_report_throttler_unittest.cc",
       "../browser/enterprise/reporting/policy_info_unittest.cc",
       "../browser/enterprise/reporting/profile_report_generator_unittest.cc",
+      "../browser/enterprise/reporting/real_time_report_generator_unittest.cc",
       "../browser/enterprise/reporting/report_generator_unittest.cc",
       "../browser/enterprise/reporting/report_request_queue_generator_unittest.cc",
       "../browser/enterprise/reporting/report_scheduler_desktop_unittest.cc",
diff --git a/chrome/test/data/extensions/manifest_tests/init_valid_platform_app_no_manifest_version.json b/chrome/test/data/extensions/manifest_tests/init_valid_platform_app_no_manifest_version.json
index 9c8c063..8f6c1fe7 100644
--- a/chrome/test/data/extensions/manifest_tests/init_valid_platform_app_no_manifest_version.json
+++ b/chrome/test/data/extensions/manifest_tests/init_valid_platform_app_no_manifest_version.json
@@ -1,6 +1,5 @@
 {
   "name": "Platform Test",
-  "manifest_version": 2,
   "version": "1",
   "app": {
     "background": {
diff --git a/chrome/test/data/policy/policy_test_cases.json b/chrome/test/data/policy/policy_test_cases.json
index f1d2d32a..72516db 100644
--- a/chrome/test/data/policy/policy_test_cases.json
+++ b/chrome/test/data/policy/policy_test_cases.json
@@ -2986,6 +2986,18 @@
     ]
   },
 
+  "AdditionalDnsQueryTypesEnabled": {
+    "os": ["win", "linux", "mac", "chromeos", "android"],
+    "policy_pref_mapping_tests": [
+      {
+        "policies": {
+          "AdditionalDnsQueryTypesEnabled": false
+        },
+        "prefs": { "async_dns.additional_dns_query_types_enabled": { "location": "local_state" } }
+      }
+    ]
+  },
+
   "WPADQuickCheckEnabled": {
     "os": ["win", "linux", "mac", "chromeos"],
     "policy_pref_mapping_tests": [
diff --git a/chrome/test/data/webui/chromeos/diagnostics/BUILD.gn b/chrome/test/data/webui/chromeos/diagnostics/BUILD.gn
index a10a1ba0..74fe9eeb 100644
--- a/chrome/test/data/webui/chromeos/diagnostics/BUILD.gn
+++ b/chrome/test/data/webui/chromeos/diagnostics/BUILD.gn
@@ -186,6 +186,7 @@
   deps = [
     "../..:chai_assert",
     "//chromeos/components/diagnostics_ui/resources:diagnostics_types",
+    "//chromeos/components/diagnostics_ui/resources:fake_network_health_provider",
     "//chromeos/components/diagnostics_ui/resources:fake_system_routine_controller",
     "//chromeos/components/diagnostics_ui/resources:mojo_interface_provider",
   ]
@@ -195,6 +196,10 @@
   deps = [
     "../..:chai_assert",
     "../..:test_util.m",
+    "//chromeos/components/diagnostics_ui/resources:diagnostics_types",
+    "//chromeos/components/diagnostics_ui/resources:fake_data",
+    "//chromeos/components/diagnostics_ui/resources:fake_network_health_provider",
+    "//chromeos/components/diagnostics_ui/resources:mojo_interface_provider",
     "//chromeos/components/diagnostics_ui/resources:network_list",
   ]
   externs_list = [ "$externs_path/mocha-2.5.js" ]
diff --git a/chrome/test/data/webui/chromeos/diagnostics/mojo_interface_provider_test.js b/chrome/test/data/webui/chromeos/diagnostics/mojo_interface_provider_test.js
index 4990713..4bd17bb 100644
--- a/chrome/test/data/webui/chromeos/diagnostics/mojo_interface_provider_test.js
+++ b/chrome/test/data/webui/chromeos/diagnostics/mojo_interface_provider_test.js
@@ -3,9 +3,10 @@
 // found in the LICENSE file.
 
 import {SystemDataProviderInterface} from 'chrome://diagnostics/diagnostics_types.js';
+import {FakeNetworkHealthProvider} from 'chrome://diagnostics/fake_network_health_provider.js';
 import {FakeSystemDataProvider} from 'chrome://diagnostics/fake_system_data_provider.js';
 import {FakeSystemRoutineController} from 'chrome://diagnostics/fake_system_routine_controller.js';
-import {getSystemDataProvider, getSystemRoutineController, setSystemDataProviderForTesting, setSystemRoutineControllerForTesting} from 'chrome://diagnostics/mojo_interface_provider.js';
+import {getNetworkHealthProvider, getSystemDataProvider, getSystemRoutineController, setNetworkHealthProviderForTesting, setSystemDataProviderForTesting, setSystemRoutineControllerForTesting} from 'chrome://diagnostics/mojo_interface_provider.js';
 
 import {assertEquals} from '../../chai_assert.js';
 
@@ -21,4 +22,10 @@
     setSystemRoutineControllerForTesting(fake_controller);
     assertEquals(fake_controller, getSystemRoutineController());
   });
+
+  test('SettingGettingTestNetworkHealthProvider', () => {
+    let fake_provider = new FakeNetworkHealthProvider();
+    setNetworkHealthProviderForTesting(fake_provider);
+    assertEquals(fake_provider, getNetworkHealthProvider());
+  });
 }
diff --git a/chrome/test/data/webui/chromeos/diagnostics/network_list_test.js b/chrome/test/data/webui/chromeos/diagnostics/network_list_test.js
index 996d2062..5ed6d123 100644
--- a/chrome/test/data/webui/chromeos/diagnostics/network_list_test.js
+++ b/chrome/test/data/webui/chromeos/diagnostics/network_list_test.js
@@ -4,6 +4,11 @@
 
 import 'chrome://diagnostics/network_list.js';
 
+import {NetworkGuidInfo} from 'chrome://diagnostics/diagnostics_types.js';
+import {fakeNetworkGuidInfoList} from 'chrome://diagnostics/fake_data.js';
+import {FakeNetworkHealthProvider} from 'chrome://diagnostics/fake_network_health_provider.js';
+import {setNetworkHealthProviderForTesting} from 'chrome://diagnostics/mojo_interface_provider.js';
+
 import {assertFalse, assertTrue} from '../../chai_assert.js';
 import {flushTasks} from '../../test_util.m.js';
 
@@ -13,6 +18,14 @@
   /** @type {?NetworkListElement} */
   let networkListElement = null;
 
+  /** @type {?FakeNetworkHealthProvider} */
+  let provider = null;
+
+  suiteSetup(() => {
+    provider = new FakeNetworkHealthProvider();
+    setNetworkHealthProviderForTesting(provider);
+  });
+
   setup(() => {
     document.body.innerHTML = '';
   });
@@ -20,10 +33,15 @@
   teardown(() => {
     networkListElement.remove();
     networkListElement = null;
+    provider.reset();
   });
 
-  function initializeNetworkList() {
+  /**
+   * @param {!Array<!NetworkGuidInfo>} fakeNetworkGuidInfoList
+   */
+  function initializeNetworkList(fakeNetworkGuidInfoList) {
     assertFalse(!!networkListElement);
+    provider.setFakeNetworkGuidInfo(fakeNetworkGuidInfoList);
 
     // Add the network list to the DOM.
     networkListElement = /** @type {!NetworkListElement} */ (
@@ -46,8 +64,34 @@
     return connectivityCard;
   }
 
-  test('ConnectivityCardInitialized', () => {
-    return initializeNetworkList().then(
-        () => assertTrue(!!getConnectivityCard()));
+  /**
+   * Causes the network list observer to fire.
+   */
+  function triggerNetworkListObserver() {
+    provider.triggerNetworkListObserver();
+    return flushTasks();
+  }
+
+  test('ActiveGuidPresent', () => {
+    // The network-list element sets up a NetworkListObserver as part
+    // of its initialization. Registering this observer causes it to
+    // fire once.
+    return initializeNetworkList(fakeNetworkGuidInfoList).then(() => {
+      dx_utils.assertElementContainsText(
+          getConnectivityCard().$$('#activeGuid'),
+          /**  @type {string} */ (fakeNetworkGuidInfoList[0].activeGuid));
+    });
+  });
+
+  test('ActiveGuidUpdates', () => {
+    return initializeNetworkList(fakeNetworkGuidInfoList)
+        .then(() => triggerNetworkListObserver())
+        .then(() => {
+          // Triggering the NetworkListObserver provides
+          // the second observation: fakeNetworkGuidInfoList[1].
+          dx_utils.assertElementContainsText(
+              getConnectivityCard().$$('#activeGuid'),
+              /** @type {string} */ (fakeNetworkGuidInfoList[1].activeGuid));
+        });
   });
 }
\ No newline at end of file
diff --git a/chrome/test/data/webui/chromeos/shimless_rma/shimless_rma_app_test.js b/chrome/test/data/webui/chromeos/shimless_rma/shimless_rma_app_test.js
index 587aa984..22b126c 100644
--- a/chrome/test/data/webui/chromeos/shimless_rma/shimless_rma_app_test.js
+++ b/chrome/test/data/webui/chromeos/shimless_rma/shimless_rma_app_test.js
@@ -4,7 +4,7 @@
 
 import {ShimlessRmaElement} from 'chrome://shimless-rma/shimless_rma.js';
 
-import {assertEquals} from '../../chai_assert.js';
+import {assertTrue} from '../../chai_assert.js';
 
 export function shimlessRMAAppTest() {
   /** @type {?ShimlessRmaElement} */
@@ -23,8 +23,11 @@
   });
 
   test('ShimlessRMALoaded', () => {
-    assertEquals(
-        'hello world',
-        component.shadowRoot.querySelector('#header').textContent);
+    const nextBtn = component.shadowRoot.querySelector('#back');
+    const prevBtn = component.shadowRoot.querySelector('#cancel');
+    const backBtn = component.shadowRoot.querySelector('#next');
+    assertTrue(!!nextBtn);
+    assertTrue(!!prevBtn);
+    assertTrue(!!backBtn);
   });
 }
\ No newline at end of file
diff --git a/chromecast/BUILD.gn b/chromecast/BUILD.gn
index e2f71d5..cd0d0b9 100644
--- a/chromecast/BUILD.gn
+++ b/chromecast/BUILD.gn
@@ -310,12 +310,6 @@
     # the cast IsSupporedVideoConfigs ignore the transfer function parameter.
     # (b/36984215).
     gtest_excludes = [ "*.IsSupportedVideoConfig_VP9TransferFunctions" ]
-    if (is_android_things) {
-      gtest_excludes += [
-        "FontUniqueNameLookupTest.TestMatchPostScriptNameTtc",
-        "SiteInstanceTest.IsSuitableForURL",
-      ]
-    }
     if (target_os == "linux" && !is_cast_desktop_build) {
       # DesktopCaptureDeviceTest.*: No capture device on Eureka
       # Disable PepperGamepadHostTest.WaitForReply (pepper not supported on Eureka)
@@ -610,8 +604,6 @@
     "ENABLE_PLAYREADY=$enable_playready",
     "ENABLE_VIDEO_CAPTURE_SERVICE=$enable_video_capture_service",
     "IS_ANDROID_APPLIANCE=$is_android_appliance",
-    "IS_ANDROID_THINGS=$is_android_things",
-    "IS_ANDROID_THINGS_NON_PUBLIC=$is_android_things_non_public",
     "IS_CAST_AUDIO_ONLY=$is_cast_audio_only",
     "IS_CAST_DESKTOP_BUILD=$is_cast_desktop_build",
     "IS_SINGLE_VOLUME=$is_single_volume",
diff --git a/chromecast/base/BUILD.gn b/chromecast/base/BUILD.gn
index d4556fae..73b5ce7 100644
--- a/chromecast/base/BUILD.gn
+++ b/chromecast/base/BUILD.gn
@@ -229,12 +229,6 @@
       "cast_sys_info_android.h",
       "cast_sys_info_android_factory.cc",
     ]
-    if (is_android_things) {
-      sources += [
-        "cast_sys_info_android_things.cc",
-        "cast_sys_info_android_things.h",
-      ]
-    }
   }
 } else {  # !is_android
   # Target for OEM partners to override sys_info shared library, i.e.
diff --git a/chromecast/base/cast_sys_info_android_factory.cc b/chromecast/base/cast_sys_info_android_factory.cc
index ed1d406..697378c5 100644
--- a/chromecast/base/cast_sys_info_android_factory.cc
+++ b/chromecast/base/cast_sys_info_android_factory.cc
@@ -3,20 +3,13 @@
 // found in the LICENSE file.
 
 #include "chromecast/chromecast_buildflags.h"
-#if BUILDFLAG(IS_ANDROID_THINGS)
-#include "chromecast/base/cast_sys_info_android_things.h"
-#endif
 #include "chromecast/base/cast_sys_info_android.h"
 
 namespace chromecast {
 
 // static
 std::unique_ptr<CastSysInfo> CreateSysInfo() {
-#if BUILDFLAG(IS_ANDROID_THINGS)
-  return std::make_unique<CastSysInfoAndroidThings>();
-#else
   return std::make_unique<CastSysInfoAndroid>();
-#endif
 }
 
 }  // namespace chromecast
diff --git a/chromecast/base/cast_sys_info_android_things.cc b/chromecast/base/cast_sys_info_android_things.cc
deleted file mode 100644
index d4b70f6..0000000
--- a/chromecast/base/cast_sys_info_android_things.cc
+++ /dev/null
@@ -1,73 +0,0 @@
-// Copyright 2018 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "chromecast/base/cast_sys_info_android_things.h"
-
-#include "base/android/jni_string.h"
-#include "chromecast/chromecast_buildflags.h"
-#if BUILDFLAG(IS_ANDROID_THINGS_NON_PUBLIC)
-#include "base/android/jni_array.h"
-#include "chromecast/browser/jni_headers/CastSysInfoAndroidThings_jni.h"
-#endif
-
-namespace chromecast {
-
-CastSysInfoAndroidThings::CastSysInfoAndroidThings() = default;
-CastSysInfoAndroidThings::~CastSysInfoAndroidThings() = default;
-
-std::string CastSysInfoAndroidThings::GetProductName() {
-#if BUILDFLAG(IS_ANDROID_THINGS_NON_PUBLIC)
-  JNIEnv* env = base::android::AttachCurrentThread();
-  return base::android::ConvertJavaStringToUTF8(
-      Java_CastSysInfoAndroidThings_getProductName(env));
-#else
-  return "";
-#endif
-}
-
-std::string CastSysInfoAndroidThings::GetDeviceModel() {
-#if BUILDFLAG(IS_ANDROID_THINGS_NON_PUBLIC)
-  JNIEnv* env = base::android::AttachCurrentThread();
-  return base::android::ConvertJavaStringToUTF8(
-      Java_CastSysInfoAndroidThings_getDeviceModel(env));
-#else
-  return "";
-#endif
-}
-
-std::string CastSysInfoAndroidThings::GetManufacturer() {
-#if BUILDFLAG(IS_ANDROID_THINGS_NON_PUBLIC)
-  JNIEnv* env = base::android::AttachCurrentThread();
-  return base::android::ConvertJavaStringToUTF8(
-      Java_CastSysInfoAndroidThings_getManufacturer(env));
-#else
-  return "";
-#endif
-}
-
-std::string CastSysInfoAndroidThings::GetSystemReleaseChannel() {
-#if BUILDFLAG(IS_ANDROID_THINGS_NON_PUBLIC)
-  JNIEnv* env = base::android::AttachCurrentThread();
-  return base::android::ConvertJavaStringToUTF8(
-      Java_CastSysInfoAndroidThings_getReleaseChannel(env));
-#else
-  return "";
-#endif
-}
-
-std::vector<std::string> CastSysInfoAndroidThings::GetFactoryLocaleList() {
-  std::vector<std::string> locale_list;
-#if BUILDFLAG(IS_ANDROID_THINGS_NON_PUBLIC)
-  JNIEnv* env = base::android::AttachCurrentThread();
-  base::android::AppendJavaStringArrayToStringVector(
-      env, Java_CastSysInfoAndroidThings_getFactoryLocaleList(env),
-      &locale_list);
-#endif
-  if (locale_list.empty()) {
-      locale_list = CastSysInfoAndroid::GetFactoryLocaleList();
-  }
-  return locale_list;
-}
-
-}  // namespace chromecast
diff --git a/chromecast/browser/BUILD.gn b/chromecast/browser/BUILD.gn
index 92b4d4e0..f4c255d 100644
--- a/chromecast/browser/BUILD.gn
+++ b/chromecast/browser/BUILD.gn
@@ -698,10 +698,6 @@
       "android/apk/src/org/chromium/chromecast/shell/CastMetricsHelper.java",
       "android/apk/src/org/chromium/chromecast/shell/CastSysInfoAndroid.java",
     ]
-
-    if (is_android_things_non_public) {
-      sources += [ "android/apk/src/org/chromium/chromecast/shell/CastSysInfoAndroidThings.java" ]
-    }
   }
 
   java_cpp_enum("java_enums") {
diff --git a/chromecast/browser/android/BUILD.gn b/chromecast/browser/android/BUILD.gn
index 557089fd..d7f286d 100644
--- a/chromecast/browser/android/BUILD.gn
+++ b/chromecast/browser/android/BUILD.gn
@@ -11,10 +11,7 @@
 cast_shell_android_manifest =
     "$root_gen_dir/cast_shell_manifest/AndroidManifest.xml"
 
-manifest_variables = [
-  "is_android_things=$is_android_things",
-  "is_android_things_non_public=$is_android_things_non_public",
-]
+manifest_variables = [ "is_android_appliance=$is_android_appliance" ]
 
 jinja_template("cast_shell_manifest") {
   input = "apk/AndroidManifest.xml.jinja2"
@@ -194,31 +191,11 @@
   ]
   annotation_processor_deps = [ "//base/android/jni_generator:jni_processor" ]
 
-  if (is_android_things_non_public) {
-    deps += [ ":cast_shell_android_things_sys_info_java" ]
-    if (enable_assistant) {
-      deps += [ "//chromecast/internal/assistant/ui:boot_parameters_java" ]
-    }
-  } else if (is_android_appliance) {
+  if (is_android_appliance) {
     deps += [ "//chromecast/internal/assistant/ui:boot_parameters_java" ]
   }
 }
 
-if (is_android_things_non_public) {
-  android_library("cast_shell_android_things_sys_info_java") {
-    sources = [ "//chromecast/browser/android/apk/src/org/chromium/chromecast/shell/CastSysInfoAndroidThings.java" ]
-    deps = [
-      "//base:base_java",
-      "//chromecast/internal/android/prebuilt/things:support_lib_java",
-    ]
-  }
-
-  generate_jni("cast_shell_android_things_sys_info_jni_headers") {
-    sources = [ "//chromecast/browser/android/apk/src/org/chromium/chromecast/shell/CastSysInfoAndroidThings.java" ]
-    deps = [ "//chromecast/internal/android/prebuilt/things:support_lib_java" ]
-  }
-}
-
 junit_binary("cast_shell_junit_tests") {
   sources = [
     "junit/src/org/chromium/chromecast/shell/AsyncTaskRunnerTest.java",
diff --git a/chromecast/browser/android/apk/AndroidManifest.xml.jinja2 b/chromecast/browser/android/apk/AndroidManifest.xml.jinja2
index 2762a83..fb840b6 100644
--- a/chromecast/browser/android/apk/AndroidManifest.xml.jinja2
+++ b/chromecast/browser/android/apk/AndroidManifest.xml.jinja2
@@ -14,18 +14,13 @@
     <uses-permission android:name="android.permission.INTERNET"/>
     <uses-permission android:name="android.permission.WAKE_LOCK"/>
 
-    {% if is_android_things_non_public == "true" %}
-      <!-- CastSysInfoAndroidThings must read factory data. -->
-      <uses-permission android:name="com.google.android.things.permission.READ_FACTORY_DATA"/>
-    {% endif %}
-
     <application android:icon="@mipmap/app_icon">
         <activity android:name="org.chromium.chromecast.shell.CastWebContentsActivity"
                   android:theme="@style/CastShellTheme"
                   android:exported="true"
                   android:hardwareAccelerated="true"
                   android:launchMode="standard"
-                  {% if is_android_things == "true" %}
+                  {% if is_android_appliance == "true" %}
                   android:persistent="true"
                   {% endif %}
                   android:screenOrientation="landscape"
diff --git a/chromecast/chromecast.gni b/chromecast/chromecast.gni
index 6b57ba9..aa42f94 100644
--- a/chromecast/chromecast.gni
+++ b/chromecast/chromecast.gni
@@ -45,9 +45,6 @@
   # Set to true for builds targeting ARC.
   is_android_arc = false
 
-  # Set true for builds targeting Android Things.
-  is_android_things = false
-
   # Set true for builds targeting Android appliances.
   is_android_appliance = false
 
@@ -135,11 +132,6 @@
 }
 
 declare_args() {
-  is_android_things_non_public =
-      is_android_things && chromecast_branding != "public"
-}
-
-declare_args() {
   # Use Playready CDMs for internal non-desktop builds.
   enable_playready = !is_cast_desktop_build && chromecast_branding != "public"
 }
diff --git a/chromeos/components/diagnostics_ui/resources/BUILD.gn b/chromeos/components/diagnostics_ui/resources/BUILD.gn
index e62eaa4..fa8508d 100644
--- a/chromeos/components/diagnostics_ui/resources/BUILD.gn
+++ b/chromeos/components/diagnostics_ui/resources/BUILD.gn
@@ -174,6 +174,7 @@
 js_library("network_list") {
   deps = [
     ":connectivity_card",
+    ":diagnostics_types",
     ":mojo_interface_provider",
     "//third_party/polymer/v3_0/components-chromium/polymer:polymer_bundled",
   ]
diff --git a/chromeos/components/diagnostics_ui/resources/connectivity_card.html b/chromeos/components/diagnostics_ui/resources/connectivity_card.html
index dd13e58..9cf261b 100644
--- a/chromeos/components/diagnostics_ui/resources/connectivity_card.html
+++ b/chromeos/components/diagnostics_ui/resources/connectivity_card.html
@@ -1,9 +1,10 @@
 <style include="diagnostics-shared diagnostics-fonts">
 </style>
 
-<diagnostics-card hide-data-points="true">
+<diagnostics-card>
     <!-- TODO(michaelcheco): Add localized strings. -->
   <div id="cardTitle" slot="title">Connectivity</div>
+  <div id="activeGuid" slot="body">[[activeGuid]]</div>
   <routine-section slot="routines" routines="[[routines_]]"
   is-test-running="{{isTestRunning}}"
   run-tests-button-text="Run Network test"
diff --git a/chromeos/components/diagnostics_ui/resources/connectivity_card.js b/chromeos/components/diagnostics_ui/resources/connectivity_card.js
index dd44c63..0d2e2d2 100644
--- a/chromeos/components/diagnostics_ui/resources/connectivity_card.js
+++ b/chromeos/components/diagnostics_ui/resources/connectivity_card.js
@@ -46,6 +46,12 @@
         chromeos.diagnostics.mojom.RoutineType.kSignalStrength,
       ],
     },
+
+    /** @type {string} */
+    activeGuid: {
+      type: String,
+      value: '',
+    },
   },
 
   /** @protected */
diff --git a/chromeos/components/diagnostics_ui/resources/mojo_interface_provider.js b/chromeos/components/diagnostics_ui/resources/mojo_interface_provider.js
index 8c01f3f3..eff56e38 100644
--- a/chromeos/components/diagnostics_ui/resources/mojo_interface_provider.js
+++ b/chromeos/components/diagnostics_ui/resources/mojo_interface_provider.js
@@ -10,8 +10,9 @@
 
 import {assert} from 'chrome://resources/js/assert.m.js';
 
-import {PowerRoutineResult, RoutineType, StandardRoutineResult, SystemDataProviderInterface, SystemInfo, SystemRoutineControllerInterface} from './diagnostics_types.js';
-import {fakeBatteryChargeStatus, fakeBatteryHealth, fakeBatteryInfo, fakeCpuUsage, fakeMemoryUsage, fakePowerRoutineResults, fakeRoutineResults, fakeSystemInfo} from './fake_data.js';
+import {NetworkHealthProviderInterface, PowerRoutineResult, RoutineType, StandardRoutineResult, SystemDataProviderInterface, SystemInfo, SystemRoutineControllerInterface} from './diagnostics_types.js';
+import {fakeAllNetworksAvailable, fakeBatteryChargeStatus, fakeBatteryHealth, fakeBatteryInfo, fakeCpuUsage, fakeMemoryUsage, fakePowerRoutineResults, fakeRoutineResults, fakeSystemInfo} from './fake_data.js';
+import {FakeNetworkHealthProvider} from './fake_network_health_provider.js';
 import {FakeSystemDataProvider} from './fake_system_data_provider.js';
 import {FakeSystemRoutineController} from './fake_system_routine_controller.js';
 
@@ -32,6 +33,11 @@
 let systemRoutineController = null;
 
 /**
+ * @type {?NetworkHealthProviderInterface}
+ */
+let networkHealthProvider = null;
+
+/**
  * @param {!SystemDataProviderInterface} testProvider
  */
 export function setSystemDataProviderForTesting(testProvider) {
@@ -70,3 +76,31 @@
   assert(!!systemRoutineController);
   return systemRoutineController;
 }
+
+/**
+ * @param {!NetworkHealthProviderInterface} testProvider
+ */
+export function setNetworkHealthProviderForTesting(testProvider) {
+  networkHealthProvider = testProvider;
+}
+
+function setupFakeNetworkHealthProvider_() {
+  const provider = new FakeNetworkHealthProvider();
+  // The fake provides a stable state with all networks connected.
+  provider.setFakeNetworkGuidInfo([fakeAllNetworksAvailable]);
+
+  setNetworkHealthProviderForTesting(provider);
+}
+
+/**
+ * @return {!NetworkHealthProviderInterface}
+ */
+export function getNetworkHealthProvider() {
+  if (!networkHealthProvider) {
+    // TODO(michaelcheco): Instantiate a real mojo interface here.
+    setupFakeNetworkHealthProvider_();
+  }
+
+  assert(!!networkHealthProvider);
+  return networkHealthProvider;
+}
diff --git a/chromeos/components/diagnostics_ui/resources/network_list.html b/chromeos/components/diagnostics_ui/resources/network_list.html
index 44e0a4b..b51bd58 100644
--- a/chromeos/components/diagnostics_ui/resources/network_list.html
+++ b/chromeos/components/diagnostics_ui/resources/network_list.html
@@ -5,7 +5,7 @@
 </style>
 
 <div id="networkListContainer">
-  <connectivity-card id="connectivityCard"
+  <connectivity-card id="connectivityCard" active-guid="[[activeGuid_]]"
     is-test-running="{{isTestRunning}}">
   </connectivity-card>
 </div>
diff --git a/chromeos/components/diagnostics_ui/resources/network_list.js b/chromeos/components/diagnostics_ui/resources/network_list.js
index a7a4294..27e48dc 100644
--- a/chromeos/components/diagnostics_ui/resources/network_list.js
+++ b/chromeos/components/diagnostics_ui/resources/network_list.js
@@ -8,6 +8,9 @@
 
 import {html, Polymer} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 
+import {NetworkGuidInfo, NetworkHealthProviderInterface} from './diagnostics_types.js'
+import {getNetworkHealthProvider} from './mojo_interface_provider.js';
+
 /**
  * @fileoverview
  * 'network-list' is responsible for displaying Ethernet, Cellular,
@@ -18,6 +21,11 @@
 
   _template: html`{__html_template__}`,
 
+  /**
+   * @private {?NetworkHealthProviderInterface}
+   */
+  networkHealthProvider_: null,
+
   properties: {
     /** @type {boolean} */
     isTestRunning: {
@@ -25,5 +33,42 @@
       value: false,
       notify: true,
     },
+
+    /** @private {Array<?string>} */
+    otherNetworkGuids_: {
+      type: Array,
+      value: () => [],
+    },
+
+    /** @private {string} */
+    activeGuid_: {
+      type: String,
+      value: '',
+    },
+  },
+
+  /** @override */
+  created() {
+    this.networkHealthProvider_ = getNetworkHealthProvider();
+    this.observeNetworkList_();
+  },
+
+  /** @private */
+  observeNetworkList_() {
+    // Calling observeNetworkList will trigger onNetworkListChanged.
+    this.networkHealthProvider_.observeNetworkList(this);
+  },
+
+  /**
+   * Implements NetworkListObserver.onNetworkListChanged
+   * @param {!NetworkGuidInfo} networkGuidInfo
+   */
+  onNetworkListChanged(networkGuidInfo) {
+    // The connectivity-card is responsible for displaying the active network
+    // so we need to filter out the activeGuid to avoid displaying a
+    // a network-info card for it.
+    this.otherNetworkGuids_ = networkGuidInfo.networkGuids.filter(
+        guid => guid !== networkGuidInfo.activeGuid);
+    this.activeGuid_ = networkGuidInfo.activeGuid || '';
   },
 });
diff --git a/chromeos/system/cpu_temperature_reader.cc b/chromeos/system/cpu_temperature_reader.cc
index f22f26c..b5e28bc24 100644
--- a/chromeos/system/cpu_temperature_reader.cc
+++ b/chromeos/system/cpu_temperature_reader.cc
@@ -79,8 +79,8 @@
        !temperature_path.empty(); temperature_path = enumerator.Next()) {
     CPUTemperatureInfo info;
     if (!ReadTemperatureFromPath(temperature_path, &info.temp_celsius)) {
-      LOG(WARNING) << "Unable to read CPU temperature from "
-                   << temperature_path.value();
+      DLOG(WARNING) << "Unable to read CPU temperature from "
+                    << temperature_path.value();
       continue;
     }
     // Get appropriate temp*_label file.
diff --git a/chromeos/tast_control.gni b/chromeos/tast_control.gni
index 1c9a223..f5617c07 100644
--- a/chromeos/tast_control.gni
+++ b/chromeos/tast_control.gni
@@ -32,6 +32,9 @@
   # crbug.com/1154794
   "policy.DefaultGeolocationSetting",
 
+  # crbug.com/1204092
+  "policy.DisableScreenshotsExtension",
+
   # crbug.com/1115622
   "inputs.VirtualKeyboardOOBE",
 
diff --git a/components/autofill/core/browser/BUILD.gn b/components/autofill/core/browser/BUILD.gn
index 30e52647..4b2941e 100644
--- a/components/autofill/core/browser/BUILD.gn
+++ b/components/autofill/core/browser/BUILD.gn
@@ -356,8 +356,8 @@
 
   if (is_ios || is_android) {
     sources += [
-      "autofill_save_address_profile_delegate_ios.cc",
-      "autofill_save_address_profile_delegate_ios.h",
+      "autofill_save_update_address_profile_delegate_ios.cc",
+      "autofill_save_update_address_profile_delegate_ios.h",
       "payments/autofill_credit_card_filling_infobar_delegate_mobile.cc",
       "payments/autofill_credit_card_filling_infobar_delegate_mobile.h",
       "payments/autofill_offer_notification_infobar_delegate_mobile.cc",
@@ -744,7 +744,7 @@
 
   if (is_ios || is_android) {
     sources += [
-      "autofill_save_address_profile_delegate_ios_unittest.cc",
+      "autofill_save_update_address_profile_delegate_ios_unittest.cc",
       "ui/mobile_label_formatter_unittest.cc",
       "ui/payments/card_expiration_date_fix_flow_controller_impl_unittest.cc",
       "ui/payments/card_name_fix_flow_controller_impl_unittest.cc",
diff --git a/components/autofill/core/browser/address_profile_save_manager_unittest.cc b/components/autofill/core/browser/address_profile_save_manager_unittest.cc
index 7d17c55..d8722dd 100644
--- a/components/autofill/core/browser/address_profile_save_manager_unittest.cc
+++ b/components/autofill/core/browser/address_profile_save_manager_unittest.cc
@@ -66,10 +66,7 @@
 
   void OnUserDecisionForTesting(UserDecision decision,
                                 AutofillProfile edited_profile) {
-    if (decision != UserDecision::kUserNotAsked) {
-      // If the user was asked, make sure the prompt is mared as being shown.
-      pending_import()->set_prompt_was_shown();
-    }
+    pending_import()->set_prompt_was_shown();
     OnUserDecision(decision, edited_profile);
   }
 
@@ -249,6 +246,26 @@
 }
 
 // Test that a profile is correctly imported when no other profile is stored
+// yet. Here, `kUserNotAsked` is supplied which is done as a fallback in case
+// the UI is unavailable for technical reasons.
+TEST_F(AddressProfileSaveManagerTest, SaveNewProfile_UserNotAskedFallback) {
+  AutofillProfile observed_profile = test::StandardProfile();
+
+  ImportScenarioTestCase test_scenario{
+      .existing_profiles = {},
+      .observed_profile = observed_profile,
+      .is_prompt_expected = true,
+      .user_decision = UserDecision::kUserNotAsked,
+      .expected_import_type = AutofillProfileImportType::kNewProfile,
+      .is_profile_change_expected = true,
+      .merge_candidate = base::nullopt,
+      .import_candidate = observed_profile,
+      .expected_final_profiles = {observed_profile}};
+
+  TestImportScenario(test_scenario);
+}
+
+// Test that a profile is correctly imported when no other profile is stored
 // yet. Here, the profile is edited by the user.
 TEST_F(AddressProfileSaveManagerTest, SaveNewProfile_Edited) {
   AutofillProfile observed_profile = test::StandardProfile();
@@ -360,6 +377,30 @@
 }
 
 // Test the observation of a profile that can only be merged with a
+// settings-visible change. Here, `kUserNotAsked` is returned as the fallback
+// mechanism when the UI is not available for technical reasons.
+TEST_F(AddressProfileSaveManagerTest,
+       UserConfirmableMerge_UserNotAskedFallback) {
+  AutofillProfile observed_profile = test::StandardProfile();
+  AutofillProfile mergeable_profile = test::SubsetOfStandardProfile();
+  AutofillProfile final_profile = observed_profile;
+  test::CopyGUID(mergeable_profile, &final_profile);
+
+  ImportScenarioTestCase test_scenario{
+      .existing_profiles = {mergeable_profile},
+      .observed_profile = observed_profile,
+      .is_prompt_expected = true,
+      .user_decision = UserDecision::kUserNotAsked,
+      .expected_import_type = AutofillProfileImportType::kConfirmableMerge,
+      .is_profile_change_expected = true,
+      .merge_candidate = mergeable_profile,
+      .import_candidate = final_profile,
+      .expected_final_profiles = {final_profile}};
+
+  TestImportScenario(test_scenario);
+}
+
+// Test the observation of a profile that can only be merged with a
 // settings-visible change.
 TEST_F(AddressProfileSaveManagerTest, UserConfirmableMerge) {
   AutofillProfile observed_profile = test::StandardProfile();
diff --git a/components/autofill/core/browser/autofill_address_util.cc b/components/autofill/core/browser/autofill_address_util.cc
index 8029e27..3c6cdee 100644
--- a/components/autofill/core/browser/autofill_address_util.cc
+++ b/components/autofill/core/browser/autofill_address_util.cc
@@ -203,7 +203,7 @@
       /*num_fields_to_include=*/label.empty() ? 2 : 1, ui_language_code);
   DCHECK(!details.empty());
   description_components.push_back(details);
-  // TODO(crbug.com/1167061): Replace the separator with proper localized
+  // TODO(crbug.com/1135178): Replace the separator with proper localized
   // string.
   return base::JoinString(description_components, u" — ");
 }
diff --git a/components/autofill/core/browser/autofill_save_address_profile_delegate_ios.cc b/components/autofill/core/browser/autofill_save_update_address_profile_delegate_ios.cc
similarity index 62%
rename from components/autofill/core/browser/autofill_save_address_profile_delegate_ios.cc
rename to components/autofill/core/browser/autofill_save_update_address_profile_delegate_ios.cc
index 9846405..7157fb2 100644
--- a/components/autofill/core/browser/autofill_save_address_profile_delegate_ios.cc
+++ b/components/autofill/core/browser/autofill_save_update_address_profile_delegate_ios.cc
@@ -2,10 +2,11 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "components/autofill/core/browser/autofill_save_address_profile_delegate_ios.h"
+#include "components/autofill/core/browser/autofill_save_update_address_profile_delegate_ios.h"
 
 #include <utility>
 
+#include "base/stl_util.h"
 #include "base/strings/utf_string_conversions.h"
 #include "components/autofill/core/common/autofill_constants.h"
 #include "components/grit/components_scaled_resources.h"
@@ -16,49 +17,53 @@
 
 namespace autofill {
 
-AutofillSaveAddressProfileDelegateIOS::AutofillSaveAddressProfileDelegateIOS(
-    const AutofillProfile& profile,
-    AutofillClient::AddressProfileSavePromptCallback callback)
+AutofillSaveUpdateAddressProfileDelegateIOS::
+    AutofillSaveUpdateAddressProfileDelegateIOS(
+        const AutofillProfile& profile,
+        const AutofillProfile* original_profile,
+        AutofillClient::AddressProfileSavePromptCallback callback)
     : profile_(profile),
+      original_profile_(base::OptionalFromPtr(original_profile)),
       address_profile_save_prompt_callback_(std::move(callback)) {}
 
-AutofillSaveAddressProfileDelegateIOS::
-    ~AutofillSaveAddressProfileDelegateIOS() = default;
+AutofillSaveUpdateAddressProfileDelegateIOS::
+    ~AutofillSaveUpdateAddressProfileDelegateIOS() = default;
 
 // static
-AutofillSaveAddressProfileDelegateIOS*
-AutofillSaveAddressProfileDelegateIOS::FromInfobarDelegate(
+AutofillSaveUpdateAddressProfileDelegateIOS*
+AutofillSaveUpdateAddressProfileDelegateIOS::FromInfobarDelegate(
     infobars::InfoBarDelegate* delegate) {
   return delegate->GetIdentifier() ==
                  AUTOFILL_ADDRESS_PROFILE_INFOBAR_DELEGATE_IOS
-             ? static_cast<AutofillSaveAddressProfileDelegateIOS*>(delegate)
+             ? static_cast<AutofillSaveUpdateAddressProfileDelegateIOS*>(
+                   delegate)
              : nullptr;
 }
 
 std::u16string
-AutofillSaveAddressProfileDelegateIOS::GetMessageDescriptionText() const {
+AutofillSaveUpdateAddressProfileDelegateIOS::GetMessageDescriptionText() const {
   // TODO(crbug.com/1167062): Replace with proper localized string.
   return std::u16string(u"Fill forms faster in Chrome");
 }
 
-std::u16string AutofillSaveAddressProfileDelegateIOS::GetMessageActionText()
-    const {
+std::u16string
+AutofillSaveUpdateAddressProfileDelegateIOS::GetMessageActionText() const {
   // TODO(crbug.com/1167062): Replace with proper localized string.
   return std::u16string(u"Save...");
 }
 
 const autofill::AutofillProfile*
-AutofillSaveAddressProfileDelegateIOS::GetProfile() const {
+AutofillSaveUpdateAddressProfileDelegateIOS::GetProfile() const {
   return &profile_;
 }
 
-bool AutofillSaveAddressProfileDelegateIOS::Accept() {
+bool AutofillSaveUpdateAddressProfileDelegateIOS::Accept() {
   RunSaveAddressProfilePromptCallback(
       AutofillClient::SaveAddressProfileOfferUserDecision::kAccepted);
   return true;
 }
 
-void AutofillSaveAddressProfileDelegateIOS::InfoBarDismissed() {
+void AutofillSaveUpdateAddressProfileDelegateIOS::InfoBarDismissed() {
   // If the address profile modal dialog is presented, the user will be asked to
   // save or cancel the address profile. In case the user cancels, then
   // InfoBarDismissed() will be called.
@@ -69,39 +74,40 @@
       AutofillClient::SaveAddressProfileOfferUserDecision::kDeclined);
 }
 
-bool AutofillSaveAddressProfileDelegateIOS::Cancel() {
+bool AutofillSaveUpdateAddressProfileDelegateIOS::Cancel() {
   RunSaveAddressProfilePromptCallback(
       AutofillClient::SaveAddressProfileOfferUserDecision::kDeclined);
   return true;
 }
 
-int AutofillSaveAddressProfileDelegateIOS::GetIconId() const {
+int AutofillSaveUpdateAddressProfileDelegateIOS::GetIconId() const {
   // TODO(crbug.com/1167062): Replace with proper icon.
   return IDR_INFOBAR_AUTOFILL_CC;
 }
 
-std::u16string AutofillSaveAddressProfileDelegateIOS::GetMessageText() const {
+std::u16string AutofillSaveUpdateAddressProfileDelegateIOS::GetMessageText()
+    const {
   // TODO(crbug.com/1167062): Replace with proper localized string.
   return std::u16string(u"Save address?");
 }
 
 infobars::InfoBarDelegate::InfoBarIdentifier
-AutofillSaveAddressProfileDelegateIOS::GetIdentifier() const {
+AutofillSaveUpdateAddressProfileDelegateIOS::GetIdentifier() const {
   return AUTOFILL_ADDRESS_PROFILE_INFOBAR_DELEGATE_IOS;
 }
 
-bool AutofillSaveAddressProfileDelegateIOS::ShouldExpire(
+bool AutofillSaveUpdateAddressProfileDelegateIOS::ShouldExpire(
     const NavigationDetails& details) const {
   // Expire the Infobar unless the navigation was triggered by the form that
   // presented the Infobar, or the navigation is a redirect.
   return !details.is_form_submission && !details.is_redirect;
 }
 
-int AutofillSaveAddressProfileDelegateIOS::GetButtons() const {
+int AutofillSaveUpdateAddressProfileDelegateIOS::GetButtons() const {
   return BUTTON_OK | BUTTON_CANCEL;
 }
 
-std::u16string AutofillSaveAddressProfileDelegateIOS::GetButtonLabel(
+std::u16string AutofillSaveUpdateAddressProfileDelegateIOS::GetButtonLabel(
     InfoBarButton button) const {
   if (button == BUTTON_OK) {
     // TODO(crbug.com/1167062): Replace with proper localized string.
@@ -117,8 +123,9 @@
   return std::u16string();
 }
 
-void AutofillSaveAddressProfileDelegateIOS::RunSaveAddressProfilePromptCallback(
-    AutofillClient::SaveAddressProfileOfferUserDecision decision) {
+void AutofillSaveUpdateAddressProfileDelegateIOS::
+    RunSaveAddressProfilePromptCallback(
+        AutofillClient::SaveAddressProfileOfferUserDecision decision) {
   std::move(address_profile_save_prompt_callback_).Run(decision, profile_);
 
   // Reset the modal dialog flags.
diff --git a/components/autofill/core/browser/autofill_save_address_profile_delegate_ios.h b/components/autofill/core/browser/autofill_save_update_address_profile_delegate_ios.h
similarity index 65%
rename from components/autofill/core/browser/autofill_save_address_profile_delegate_ios.h
rename to components/autofill/core/browser/autofill_save_update_address_profile_delegate_ios.h
index f059a88..00a8b07d 100644
--- a/components/autofill/core/browser/autofill_save_address_profile_delegate_ios.h
+++ b/components/autofill/core/browser/autofill_save_update_address_profile_delegate_ios.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef COMPONENTS_AUTOFILL_CORE_BROWSER_AUTOFILL_SAVE_ADDRESS_PROFILE_DELEGATE_IOS_H_
-#define COMPONENTS_AUTOFILL_CORE_BROWSER_AUTOFILL_SAVE_ADDRESS_PROFILE_DELEGATE_IOS_H_
+#ifndef COMPONENTS_AUTOFILL_CORE_BROWSER_AUTOFILL_SAVE_UPDATE_ADDRESS_PROFILE_DELEGATE_IOS_H_
+#define COMPONENTS_AUTOFILL_CORE_BROWSER_AUTOFILL_SAVE_UPDATE_ADDRESS_PROFILE_DELEGATE_IOS_H_
 
 #include <memory>
 
@@ -16,20 +16,22 @@
 
 // A delegate for the prompt that enables the user to allow or deny storing
 // an address profile gathered from a form submission. Only used on iOS.
-class AutofillSaveAddressProfileDelegateIOS : public ConfirmInfoBarDelegate {
+class AutofillSaveUpdateAddressProfileDelegateIOS
+    : public ConfirmInfoBarDelegate {
  public:
-  AutofillSaveAddressProfileDelegateIOS(
+  AutofillSaveUpdateAddressProfileDelegateIOS(
       const AutofillProfile& profile,
+      const AutofillProfile* original_profile,
       AutofillClient::AddressProfileSavePromptCallback callback);
-  AutofillSaveAddressProfileDelegateIOS(
-      const AutofillSaveAddressProfileDelegateIOS&) = delete;
-  AutofillSaveAddressProfileDelegateIOS& operator=(
-      const AutofillSaveAddressProfileDelegateIOS&) = delete;
-  ~AutofillSaveAddressProfileDelegateIOS() override;
+  AutofillSaveUpdateAddressProfileDelegateIOS(
+      const AutofillSaveUpdateAddressProfileDelegateIOS&) = delete;
+  AutofillSaveUpdateAddressProfileDelegateIOS& operator=(
+      const AutofillSaveUpdateAddressProfileDelegateIOS&) = delete;
+  ~AutofillSaveUpdateAddressProfileDelegateIOS() override;
 
-  // Returns |delegate| as an AutofillSaveAddressProfileDelegateIOS, or nullptr
-  // if it is of another type.
-  static AutofillSaveAddressProfileDelegateIOS* FromInfobarDelegate(
+  // Returns |delegate| as an AutofillSaveUpdateAddressProfileDelegateIOS, or
+  // nullptr if it is of another type.
+  static AutofillSaveUpdateAddressProfileDelegateIOS* FromInfobarDelegate(
       infobars::InfoBarDelegate* delegate);
 
   std::u16string GetMessageDescriptionText() const;
@@ -58,6 +60,10 @@
   // The profile that will be saved if the user accepts.
   AutofillProfile profile_;
 
+  // The original profile that will be updated if the user accepts the update
+  // prompt. NULL if saving a new profile.
+  base::Optional<AutofillProfile> original_profile_;
+
   // The callback to run once the user makes a decision.
   AutofillClient::AddressProfileSavePromptCallback
       address_profile_save_prompt_callback_;
@@ -71,4 +77,4 @@
 
 }  // namespace autofill
 
-#endif  // COMPONENTS_AUTOFILL_CORE_BROWSER_AUTOFILL_SAVE_ADDRESS_PROFILE_DELEGATE_IOS_H_
+#endif  // COMPONENTS_AUTOFILL_CORE_BROWSER_AUTOFILL_SAVE_UPDATE_ADDRESS_PROFILE_DELEGATE_IOS_H_
diff --git a/components/autofill/core/browser/autofill_save_address_profile_delegate_ios_unittest.cc b/components/autofill/core/browser/autofill_save_update_address_profile_delegate_ios_unittest.cc
similarity index 69%
rename from components/autofill/core/browser/autofill_save_address_profile_delegate_ios_unittest.cc
rename to components/autofill/core/browser/autofill_save_update_address_profile_delegate_ios_unittest.cc
index c003f59..859b5cf 100644
--- a/components/autofill/core/browser/autofill_save_address_profile_delegate_ios_unittest.cc
+++ b/components/autofill/core/browser/autofill_save_update_address_profile_delegate_ios_unittest.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "components/autofill/core/browser/autofill_save_address_profile_delegate_ios.h"
+#include "components/autofill/core/browser/autofill_save_update_address_profile_delegate_ios.h"
 
 #include <memory>
 
@@ -13,11 +13,12 @@
 
 namespace autofill {
 
-TEST(AutofillSaveAddressProfileDelegateIOSTest, HandleUserAction_Accepted) {
+TEST(AutofillSaveUpdateAddressProfileDelegateIOSTest,
+     HandleUserAction_Accepted) {
   AutofillProfile profile = test::GetFullProfile();
   base::MockCallback<AutofillClient::AddressProfileSavePromptCallback> callback;
-  auto delegate = std::make_unique<AutofillSaveAddressProfileDelegateIOS>(
-      profile, callback.Get());
+  auto delegate = std::make_unique<AutofillSaveUpdateAddressProfileDelegateIOS>(
+      profile, /*original_profile=*/nullptr, callback.Get());
 
   EXPECT_CALL(
       callback,
diff --git a/components/autofill/core/browser/webdata/autofill_sync_bridge_util.cc b/components/autofill/core/browser/webdata/autofill_sync_bridge_util.cc
index 2009047..64c5af2f5 100644
--- a/components/autofill/core/browser/webdata/autofill_sync_bridge_util.cc
+++ b/components/autofill/core/browser/webdata/autofill_sync_bridge_util.cc
@@ -100,11 +100,38 @@
   result.SetExpirationMonth(card.exp_month());
   result.SetExpirationYear(card.exp_year());
   result.set_billing_address_id(card.billing_address_id());
-  result.set_card_issuer(
-      static_cast<CreditCard::Issuer>(card.card_issuer().issuer()));
+
+  CreditCard::Issuer issuer = CreditCard::ISSUER_UNKNOWN;
+  switch (card.card_issuer().issuer()) {
+    case sync_pb::CardIssuer::ISSUER_UNKNOWN:
+      issuer = CreditCard::ISSUER_UNKNOWN;
+      break;
+    case sync_pb::CardIssuer::GOOGLE:
+      issuer = CreditCard::GOOGLE;
+      break;
+  }
+  result.set_card_issuer(issuer);
+
   if (!card.nickname().empty())
     result.SetNickname(base::UTF8ToUTF16(card.nickname()));
   result.set_instrument_id(card.instrument_id());
+
+  CreditCard::VirtualCardEnrollmentState state = CreditCard::UNSPECIFIED;
+  switch (card.virtual_card_enrollment_state()) {
+    case sync_pb::WalletMaskedCreditCard::UNENROLLED:
+      state = CreditCard::UNENROLLED;
+      break;
+    case sync_pb::WalletMaskedCreditCard::ENROLLED:
+      state = CreditCard::ENROLLED;
+      break;
+    case sync_pb::WalletMaskedCreditCard::UNSPECIFIED:
+      state = CreditCard::UNSPECIFIED;
+      break;
+  }
+  result.set_virtual_card_enrollment_state(state);
+
+  if (!card.card_art_url().empty())
+    result.set_card_art_url(GURL(card.card_art_url()));
   return result;
 }
 
@@ -247,9 +274,37 @@
   wallet_card->set_exp_year(card.expiration_year());
   if (!card.nickname().empty())
     wallet_card->set_nickname(base::UTF16ToUTF8(card.nickname()));
-  wallet_card->mutable_card_issuer()->set_issuer(
-      static_cast<sync_pb::CardIssuer::Issuer>(card.card_issuer()));
+
+  sync_pb::CardIssuer::Issuer issuer = sync_pb::CardIssuer::ISSUER_UNKNOWN;
+  switch (card.card_issuer()) {
+    case CreditCard::ISSUER_UNKNOWN:
+      issuer = sync_pb::CardIssuer::ISSUER_UNKNOWN;
+      break;
+    case CreditCard::GOOGLE:
+      issuer = sync_pb::CardIssuer::GOOGLE;
+      break;
+  }
+  wallet_card->mutable_card_issuer()->set_issuer(issuer);
+
   wallet_card->set_instrument_id(card.instrument_id());
+
+  sync_pb::WalletMaskedCreditCard::VirtualCardEnrollmentState state =
+      sync_pb::WalletMaskedCreditCard::UNSPECIFIED;
+  switch (card.virtual_card_enrollment_state()) {
+    case CreditCard::UNENROLLED:
+      state = sync_pb::WalletMaskedCreditCard::UNENROLLED;
+      break;
+    case CreditCard::ENROLLED:
+      state = sync_pb::WalletMaskedCreditCard::ENROLLED;
+      break;
+    case CreditCard::UNSPECIFIED:
+      state = sync_pb::WalletMaskedCreditCard::UNSPECIFIED;
+      break;
+  }
+  wallet_card->set_virtual_card_enrollment_state(state);
+
+  if (!card.card_art_url().is_empty())
+    wallet_card->set_card_art_url(card.card_art_url().spec());
 }
 
 void SetAutofillWalletSpecificsFromPaymentsCustomerData(
diff --git a/components/autofill/core/browser/webdata/autofill_sync_bridge_util_unittest.cc b/components/autofill/core/browser/webdata/autofill_sync_bridge_util_unittest.cc
index 645bf1a..0d0d5bf 100644
--- a/components/autofill/core/browser/webdata/autofill_sync_bridge_util_unittest.cc
+++ b/components/autofill/core/browser/webdata/autofill_sync_bridge_util_unittest.cc
@@ -85,6 +85,9 @@
       CreateAutofillWalletSpecificsForCard(
           /*id=*/credit_card_id_1,
           /*billing_address_id=*/address_id);
+  wallet_specifics_card1.mutable_masked_card()
+      ->set_virtual_card_enrollment_state(
+          sync_pb::WalletMaskedCreditCard::UNENROLLED);
   // Add the second card that has nickname.
   std::string nickname("Grocery card");
   sync_pb::AutofillWalletSpecifics wallet_specifics_card2 =
@@ -95,6 +98,11 @@
   wallet_specifics_card2.mutable_masked_card()
       ->mutable_card_issuer()
       ->set_issuer(sync_pb::CardIssuer::GOOGLE);
+  wallet_specifics_card2.mutable_masked_card()
+      ->set_virtual_card_enrollment_state(
+          sync_pb::WalletMaskedCreditCard::ENROLLED);
+  wallet_specifics_card2.mutable_masked_card()->set_card_art_url(
+      "https://www.example.com/card.png");
   entity_data.push_back(EntityChange::CreateAdd(
       credit_card_id_1,
       SpecificsToEntity(wallet_specifics_card1, /*client_tag=*/"card-card1")));
@@ -141,6 +149,17 @@
   // Verify that the card_issuer is set correctly.
   EXPECT_EQ(wallet_cards.front().card_issuer(), CreditCard::ISSUER_UNKNOWN);
   EXPECT_EQ(wallet_cards.back().card_issuer(), CreditCard::GOOGLE);
+
+  // Verify that the virtual_card_enrollment_state is set correctly.
+  EXPECT_EQ(wallet_cards.front().virtual_card_enrollment_state(),
+            CreditCard::UNENROLLED);
+  EXPECT_EQ(wallet_cards.back().virtual_card_enrollment_state(),
+            CreditCard::ENROLLED);
+
+  // Verify that the card_art_url is set correctly.
+  EXPECT_TRUE(wallet_cards.front().card_art_url().is_empty());
+  EXPECT_EQ(wallet_cards.back().card_art_url().spec(),
+            "https://www.example.com/card.png");
 }
 
 // Verify that the billing address id from the card saved on disk is kept if it
diff --git a/components/autofill/core/browser/webdata/autofill_wallet_sync_bridge_unittest.cc b/components/autofill/core/browser/webdata/autofill_wallet_sync_bridge_unittest.cc
index 5a55f02..a8a8f3f7 100644
--- a/components/autofill/core/browser/webdata/autofill_wallet_sync_bridge_unittest.cc
+++ b/components/autofill/core/browser/webdata/autofill_wallet_sync_bridge_unittest.cc
@@ -406,8 +406,13 @@
   CreditCard card1 = test::GetMaskedServerCard();
   // Set the card issuer to Google.
   card1.set_card_issuer(CreditCard::Issuer::GOOGLE);
+  card1.set_virtual_card_enrollment_state(
+      CreditCard::VirtualCardEnrollmentState::UNENROLLED);
+  card1.set_card_art_url(GURL("https://www.example.com/card.png"));
   CreditCard card2 = test::GetMaskedServerCardAmex();
   CreditCard card_with_nickname = test::GetMaskedServerCardWithNickname();
+  card2.set_virtual_card_enrollment_state(
+      CreditCard::VirtualCardEnrollmentState::ENROLLED);
   table()->SetServerCreditCards({card1, card2, card_with_nickname});
   PaymentsCustomerData customer_data{/*customer_id=*/kCustomerDataId};
   table()->SetPaymentsCustomerData(&customer_data);
@@ -444,6 +449,14 @@
             card_specifics1.masked_card().card_issuer().issuer());
   EXPECT_EQ(sync_pb::CardIssuer::ISSUER_UNKNOWN,
             card_specifics2.masked_card().card_issuer().issuer());
+  EXPECT_EQ(sync_pb::WalletMaskedCreditCard::UNENROLLED,
+            card_specifics1.masked_card().virtual_card_enrollment_state());
+  EXPECT_EQ(sync_pb::WalletMaskedCreditCard::ENROLLED,
+            card_specifics2.masked_card().virtual_card_enrollment_state());
+  EXPECT_EQ("https://www.example.com/card.png",
+            card_specifics1.masked_card().card_art_url());
+  EXPECT_TRUE(card_specifics2.masked_card().card_art_url().empty());
+
   // Read local Wallet Data from Autofill table, and compare with expected
   // wallet specifics.
   EXPECT_THAT(
@@ -488,6 +501,9 @@
   AutofillProfile address1 = test::GetServerProfile();
   table()->SetServerProfiles({address1});
   CreditCard card1 = test::GetMaskedServerCard();
+  card1.set_virtual_card_enrollment_state(
+      CreditCard::VirtualCardEnrollmentState::UNENROLLED);
+  card1.set_card_art_url(GURL("https://www.example.com/card.png"));
   table()->SetServerCreditCards({card1});
   PaymentsCustomerData customer_data{/*customer_id=*/kCustomerDataId};
   table()->SetPaymentsCustomerData(&customer_data);
@@ -500,6 +516,9 @@
   AutofillWalletSpecifics profile_specifics2;
   SetAutofillWalletSpecificsFromServerProfile(address2, &profile_specifics2);
   CreditCard card2 = test::GetMaskedServerCardAmex();
+  card2.set_virtual_card_enrollment_state(
+      CreditCard::VirtualCardEnrollmentState::ENROLLED);
+  card2.set_card_art_url(GURL("https://www.test.com/card.png"));
   AutofillWalletSpecifics card_specifics2;
   SetAutofillWalletSpecificsFromServerCard(card2, &card_specifics2);
   AutofillWalletSpecifics customer_data_specifics;
@@ -714,6 +733,9 @@
   AutofillProfile profile = test::GetServerProfile();
   table()->SetServerProfiles({profile});
   CreditCard card = test::GetMaskedServerCard();
+  card.set_virtual_card_enrollment_state(
+      CreditCard::VirtualCardEnrollmentState::UNENROLLED);
+  card.set_card_art_url(GURL("https://www.example.com/card.png"));
   table()->SetServerCreditCards({card});
   PaymentsCustomerData customer_data{/*customer_id=*/kCustomerDataId};
   table()->SetPaymentsCustomerData(&customer_data);
@@ -856,6 +878,9 @@
   card.SetNickname(u"Grocery card");
   // Set the card issuer to Google.
   card.set_card_issuer(CreditCard::Issuer::GOOGLE);
+  card.set_virtual_card_enrollment_state(
+      CreditCard::VirtualCardEnrollmentState::UNENROLLED);
+  card.set_card_art_url(GURL("https://www.example.com/card.png"));
   AutofillWalletSpecifics card_specifics;
   SetAutofillWalletSpecificsFromServerCard(card, &card_specifics);
 
@@ -882,6 +907,9 @@
   EXPECT_EQ(card.nickname(), cards[0]->nickname());
   EXPECT_EQ(card.card_issuer(), cards[0]->card_issuer());
   EXPECT_EQ(card.instrument_id(), cards[0]->instrument_id());
+  EXPECT_EQ(card.virtual_card_enrollment_state(),
+            cards[0]->virtual_card_enrollment_state());
+  EXPECT_EQ(card.card_art_url(), cards[0]->card_art_url());
 
   // Also make sure that those types are not empty, to exercice all the code
   // paths.
diff --git a/components/back_forward_cache/back_forward_cache_disable.cc b/components/back_forward_cache/back_forward_cache_disable.cc
index 216dec71..5546dac 100644
--- a/components/back_forward_cache/back_forward_cache_disable.cc
+++ b/components/back_forward_cache/back_forward_cache_disable.cc
@@ -31,6 +31,8 @@
       return "PermissionRequestManager";
     case DisabledReasonId::kModalDialog:
       return "ModalDialog";
+    case DisabledReasonId::kExtensions:
+      return "Extensions";
     default:
       return "Unknown (default)";
   }
diff --git a/components/back_forward_cache/back_forward_cache_disable.h b/components/back_forward_cache/back_forward_cache_disable.h
index 2d200d58..fb6520f 100644
--- a/components/back_forward_cache/back_forward_cache_disable.h
+++ b/components/back_forward_cache/back_forward_cache_disable.h
@@ -29,6 +29,7 @@
   // Modal dialog such as form resubmittion or http password dialog is shown for
   // the page.
   kModalDialog = 11,
+  kExtensions = 12,
   // New reasons should be accompanied by a comment as to why BackForwardCache
   // cannot be used in this case and a link to a bug to fix that if it is
   // fixable.
diff --git a/components/breadcrumbs/core/BUILD.gn b/components/breadcrumbs/core/BUILD.gn
index 1169e11..196014a 100644
--- a/components/breadcrumbs/core/BUILD.gn
+++ b/components/breadcrumbs/core/BUILD.gn
@@ -26,14 +26,28 @@
   testonly = true
   deps = [
     ":core",
+    ":generate_not_user_triggered_actions",
     "//base/test:test_support",
     "//testing/gtest",
   ]
 
   sources = [
+    "application_breadcrumbs_not_user_action_unittest.cc",
     "breadcrumb_manager_keyed_service_unittest.cc",
     "breadcrumb_manager_observer_unittest.cc",
     "breadcrumb_manager_unittest.cc",
     "breadcrumb_persistent_storage_util_unittest.cc",
   ]
 }
+
+action("generate_not_user_triggered_actions") {
+  script = "generate_not_user_triggered_actions.py"
+  sources = [ "//tools/metrics/actions/actions.xml" ]
+  outputs = [ "$target_gen_dir/application_breadcrumbs_not_user_action.inc" ]
+  args = [
+    "--actions",
+    rebase_path(sources[0], root_build_dir),
+    "--output",
+    rebase_path(outputs[0], root_build_dir),
+  ]
+}
diff --git a/ios/chrome/browser/crash_report/breadcrumbs/application_breadcrumbs_not_user_action_unittest.mm b/components/breadcrumbs/core/application_breadcrumbs_not_user_action_unittest.cc
similarity index 88%
rename from ios/chrome/browser/crash_report/breadcrumbs/application_breadcrumbs_not_user_action_unittest.mm
rename to components/breadcrumbs/core/application_breadcrumbs_not_user_action_unittest.cc
index be60df3c..2486185 100644
--- a/ios/chrome/browser/crash_report/breadcrumbs/application_breadcrumbs_not_user_action_unittest.mm
+++ b/components/breadcrumbs/core/application_breadcrumbs_not_user_action_unittest.cc
@@ -2,17 +2,13 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#import "ios/chrome/browser/crash_report/breadcrumbs/application_breadcrumbs_not_user_action.inc"
+#include "components/breadcrumbs/core/application_breadcrumbs_not_user_action.inc"
 
 #include <string>
 #include <vector>
 
 #include "testing/platform_test.h"
 
-#if !defined(__has_feature) || !__has_feature(objc_arc)
-#error "This file requires ARC support."
-#endif
-
 // Tests that kNotUserTriggeredActions array was correctly generated by
 // generate_not_user_triggered_actions.py
 using ApplicationBreadcrumbsNotUserActions = PlatformTest;
@@ -24,7 +20,7 @@
   // large.
   EXPECT_LT(
       std::end(kNotUserTriggeredActions) - std::begin(kNotUserTriggeredActions),
-      200U);
+      200);
 }
 
 // Tests that each string in kNotUserTriggeredActions array is not too long but
diff --git a/ios/chrome/browser/crash_report/breadcrumbs/generate_not_user_triggered_actions.py b/components/breadcrumbs/core/generate_not_user_triggered_actions.py
similarity index 100%
rename from ios/chrome/browser/crash_report/breadcrumbs/generate_not_user_triggered_actions.py
rename to components/breadcrumbs/core/generate_not_user_triggered_actions.py
diff --git a/components/content_settings/browser/page_specific_content_settings.cc b/components/content_settings/browser/page_specific_content_settings.cc
index 49d1a941..5b5d410 100644
--- a/components/content_settings/browser/page_specific_content_settings.cc
+++ b/components/content_settings/browser/page_specific_content_settings.cc
@@ -118,10 +118,9 @@
       map_(delegate_->GetSettingsMap()) {
   DCHECK(!PageSpecificContentSettings::GetForCurrentDocument(
       web_contents->GetMainFrame()));
-  content::SetRenderDocumentHostUserData(
-      web_contents->GetMainFrame(), PageSpecificContentSettings::UserDataKey(),
-      base::WrapUnique(
-          new PageSpecificContentSettings(*this, delegate_.get())));
+  content::RenderDocumentHostUserData<PageSpecificContentSettings>::
+      CreateForCurrentDocument(web_contents->GetMainFrame(), *this,
+                               delegate_.get());
 }
 
 PageSpecificContentSettings::WebContentsHandler::~WebContentsHandler() {
@@ -146,16 +145,13 @@
 void PageSpecificContentSettings::WebContentsHandler::OnCookiesAccessed(
     content::NavigationHandle* navigation,
     const content::CookieAccessDetails& details) {
-  auto it = inflight_navigation_settings_.find(navigation);
-  if (it != inflight_navigation_settings_.end()) {
-    it->second.cookie_accesses.push_back(details);
+  if (WillNavigationCreateNewPageSpecificContentSettingsOnCommit(navigation)) {
+    auto* inflight_navigation_settings =
+        content::NavigationHandleUserData<InflightNavigationContentSettings>::
+            GetOrCreateForNavigationHandle(*navigation);
+    inflight_navigation_settings->cookie_accesses.push_back(details);
     return;
   }
-  // TODO(carlscab): We should be able to
-  // DHECK(!WillNavigationCreateNewPageSpecificContentSettingsOnCommit) here,
-  // but there is still code that starts a navigation before attaching the tab
-  // helpers in DevConsole related code. So we miss the DidStartNavigation event
-  // for those navigations. (https://crbug.com/1095576)
   OnCookiesAccessed(web_contents()->GetMainFrame(), details);
 }
 
@@ -174,17 +170,14 @@
     content::AllowServiceWorkerResult allowed) {
   DCHECK(scope.is_valid());
 
-  auto it = inflight_navigation_settings_.find(navigation);
-  if (it != inflight_navigation_settings_.end()) {
-    it->second.service_worker_accesses.emplace_back(
+  if (WillNavigationCreateNewPageSpecificContentSettingsOnCommit(navigation)) {
+    auto* inflight_navigation_settings =
+        content::NavigationHandleUserData<InflightNavigationContentSettings>::
+            GetOrCreateForNavigationHandle(*navigation);
+    inflight_navigation_settings->service_worker_accesses.emplace_back(
         std::make_pair(scope, allowed));
     return;
   }
-  // TODO(carlscab): We should be able to
-  // DHECK(!WillNavigationCreateNewPageSpecificContentSettingsOnCommit) here,
-  // but there is still code that starts a navigation before attaching the tab
-  // helpers in DevConsole related code. So we miss the DidStartNavigation event
-  // for those navigations.
   OnServiceWorkerAccessed(web_contents()->GetMainFrame(), scope, allowed);
 }
 
@@ -198,17 +191,6 @@
     tscs->OnServiceWorkerAccessed(scope, allowed);
 }
 
-void PageSpecificContentSettings::WebContentsHandler::DidStartNavigation(
-    content::NavigationHandle* navigation_handle) {
-  if (!WillNavigationCreateNewPageSpecificContentSettingsOnCommit(
-          navigation_handle)) {
-    return;
-  }
-
-  inflight_navigation_settings_.insert(
-      std::make_pair(navigation_handle, InflightNavigationContentSettings()));
-}
-
 void PageSpecificContentSettings::WebContentsHandler::ReadyToCommitNavigation(
     content::NavigationHandle* navigation_handle) {
   if (!WillNavigationCreateNewPageSpecificContentSettingsOnCommit(
@@ -227,29 +209,21 @@
 void PageSpecificContentSettings::WebContentsHandler::DidFinishNavigation(
     content::NavigationHandle* navigation_handle) {
   if (!WillNavigationCreateNewPageSpecificContentSettingsOnCommit(
-          navigation_handle)) {
+          navigation_handle) ||
+      !navigation_handle->HasCommitted()) {
     return;
   }
 
-  if (!navigation_handle->HasCommitted()) {
-    inflight_navigation_settings_.erase(navigation_handle);
-    return;
-  }
+  content::RenderDocumentHostUserData<PageSpecificContentSettings>::
+      CreateForCurrentDocument(navigation_handle->GetRenderFrameHost(), *this,
+                               delegate_.get());
+  InflightNavigationContentSettings* inflight_settings =
+      content::NavigationHandleUserData<InflightNavigationContentSettings>::
+          GetForNavigationHandle(*navigation_handle);
 
-  auto tscs =
-      base::WrapUnique(new PageSpecificContentSettings(*this, delegate_.get()));
-
-  // TODO(carlscab): This sort of internal. Maybe add a
-  // RenderDocumentHostUserData::Create(RenderFrameHost* rfh, Params...)
-  content::SetRenderDocumentHostUserData(
-      navigation_handle->GetRenderFrameHost(),
-      PageSpecificContentSettings::UserDataKey(), std::move(tscs));
-
-  auto it = inflight_navigation_settings_.find(navigation_handle);
-  if (it != inflight_navigation_settings_.end()) {
+  if (inflight_settings) {
     TransferNavigationContentSettingsToCommittedDocument(
-        it->second, navigation_handle->GetRenderFrameHost());
-    inflight_navigation_settings_.erase(it);
+        *inflight_settings, navigation_handle->GetRenderFrameHost());
   }
   delegate_->UpdateLocationBar();
 }
@@ -279,29 +253,19 @@
     observer.OnSiteDataAccessed();
 }
 
-PageSpecificContentSettings::WebContentsHandler::
-    InflightNavigationContentSettings::InflightNavigationContentSettings() =
-        default;
-PageSpecificContentSettings::WebContentsHandler::
-    InflightNavigationContentSettings::InflightNavigationContentSettings(
-        const InflightNavigationContentSettings&) = default;
-PageSpecificContentSettings::WebContentsHandler::
-    InflightNavigationContentSettings::InflightNavigationContentSettings(
-        InflightNavigationContentSettings&&) = default;
+PageSpecificContentSettings::InflightNavigationContentSettings::
+    InflightNavigationContentSettings(content::NavigationHandle&) {}
 
-PageSpecificContentSettings::WebContentsHandler::
-    InflightNavigationContentSettings::~InflightNavigationContentSettings() =
-        default;
+PageSpecificContentSettings::InflightNavigationContentSettings::
+    ~InflightNavigationContentSettings() = default;
 
-PageSpecificContentSettings::WebContentsHandler::
-    InflightNavigationContentSettings&
-    PageSpecificContentSettings::WebContentsHandler::
-        InflightNavigationContentSettings::operator=(
-            InflightNavigationContentSettings&&) = default;
+NAVIGATION_HANDLE_USER_DATA_KEY_IMPL(
+    PageSpecificContentSettings::InflightNavigationContentSettings)
 
 WEB_CONTENTS_USER_DATA_KEY_IMPL(PageSpecificContentSettings::WebContentsHandler)
 
 PageSpecificContentSettings::PageSpecificContentSettings(
+    content::RenderFrameHost*,
     PageSpecificContentSettings::WebContentsHandler& handler,
     Delegate* delegate)
     : handler_(handler),
diff --git a/components/content_settings/browser/page_specific_content_settings.h b/components/content_settings/browser/page_specific_content_settings.h
index 1fd75bd7..06b1363 100644
--- a/components/content_settings/browser/page_specific_content_settings.h
+++ b/components/content_settings/browser/page_specific_content_settings.h
@@ -26,6 +26,7 @@
 #include "components/content_settings/core/common/content_settings.h"
 #include "components/content_settings/core/common/content_settings_types.h"
 #include "content/public/browser/allow_service_worker_result.h"
+#include "content/public/browser/navigation_handle_user_data.h"
 #include "content/public/browser/render_document_host_user_data.h"
 #include "content/public/browser/render_frame_host.h"
 #include "content/public/browser/web_contents_observer.h"
@@ -390,6 +391,31 @@
  private:
   friend class content::RenderDocumentHostUserData<PageSpecificContentSettings>;
 
+  // Keeps track of cookie and service worker access during a navigation.
+  // These types of access can happen for the current page or for a new
+  // navigation (think cookies sent in the HTTP request or service worker
+  // being run to serve a fetch request). A navigation might fail to
+  // commit in which case we have to handle it as if it had never
+  // occurred. So we cache all cookies and service worker accesses that
+  // happen during a navigation and only apply the changes if the
+  // navigation commits.
+  class InflightNavigationContentSettings
+      : public content::NavigationHandleUserData<
+            InflightNavigationContentSettings> {
+   public:
+    ~InflightNavigationContentSettings() override;
+    std::vector<content::CookieAccessDetails> cookie_accesses;
+    std::vector<std::pair<GURL, content::AllowServiceWorkerResult>>
+        service_worker_accesses;
+
+   private:
+    explicit InflightNavigationContentSettings(
+        content::NavigationHandle& navigation_handle);
+    friend class content::NavigationHandleUserData<
+        InflightNavigationContentSettings>;
+    NAVIGATION_HANDLE_USER_DATA_KEY_DECL();
+  };
+
   // This class attaches to WebContents to listen to events and route them to
   // appropriate PageSpecificContentSettings, store navigation related events
   // until the navigation finishes and then transferring the
@@ -416,30 +442,6 @@
    private:
     friend class content::WebContentsUserData<WebContentsHandler>;
 
-    // Keeps track of cookie and service worker access during a navigation.
-    // These types of access can happen for the current page or for a new
-    // navigation (think cookies sent in the HTTP request or service worker
-    // being run to serve a fetch request). A navigation might fail to
-    // commit in which case we have to handle it as if it had never
-    // occurred. So we cache all cookies and service worker accesses that
-    // happen during a navigation and only apply the changes if the
-    // navigation commits.
-    struct InflightNavigationContentSettings {
-      InflightNavigationContentSettings();
-      InflightNavigationContentSettings(
-          const InflightNavigationContentSettings&);
-      InflightNavigationContentSettings(InflightNavigationContentSettings&&);
-
-      ~InflightNavigationContentSettings();
-
-      InflightNavigationContentSettings& operator=(
-          InflightNavigationContentSettings&&);
-
-      std::vector<content::CookieAccessDetails> cookie_accesses;
-      std::vector<std::pair<GURL, content::AllowServiceWorkerResult>>
-          service_worker_accesses;
-    };
-
     // Applies all stored events for the given navigation to the current main
     // document.
     void TransferNavigationContentSettingsToCommittedDocument(
@@ -447,8 +449,6 @@
         content::RenderFrameHost* rfh);
 
     // content::WebContentsObserver overrides.
-    void DidStartNavigation(
-        content::NavigationHandle* navigation_handle) override;
     void ReadyToCommitNavigation(
         content::NavigationHandle* navigation_handle) override;
     void DidFinishNavigation(
@@ -483,16 +483,11 @@
     // All currently registered |SiteDataObserver|s.
     base::ObserverList<SiteDataObserver>::Unchecked observer_list_;
 
-    // Keeps track of currently inflight navigations. Updates for those are
-    // kept aside until the navigation commits.
-    std::unordered_map<content::NavigationHandle*,
-                       InflightNavigationContentSettings>
-        inflight_navigation_settings_;
-
     WEB_CONTENTS_USER_DATA_KEY_DECL();
   };
 
   explicit PageSpecificContentSettings(
+      content::RenderFrameHost* rfh,
       PageSpecificContentSettings::WebContentsHandler& handler,
       Delegate* delegate);
 
diff --git a/components/enterprise/BUILD.gn b/components/enterprise/BUILD.gn
index bbbac7f8..b6309ee 100644
--- a/components/enterprise/BUILD.gn
+++ b/components/enterprise/BUILD.gn
@@ -32,6 +32,8 @@
       "browser/reporting/policy_info.h",
       "browser/reporting/profile_report_generator.cc",
       "browser/reporting/profile_report_generator.h",
+      "browser/reporting/real_time_report_generator.cc",
+      "browser/reporting/real_time_report_generator.h",
       "browser/reporting/real_time_uploader.cc",
       "browser/reporting/real_time_uploader.h",
       "browser/reporting/report_generator.cc",
diff --git a/components/enterprise/DEPS b/components/enterprise/DEPS
index c7c2cb1..fb27fb38 100644
--- a/components/enterprise/DEPS
+++ b/components/enterprise/DEPS
@@ -7,4 +7,5 @@
   "+components/version_info",
   "+net",
   "+services/network/public",
+  "+third_party/protobuf"
 ]
diff --git a/components/enterprise/browser/reporting/real_time_report_generator.cc b/components/enterprise/browser/reporting/real_time_report_generator.cc
new file mode 100644
index 0000000..5c23721f
--- /dev/null
+++ b/components/enterprise/browser/reporting/real_time_report_generator.cc
@@ -0,0 +1,25 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/enterprise/browser/reporting/real_time_report_generator.h"
+#include "components/enterprise/browser/reporting/reporting_delegate_factory.h"
+
+namespace enterprise_reporting {
+
+RealTimeReportGenerator::Delegate::Delegate() = default;
+RealTimeReportGenerator::Delegate::~Delegate() = default;
+
+RealTimeReportGenerator::RealTimeReportGenerator(
+    ReportingDelegateFactory* delegate_factory)
+    : delegate_(delegate_factory->GetRealTimeReportGeneratorDelegate()) {}
+RealTimeReportGenerator::~RealTimeReportGenerator() = default;
+
+std::vector<std::unique_ptr<google::protobuf::MessageLite>>
+RealTimeReportGenerator::Generate(ReportType type) {
+  if (!delegate_)
+    return std::vector<std::unique_ptr<google::protobuf::MessageLite>>();
+  return delegate_->Generate(type);
+}
+
+}  // namespace enterprise_reporting
diff --git a/components/enterprise/browser/reporting/real_time_report_generator.h b/components/enterprise/browser/reporting/real_time_report_generator.h
new file mode 100644
index 0000000..c0fc400
--- /dev/null
+++ b/components/enterprise/browser/reporting/real_time_report_generator.h
@@ -0,0 +1,54 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_ENTERPRISE_BROWSER_REPORTING_REAL_TIME_REPORT_GENERATOR_H_
+#define COMPONENTS_ENTERPRISE_BROWSER_REPORTING_REAL_TIME_REPORT_GENERATOR_H_
+
+#include <memory>
+#include <vector>
+
+#include "third_party/protobuf/src/google/protobuf/message_lite.h"
+
+namespace enterprise_reporting {
+
+class ReportingDelegateFactory;
+
+// Generator of reports that is uploaded with the ERP (Encrypted Reporting
+// Pipeline). The reports generated here should be relatively small and can be
+// uploaded much more frequently than the CBCM status report.
+class RealTimeReportGenerator {
+ public:
+  enum ReportType { kExtensionRequest = 0 };
+
+  // Delegate class that is used to collect information and generate reports
+  // outside the //components. For example, RealTimeReportGeneratorDesktop
+  // actual_report chrome/browser/enterprise/reporting.
+  class Delegate {
+   public:
+    Delegate();
+    Delegate(const Delegate&) = delete;
+    Delegate& operator=(const Delegate&) = delete;
+    virtual ~Delegate();
+
+    virtual std::vector<std::unique_ptr<google::protobuf::MessageLite>>
+    Generate(ReportType type) = 0;
+  };
+
+  explicit RealTimeReportGenerator(ReportingDelegateFactory* delegate_factory);
+  RealTimeReportGenerator(const RealTimeReportGenerator&) = delete;
+  RealTimeReportGenerator& operator=(const RealTimeReportGenerator&) = delete;
+  ~RealTimeReportGenerator();
+
+  // Generates and returns reports for |type|. Multiple reports can be generated
+  // together in case of previous events are not generated successfully.
+  std::vector<std::unique_ptr<google::protobuf::MessageLite>> Generate(
+      ReportType type);
+
+ private:
+  std::unique_ptr<Delegate> delegate_;
+};
+
+}  // namespace enterprise_reporting
+
+#endif  // COMPONENTS_ENTERPRISE_BROWSER_REPORTING_REAL_TIME_REPORT_GENERATOR_H_
diff --git a/components/enterprise/browser/reporting/reporting_delegate_factory.h b/components/enterprise/browser/reporting/reporting_delegate_factory.h
index f4f64d86..9cdbaff7 100644
--- a/components/enterprise/browser/reporting/reporting_delegate_factory.h
+++ b/components/enterprise/browser/reporting/reporting_delegate_factory.h
@@ -9,6 +9,7 @@
 
 #include "components/enterprise/browser/reporting/browser_report_generator.h"
 #include "components/enterprise/browser/reporting/profile_report_generator.h"
+#include "components/enterprise/browser/reporting/real_time_report_generator.h"
 #include "components/enterprise/browser/reporting/report_generator.h"
 #include "components/enterprise/browser/reporting/report_scheduler.h"
 
@@ -33,6 +34,9 @@
 
   virtual std::unique_ptr<ReportScheduler::Delegate>
   GetReportSchedulerDelegate() = 0;
+
+  virtual std::unique_ptr<RealTimeReportGenerator::Delegate>
+  GetRealTimeReportGeneratorDelegate() = 0;
 };
 
 }  // namespace enterprise_reporting
diff --git a/components/feed/core/v2/api_test/feed_api_stream_unittest.cc b/components/feed/core/v2/api_test/feed_api_stream_unittest.cc
index fc3b83c..edee4d23 100644
--- a/components/feed/core/v2/api_test/feed_api_stream_unittest.cc
+++ b/components/feed/core/v2/api_test/feed_api_stream_unittest.cc
@@ -17,6 +17,7 @@
 #include "components/feed/core/v2/public/feed_api.h"
 #include "components/feed/core/v2/public/feed_service.h"
 #include "components/feed/core/v2/public/stream_type.h"
+#include "components/feed/core/v2/scheduling.h"
 #include "components/feed/core/v2/test/callback_receiver.h"
 #include "components/feed/core/v2/test/stream_builder.h"
 #include "components/feed/feed_feature_list.h"
@@ -64,22 +65,24 @@
   EXPECT_EQ(1, prefetch_service_.NewSuggestionsAvailableCallCount());
 }
 
-TEST_F(FeedApiTest, BackgroundRefreshWebFeedSuccess) {
-  // Trigger a background refresh.
-  response_translator_.InjectResponse(MakeTypicalInitialModelState());
-  stream_->ExecuteRefreshTask(RefreshTaskId::kRefreshWebFeed);
-  WaitForIdleTaskQueue();
+TEST_F(FeedApiTest, WebFeedDoesNotBackgroundRefresh) {
+  {
+    RefreshResponseData injected_response;
+    injected_response.model_update_request = MakeTypicalInitialModelState();
+    RequestSchedule schedule;
+    schedule.anchor_time = kTestTimeEpoch;
+    schedule.refresh_offsets = {base::TimeDelta::FromSeconds(12),
+                                base::TimeDelta::FromSeconds(48)};
 
-  // Verify the refresh happened and that we can load a stream without the
-  // network.
-  ASSERT_TRUE(
-      refresh_scheduler_.completed_tasks.count(RefreshTaskId::kRefreshWebFeed));
-  EXPECT_TRUE(response_translator_.InjectedResponseConsumed());
+    injected_response.request_schedule = schedule;
+    response_translator_.InjectResponse(std::move(injected_response));
+  }
+
   TestWebFeedSurface surface(stream_.get());
   WaitForIdleTaskQueue();
-  EXPECT_EQ("loading -> 2 slices", surface.DescribeUpdates());
-  // Verify that prefetch service is NOT informed.
-  EXPECT_EQ(0, prefetch_service_.NewSuggestionsAvailableCallCount());
+
+  // The request schedule should be ignored.
+  EXPECT_TRUE(refresh_scheduler_.scheduled_run_times.empty());
 }
 
 TEST_F(FeedApiTest, BackgroundRefreshPrefetchesImages) {
@@ -365,7 +368,7 @@
             surface.DescribeUpdates());
 }
 
-TEST_P(FeedStreamTestForAllStreamTypes, RefreshScheduleFlow) {
+TEST_F(FeedApiTest, RefreshScheduleFlow) {
   // Inject a typical network response, with a server-defined request schedule.
   {
     RequestSchedule schedule;
@@ -380,36 +383,41 @@
 
     // Load the stream, and then destroy the surface to allow background
     // refresh.
-    TestSurface surface(stream_.get());
+    TestForYouSurface surface(stream_.get());
     WaitForIdleTaskQueue();
     UnloadModel(surface.GetStreamType());
   }
 
   // Verify the first refresh was scheduled.
   EXPECT_EQ(base::TimeDelta::FromSeconds(12),
-            refresh_scheduler_.scheduled_run_times[GetRefreshTaskId()]);
+            refresh_scheduler_
+                .scheduled_run_times[RefreshTaskId::kRefreshForYouFeed]);
 
   // Simulate executing the background task.
   refresh_scheduler_.Clear();
   task_environment_.AdvanceClock(base::TimeDelta::FromSeconds(12));
-  stream_->ExecuteRefreshTask(GetRefreshTaskId());
+  stream_->ExecuteRefreshTask(RefreshTaskId::kRefreshForYouFeed);
   WaitForIdleTaskQueue();
 
   // Verify |RefreshTaskComplete()| was called and next refresh was scheduled.
-  EXPECT_TRUE(refresh_scheduler_.completed_tasks.count(GetRefreshTaskId()));
+  EXPECT_TRUE(refresh_scheduler_.completed_tasks.count(
+      RefreshTaskId::kRefreshForYouFeed));
   EXPECT_EQ(base::TimeDelta::FromSeconds(48 - 12),
-            refresh_scheduler_.scheduled_run_times[GetRefreshTaskId()]);
+            refresh_scheduler_
+                .scheduled_run_times[RefreshTaskId::kRefreshForYouFeed]);
 
   // Simulate executing the background task again.
   refresh_scheduler_.Clear();
   task_environment_.AdvanceClock(base::TimeDelta::FromSeconds(48 - 12));
-  stream_->ExecuteRefreshTask(GetRefreshTaskId());
+  stream_->ExecuteRefreshTask(RefreshTaskId::kRefreshForYouFeed);
   WaitForIdleTaskQueue();
 
   // Verify |RefreshTaskComplete()| was called and next refresh was scheduled.
-  EXPECT_TRUE(refresh_scheduler_.completed_tasks.count(GetRefreshTaskId()));
+  EXPECT_TRUE(refresh_scheduler_.completed_tasks.count(
+      RefreshTaskId::kRefreshForYouFeed));
   EXPECT_EQ(GetFeedConfig().default_background_refresh_interval,
-            refresh_scheduler_.scheduled_run_times[GetRefreshTaskId()]);
+            refresh_scheduler_
+                .scheduled_run_times[RefreshTaskId::kRefreshForYouFeed]);
 }
 
 TEST_F(FeedApiTest, ForceRefreshIfMissedScheduledRefresh) {
diff --git a/components/feed/core/v2/public/stream_type.cc b/components/feed/core/v2/public/stream_type.cc
index 5d4f94c..077d5e7 100644
--- a/components/feed/core/v2/public/stream_type.cc
+++ b/components/feed/core/v2/public/stream_type.cc
@@ -35,8 +35,7 @@
       out_id = RefreshTaskId::kRefreshForYouFeed;
       return true;
     case Type::kWebFeed:
-      out_id = RefreshTaskId::kRefreshWebFeed;
-      return true;
+      return false;
   }
 }
 
diff --git a/components/feed/core/v2/public/types.h b/components/feed/core/v2/public/types.h
index 2884ad3..6b23e41 100644
--- a/components/feed/core/v2/public/types.h
+++ b/components/feed/core/v2/public/types.h
@@ -21,6 +21,8 @@
 
 enum class RefreshTaskId {
   kRefreshForYouFeed,
+  // TODO(1152592): Refresh is not currently used for the Web Feed. Remove this
+  // code if we don't need it.
   kRefreshWebFeed,
 };
 
diff --git a/components/history_clusters/core/memories.mojom b/components/history_clusters/core/memories.mojom
index bc039db4..b6dfa80 100644
--- a/components/history_clusters/core/memories.mojom
+++ b/components/history_clusters/core/memories.mojom
@@ -38,7 +38,10 @@
   array<WebPage> pages;
 };
 
-// Represents a visit to a webpage.
+// Represents the most recent visit to a URL in a Memory. Those visits in a
+// Memory for which there is a more recent visit to the same URL are omitted.
+// However, the time of the least recent visit as well as the number of those
+// visits are preserved for use in the UI as well as deletion.
 struct Visit {
   // Visit identifier. See //components/history/core/browser/history_types.h
   int64 id;
@@ -50,6 +53,9 @@
   url.mojom.Url? thumbnail_url;
   // Time of the visit.
   mojo_base.mojom.Time time;
+  // Time of the least recent visit in the Memory to the same URL. Equals |time|
+  // if there is only one visit in the Memory to the URL.
+  mojo_base.mojom.Time first_visit_time;
   // Localized string of approximate time of visit, e.g., "2 days ago".
   string relative_date;
   // Time of day of visit, e.g., "3:07 PM".
diff --git a/components/history_clusters/core/memories_remote_model_helper.cc b/components/history_clusters/core/memories_remote_model_helper.cc
index 9b098c20..d51186e 100644
--- a/components/history_clusters/core/memories_remote_model_helper.cc
+++ b/components/history_clusters/core/memories_remote_model_helper.cc
@@ -122,6 +122,8 @@
       memory->top_visits.push_back(CreateVisitMojom(visits, visit_id));
     }
 
+    // TODO(crbug.com/1179069): fill out the remaining Memories mojom fields.
+
     if (debug_logger) {
       base::DictionaryValue debug_cluster;
       debug_cluster.SetStringKey("id", memory->id.ToString());
@@ -141,13 +143,6 @@
       debug_clusters_list.Append(std::move(debug_cluster));
     }
 
-    // TODO(manukh) fill out:
-    //  |related_searches|
-    //  |related_tab_groups|
-    //  |bookmarks|
-    //  |last_visit_time|
-    //  |engagement_score|
-
     result.emplace_back(std::move(memory));
   }
 
diff --git a/components/history_clusters/core/memories_service.cc b/components/history_clusters/core/memories_service.cc
index a64df48..eb40ff6 100644
--- a/components/history_clusters/core/memories_service.cc
+++ b/components/history_clusters/core/memories_service.cc
@@ -160,4 +160,12 @@
     std::move(on_visits_callback).Run(visits_);
 }
 
+void MemoriesService::RemoveVisits(
+    const std::vector<history::ExpireHistoryArgs>& expire_list,
+    base::OnceClosure closure,
+    base::CancelableTaskTracker* task_tracker) {
+  std::move(closure).Run();
+  // TODO(crbug.com/1203789): Remove the visits from relevant history tables.
+}
+
 }  // namespace history_clusters
diff --git a/components/history_clusters/core/memories_service.h b/components/history_clusters/core/memories_service.h
index ff054535..39fa01e 100644
--- a/components/history_clusters/core/memories_service.h
+++ b/components/history_clusters/core/memories_service.h
@@ -76,6 +76,11 @@
       base::OnceCallback<void(mojom::QueryParamsPtr, Memories)>;
   void QueryMemories(mojom::QueryParamsPtr query_params,
                      QueryMemoriesCallback callback);
+  // Removes all visits to the specified URLs in the specified time ranges in
+  // |expire_list|. Calls |closure| when done.
+  void RemoveVisits(const std::vector<history::ExpireHistoryArgs>& expire_list,
+                    base::OnceClosure closure,
+                    base::CancelableTaskTracker* task_tracker);
 
  private:
   friend class MemoriesServiceTestApi;
diff --git a/components/optimization_guide/content/browser/optimization_target_model_executor.h b/components/optimization_guide/content/browser/optimization_target_model_executor.h
index aa0e352e..143813cc 100644
--- a/components/optimization_guide/content/browser/optimization_target_model_executor.h
+++ b/components/optimization_guide/content/browser/optimization_target_model_executor.h
@@ -130,7 +130,7 @@
     if (optimization_target_ != optimization_target)
       return;
 
-    model_metadata_to_load_ = model_metadata;
+    supported_features_for_loaded_model_ = model_metadata;
     file_path_to_load_ = file_path;
 
     if (features::LoadModelFileForEachExecution()) {
@@ -181,7 +181,6 @@
     DCHECK(model_execution_task_runner_->RunsTasksInCurrentSequence());
     loaded_model_.reset();
     model_fb_.reset();
-    supported_features_for_loaded_model_ = base::nullopt;
   }
 
   // Callback invoked when a model file for |optimization_target| has been
@@ -209,8 +208,6 @@
       return false;
     model_fb_ = std::move(model_fb);
 
-    supported_features_for_loaded_model_ = model_metadata_to_load_;
-
     loaded_model_ = BuildModelExecutionTask(model_fb_.get());
     if (loaded_model_) {
       scoped_model_loading_recorder.set_model_loading_state(
@@ -302,18 +299,12 @@
 
   scoped_refptr<base::SequencedTaskRunner> model_execution_task_runner_;
 
-  // When the model file is updated, the server may pass this metadata that
-  // accompanies the model. This can be nullopt even after a model file update
-  // occurs since.
-  base::Optional<proto::Any> model_metadata_to_load_;
-
   // The model file path to be loaded. May be nullopt if no model has been
   // downloaded yet.
   base::Optional<base::FilePath> file_path_to_load_;
 
-  // Note on lifetimes: |model_fb_|, |loaded_model_|, and
-  // |supported_features_for_loaded_model_| all share the same lifetime, being
-  // set in |LoadModelFile()| and being destroyed in |ResetModelFile()|.
+  // Note on lifetimes: |model_fb_| and |loaded_model_| share the same lifetime,
+  // being set in |LoadModelFile()| and being destroyed in |ResetModelFile()|.
 
   // This will only be non-null when |file_path_to_load_| is set, and while the
   // model is loaded which is manged by a feature flag. See also the above note
@@ -324,7 +315,9 @@
   // See also the above note regarding lifetime.
   std::unique_ptr<ModelExecutionTask> loaded_model_;
 
-  // See the above note regarding lifetime.
+  // When the model file is updated, the server may pass this metadata that
+  // accompanies the model. This can be nullopt even after a model file update
+  // occurs.
   base::Optional<proto::Any> supported_features_for_loaded_model_;
 
   base::WeakPtrFactory<OptimizationTargetModelExecutor> weak_ptr_factory_{this};
diff --git a/components/optimization_guide/content/browser/optimization_target_model_executor_unittest.cc b/components/optimization_guide/content/browser/optimization_target_model_executor_unittest.cc
index 5e3e2145..9f21c560 100644
--- a/components/optimization_guide/content/browser/optimization_target_model_executor_unittest.cc
+++ b/components/optimization_guide/content/browser/optimization_target_model_executor_unittest.cc
@@ -439,9 +439,12 @@
       proto::OptimizationTarget::OPTIMIZATION_TARGET_PAINFUL_PAGE_LOAD,
       any_metadata);
 
+  // While the model isn't actually loaded yet, the supported features are
+  // already known and do not change when the model is loaded or unloaded.
+  EXPECT_TRUE(model_executor()->supported_features_for_loaded_model());
+
   // Model shouldn't be loaded until there is something to execute.
   EXPECT_FALSE(model_executor()->HasLoadedModel());
-  EXPECT_FALSE(model_executor()->supported_features_for_loaded_model());
 
   std::vector<float> input;
   size_t expected_dims = 1 * 32 * 32 * 3;
@@ -463,10 +466,11 @@
       input);
   run_loop->Run();
 
-  // After execution, the model should be unloaded in a PostTask.
+  // After execution, the model should be unloaded in a PostTask, but the
+  // metadata should still be available.
   RunUntilIdle();
   EXPECT_FALSE(model_executor()->HasLoadedModel());
-  EXPECT_FALSE(model_executor()->supported_features_for_loaded_model());
+  EXPECT_TRUE(model_executor()->supported_features_for_loaded_model());
 
   histogram_tester.ExpectTotalCount(
       "OptimizationGuide.ModelExecutor.TaskSchedulingLatency." +
diff --git a/components/policy/DIR_METADATA b/components/policy/DIR_METADATA
index ccb8f65..04f1804 100644
--- a/components/policy/DIR_METADATA
+++ b/components/policy/DIR_METADATA
@@ -1,5 +1,5 @@
 monorail {
-  component: "Enterprise>CloudPolicy"
+  component: "Enterprise"
 }
 
-team_email: "chromium-reviews@chromium.org"
+team_email: "chromium-enterprise@chromium.org"
diff --git a/components/policy/android/java/src/org/chromium/components/policy/PolicyCache.java b/components/policy/android/java/src/org/chromium/components/policy/PolicyCache.java
index 26f3e6d..fc1ae67c 100644
--- a/components/policy/android/java/src/org/chromium/components/policy/PolicyCache.java
+++ b/components/policy/android/java/src/org/chromium/components/policy/PolicyCache.java
@@ -91,8 +91,10 @@
     public Integer getIntValue(String policy) {
         SharedPreferences sharedPreferences = getSharedPreferences();
         if (sharedPreferences == null) return null;
-        if (!sharedPreferences.contains(policy)) return null;
-        return sharedPreferences.getInt(policy, 0);
+        try (StrictModeContext ignored = StrictModeContext.allowDiskReads()) {
+            if (!sharedPreferences.contains(policy)) return null;
+            return sharedPreferences.getInt(policy, 0);
+        }
     }
 
     /**
@@ -103,8 +105,10 @@
     public Boolean getBooleanValue(String policy) {
         SharedPreferences sharedPreferences = getSharedPreferences();
         if (sharedPreferences == null) return null;
-        if (!sharedPreferences.contains(policy)) return null;
-        return sharedPreferences.getBoolean(policy, false);
+        try (StrictModeContext ignored = StrictModeContext.allowDiskReads()) {
+            if (!sharedPreferences.contains(policy)) return null;
+            return sharedPreferences.getBoolean(policy, false);
+        }
     }
 
     /**
@@ -115,8 +119,10 @@
     public String getStringValue(String policy) {
         SharedPreferences sharedPreferences = getSharedPreferences();
         if (sharedPreferences == null) return null;
-        if (!sharedPreferences.contains(policy)) return null;
-        return sharedPreferences.getString(policy, null);
+        try (StrictModeContext ignored = StrictModeContext.allowDiskReads()) {
+            if (!sharedPreferences.contains(policy)) return null;
+            return sharedPreferences.getString(policy, null);
+        }
     }
 
     /**
@@ -127,24 +133,17 @@
     public JSONArray getListValue(String policy) {
         SharedPreferences sharedPreferences = getSharedPreferences();
         if (sharedPreferences == null) return null;
-        if (!sharedPreferences.contains(policy)) return null;
-        try {
-            return new JSONArray(sharedPreferences.getString(policy, null));
-        } catch (JSONException e) {
-            return null;
+        try (StrictModeContext ignored = StrictModeContext.allowDiskReads()) {
+            if (!sharedPreferences.contains(policy)) return null;
+            try {
+                return new JSONArray(sharedPreferences.getString(policy, null));
+            } catch (JSONException e) {
+                return null;
+            }
         }
     }
 
     /**
-     * @return ALl cached policies.
-     */
-    public Map<String, ?> getAllPolicies() {
-        SharedPreferences sharedPreferences = getSharedPreferences();
-        if (sharedPreferences == null) return null;
-        return sharedPreferences.getAll();
-    }
-
-    /**
      * @param policy The name of policy.
      * @return The value of cached dictionary policy, null if there is no valid
      * cached policy.
@@ -152,11 +151,24 @@
     public JSONObject getDictValue(String policy) {
         SharedPreferences sharedPreferences = getSharedPreferences();
         if (sharedPreferences == null) return null;
-        if (!sharedPreferences.contains(policy)) return null;
-        try {
-            return new JSONObject(sharedPreferences.getString(policy, null));
-        } catch (JSONException e) {
-            return null;
+        try (StrictModeContext ignored = StrictModeContext.allowDiskReads()) {
+            if (!sharedPreferences.contains(policy)) return null;
+            try {
+                return new JSONObject(sharedPreferences.getString(policy, null));
+            } catch (JSONException e) {
+                return null;
+            }
+        }
+    }
+
+    /**
+     * @return All cached policies.
+     */
+    public Map<String, ?> getAllPolicies() {
+        SharedPreferences sharedPreferences = getSharedPreferences();
+        if (sharedPreferences == null) return null;
+        try (StrictModeContext ignored = StrictModeContext.allowDiskReads()) {
+            return sharedPreferences.getAll();
         }
     }
 
diff --git a/components/policy/resources/policy_templates.json b/components/policy/resources/policy_templates.json
index cbf43c3..8efdee99 100644
--- a/components/policy/resources/policy_templates.json
+++ b/components/policy/resources/policy_templates.json
@@ -11233,7 +11233,7 @@
     },
     {
       'name': 'BuiltInDnsClientEnabled',
-      'owners': ['szym@chromium.org', 'pmarko@chromium.org'],
+      'owners': ['ericorth@chromium.org', 'pmarko@chromium.org'],
       'type': 'main',
       'schema': { 'type': 'boolean' },
       'supported_on': [
@@ -11259,7 +11259,7 @@
     },
     {
       'name': 'DnsOverHttpsMode',
-      'owners': ['dalyk@chromium.org', 'ericorth@chromium.org', 'bingler@chromium.org'],
+      'owners': ['ericorth@chromium.org', 'bingler@chromium.org'],
       'type': 'string-enum',
       'schema': {
         'type': 'string',
@@ -11314,7 +11314,7 @@
     },
     {
       'name': 'DnsOverHttpsTemplates',
-      'owners': ['dalyk@chromium.org', 'ericorth@chromium.org', 'bingler@chromium.org'],
+      'owners': ['ericorth@chromium.org', 'bingler@chromium.org'],
       'type': 'string',
       'schema': { 'type': 'string' },
       'supported_on': [
@@ -11341,6 +11341,43 @@
       Incorrectly formatted templates will be ignored.''',
     },
     {
+      'name': 'AdditionalDnsQueryTypesEnabled',
+      'owners': ['ericorth@chromium.org', 'file://net/OWNERS'],
+      'type': 'main',
+      'schema': { 'type': 'boolean' },
+      'future_on': [
+        'android',
+        'chrome.*',
+        'chrome_os',
+      ],
+      'features': {
+        'dynamic_refresh': True,
+        'per_profile': False,
+      },
+      'items': [
+        {
+          'value': True,
+          'caption': 'Allow additional DNS query types',
+        },
+        {
+          'value': False,
+          'caption': 'Prevent additional DNS query types',
+        },
+      ],
+      'example_value': True,
+      'default': True,
+      'id': 857,
+      'caption': '''Allow DNS queries for additional DNS record types''',
+      'tags': [],
+      'desc': '''This policy controls whether <ph name="PRODUCT_NAME">$1<ex>Google Chrome</ex></ph> may query additional DNS record types when making insecure DNS requests. This policy has no effect on DNS queries made via Secure DNS, which may always query additional DNS types.
+
+      If this policy is unset or set to Enabled, additional types such as <ph name="DNS_TYPE_HTTPS">HTTPS</ph> (DNS type 65) may be queried in addition to <ph name="DNS_TYPE_A">A</ph> (DNS type 1) and <ph name="DNS_TYPE_AAAA">AAAA</ph> (DNS type 28).
+
+      If this policy is set to Disabled, DNS will only be queried for <ph name="DNS_TYPE_A">A</ph> (DNS type 1) and/or <ph name="DNS_TYPE_AAAA">AAAA</ph> (DNS type 28).
+
+      This policy is a temporary measure and will be removed in future versions of <ph name="PRODUCT_NAME">$1<ex>Google Chrome</ex></ph>. After removal of the policy, <ph name="PRODUCT_NAME">$1<ex>Google Chrome</ex></ph> will always be able to query additional DNS types.''',
+    },
+    {
       'name': 'ShelfAutoHideBehavior',
       'owners': ['file://components/policy/resources/OWNERS', 'bartfab@chromium.org'],
       'type': 'string-enum',
@@ -26718,6 +26755,6 @@
   'placeholders': [],
   'deleted_policy_ids': [114, 115, 204, 205, 206, 412, 476, 544, 546, 562, 569, 578, 583, 585, 586, 587, 588, 589, 590, 591, 600, 668, 669],
   'deleted_atomic_policy_group_ids': [19],
-  'highest_id_currently_used': 856,
+  'highest_id_currently_used': 857,
   'highest_atomic_group_id_currently_used': 40
 }
diff --git a/components/remote_cocoa/app_shim/native_widget_mac_nswindow.mm b/components/remote_cocoa/app_shim/native_widget_mac_nswindow.mm
index a071d1f..ca3d6a662 100644
--- a/components/remote_cocoa/app_shim/native_widget_mac_nswindow.mm
+++ b/components/remote_cocoa/app_shim/native_widget_mac_nswindow.mm
@@ -117,7 +117,10 @@
   if (_isEnforcingNeverMadeVisible)
     return;
   _isEnforcingNeverMadeVisible = YES;
-  [self addObserver:self forKeyPath:@"visible" options:0 context:nil];
+  [self addObserver:self
+         forKeyPath:@"visible"
+            options:NSKeyValueObservingOptionNew
+            context:nil];
 }
 
 - (void)observeValueForKeyPath:(NSString*)keyPath
@@ -128,7 +131,8 @@
   DCHECK([keyPath isEqual:@"visible"]);
   DCHECK_EQ(object, self);
   DCHECK_EQ(context, nil);
-  base::debug::DumpWithoutCrashing();
+  if ([[change objectForKey:NSKeyValueChangeNewKey] boolValue])
+    base::debug::DumpWithoutCrashing();
 }
 
 // Public methods.
diff --git a/components/services/storage/service_worker/service_worker_storage.cc b/components/services/storage/service_worker/service_worker_storage.cc
index 1800b0af..71cb6f2 100644
--- a/components/services/storage/service_worker/service_worker_storage.cc
+++ b/components/services/storage/service_worker/service_worker_storage.cc
@@ -1306,7 +1306,7 @@
     next_resource_id_ = data->next_resource_id;
     registered_keys_.swap(data->keys);
     state_ = STORAGE_STATE_INITIALIZED;
-    base::UmaHistogramCounts1M("ServiceWorker.RegisteredOriginCount",
+    base::UmaHistogramCounts1M("ServiceWorker.RegisteredStorageKeyCount",
                                registered_keys_.size());
   } else {
     DVLOG(2) << "Failed to initialize: "
diff --git a/components/sync/driver/sync_driver_switches.cc b/components/sync/driver/sync_driver_switches.cc
index 59f86d9..4e7a184 100644
--- a/components/sync/driver/sync_driver_switches.cc
+++ b/components/sync/driver/sync_driver_switches.cc
@@ -58,25 +58,6 @@
 const base::Feature kDecoupleSyncFromAndroidMasterSync{
     "DecoupleSyncFromAndroidMasterSync", base::FEATURE_ENABLED_BY_DEFAULT};
 
-// Allows trusted vault implementation to follow key rotation (including device
-// registration).
-const base::Feature kFollowTrustedVaultKeyRotation{
-    "FollowTrustedVaultKeyRotation", base::FEATURE_DISABLED_BY_DEFAULT};
-
-// Allows device registration within trusted vault server without having trusted
-// vault key. Effectively disabled if kFollowTrustedVaultKeyRotation is
-// disabled.
-const base::Feature kAllowSilentTrustedVaultDeviceRegistration{
-    "AllowSilentTrustedVaultDeviceRegistration",
-    base::FEATURE_ENABLED_BY_DEFAULT};
-
-// Specifies how long requests to vault service shouldn't be retried after
-// encountering transient error.
-const base::FeatureParam<base::TimeDelta>
-    kTrustedVaultServiceThrottlingDuration{
-        &kFollowTrustedVaultKeyRotation,
-        "TrustedVaultServiceThrottlingDuration", base::TimeDelta::FromDays(1)};
-
 // Sync requires policies to be loaded before starting.
 const base::Feature kSyncRequiresPoliciesLoaded{
     "SyncRequiresPoliciesLoaded", base::FEATURE_DISABLED_BY_DEFAULT};
diff --git a/components/sync/driver/sync_driver_switches.h b/components/sync/driver/sync_driver_switches.h
index 7613f51c..f746e57 100644
--- a/components/sync/driver/sync_driver_switches.h
+++ b/components/sync/driver/sync_driver_switches.h
@@ -31,10 +31,6 @@
 extern const base::Feature kSyncAutofillWalletOfferData;
 extern const base::Feature kSyncWifiConfigurations;
 extern const base::Feature kDecoupleSyncFromAndroidMasterSync;
-extern const base::Feature kFollowTrustedVaultKeyRotation;
-extern const base::Feature kAllowSilentTrustedVaultDeviceRegistration;
-extern const base::FeatureParam<base::TimeDelta>
-    kTrustedVaultServiceThrottlingDuration;
 
 extern const base::Feature kSyncRequiresPoliciesLoaded;
 extern const base::FeatureParam<base::TimeDelta> kSyncPolicyLoadTimeout;
diff --git a/components/sync/trusted_vault/BUILD.gn b/components/sync/trusted_vault/BUILD.gn
index 854dbfa..d2914d7ec 100644
--- a/components/sync/trusted_vault/BUILD.gn
+++ b/components/sync/trusted_vault/BUILD.gn
@@ -29,6 +29,8 @@
     "trusted_vault_request.h",
     "trusted_vault_server_constants.cc",
     "trusted_vault_server_constants.h",
+    "trusted_vault_switches.cc",
+    "trusted_vault_switches.h",
   ]
   public_deps = [
     "//base",
diff --git a/components/sync/trusted_vault/standalone_trusted_vault_backend.cc b/components/sync/trusted_vault/standalone_trusted_vault_backend.cc
index decf97e..96edf9d 100644
--- a/components/sync/trusted_vault/standalone_trusted_vault_backend.cc
+++ b/components/sync/trusted_vault/standalone_trusted_vault_backend.cc
@@ -17,9 +17,9 @@
 #include "base/time/time.h"
 #include "components/os_crypt/os_crypt.h"
 #include "components/sync/base/time.h"
-#include "components/sync/driver/sync_driver_switches.h"
 #include "components/sync/trusted_vault/proto_string_bytes_conversion.h"
 #include "components/sync/trusted_vault/securebox.h"
+#include "components/sync/trusted_vault/trusted_vault_switches.h"
 
 namespace syncer {
 
diff --git a/components/sync/trusted_vault/standalone_trusted_vault_backend_unittest.cc b/components/sync/trusted_vault/standalone_trusted_vault_backend_unittest.cc
index 32ffa73..5b060635 100644
--- a/components/sync/trusted_vault/standalone_trusted_vault_backend_unittest.cc
+++ b/components/sync/trusted_vault/standalone_trusted_vault_backend_unittest.cc
@@ -20,6 +20,7 @@
 #include "components/sync/driver/sync_driver_switches.h"
 #include "components/sync/trusted_vault/securebox.h"
 #include "components/sync/trusted_vault/trusted_vault_connection.h"
+#include "components/sync/trusted_vault/trusted_vault_switches.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
@@ -97,9 +98,6 @@
       : file_path_(
             CreateUniqueTempDir(&temp_dir_)
                 .Append(base::FilePath(FILE_PATH_LITERAL("some_file")))) {
-    override_features.InitAndEnableFeature(
-        switches::kFollowTrustedVaultKeyRotation);
-
     auto delegate = std::make_unique<testing::NiceMock<MockDelegate>>();
     delegate_ = delegate.get();
 
@@ -177,8 +175,6 @@
   }
 
  private:
-  base::test::ScopedFeatureList override_features;
-
   base::ScopedTempDir temp_dir_;
   const base::FilePath file_path_;
   testing::NiceMock<MockDelegate>* delegate_;
diff --git a/components/sync/trusted_vault/standalone_trusted_vault_client.cc b/components/sync/trusted_vault/standalone_trusted_vault_client.cc
index cd3899b7..b3160ce 100644
--- a/components/sync/trusted_vault/standalone_trusted_vault_client.cc
+++ b/components/sync/trusted_vault/standalone_trusted_vault_client.cc
@@ -142,7 +142,8 @@
   std::unique_ptr<TrustedVaultConnection> connection;
   GURL trusted_vault_service_gurl =
       ExtractTrustedVaultServiceURLFromCommandLine();
-  if (base::FeatureList::IsEnabled(switches::kFollowTrustedVaultKeyRotation) &&
+  if (base::FeatureList::IsEnabled(
+          switches::kSyncSupportTrustedVaultPassphraseRecovery) &&
       trusted_vault_service_gurl.is_valid()) {
     connection = std::make_unique<TrustedVaultConnectionImpl>(
         trusted_vault_service_gurl, url_loader_factory->Clone(),
diff --git a/components/sync/trusted_vault/trusted_vault_switches.cc b/components/sync/trusted_vault/trusted_vault_switches.cc
new file mode 100644
index 0000000..7b22556
--- /dev/null
+++ b/components/sync/trusted_vault/trusted_vault_switches.cc
@@ -0,0 +1,25 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/sync/trusted_vault/trusted_vault_switches.h"
+
+#include "components/sync/driver/sync_driver_switches.h"
+
+namespace switches {
+
+// Allows device registration within trusted vault server without having trusted
+// vault key. Effectively disabled if kSyncSupportTrustedVaultPassphraseRecovery
+// is disabled.
+const base::Feature kAllowSilentTrustedVaultDeviceRegistration{
+    "AllowSilentTrustedVaultDeviceRegistration",
+    base::FEATURE_ENABLED_BY_DEFAULT};
+
+// Specifies how long requests to vault service shouldn't be retried after
+// encountering transient error.
+const base::FeatureParam<base::TimeDelta>
+    kTrustedVaultServiceThrottlingDuration{
+        &kSyncSupportTrustedVaultPassphraseRecovery,
+        "TrustedVaultServiceThrottlingDuration", base::TimeDelta::FromDays(1)};
+
+}  // namespace switches
diff --git a/components/sync/trusted_vault/trusted_vault_switches.h b/components/sync/trusted_vault/trusted_vault_switches.h
new file mode 100644
index 0000000..6599ebb6
--- /dev/null
+++ b/components/sync/trusted_vault/trusted_vault_switches.h
@@ -0,0 +1,19 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_SYNC_TRUSTED_VAULT_TRUSTED_VAULT_SWITCHES_H_
+#define COMPONENTS_SYNC_TRUSTED_VAULT_TRUSTED_VAULT_SWITCHES_H_
+
+#include "base/feature_list.h"
+#include "base/metrics/field_trial_params.h"
+
+namespace switches {
+
+extern const base::Feature kAllowSilentTrustedVaultDeviceRegistration;
+extern const base::FeatureParam<base::TimeDelta>
+    kTrustedVaultServiceThrottlingDuration;
+
+}  // namespace switches
+
+#endif  // COMPONENTS_SYNC_TRUSTED_VAULT_TRUSTED_VAULT_SWITCHES_H_
diff --git a/components/viz/host/gpu_client.cc b/components/viz/host/gpu_client.cc
index 8bef6bf..8104da38 100644
--- a/components/viz/host/gpu_client.cc
+++ b/components/viz/host/gpu_client.cc
@@ -25,41 +25,16 @@
   return bytes.IsValid();
 }
 
-#if BUILDFLAG(IS_CHROMEOS_ASH)
-void CreateJpegDecodeAcceleratorInternal(
-    mojo::PendingReceiver<chromeos_camera::mojom::MjpegDecodeAccelerator>
-        jda_receiver,
-    GpuClientDelegate* delegate) {
-  if (auto* gpu_host = delegate->EnsureGpuHost()) {
-    gpu_host->gpu_service()->CreateJpegDecodeAccelerator(
-        std::move(jda_receiver));
-  }
-}
-#endif  // BUILDFLAG(IS_CHROMEOS_ASH)
-
-void CreateVideoEncodeAcceleratorProviderInternal(
-    mojo::PendingReceiver<media::mojom::VideoEncodeAcceleratorProvider>
-        vea_provider_receiver,
-    GpuClientDelegate* delegate) {
-  if (auto* gpu_host = delegate->EnsureGpuHost()) {
-    gpu_host->gpu_service()->CreateVideoEncodeAcceleratorProvider(
-        std::move(vea_provider_receiver));
-  }
-}
-
 }  // namespace
 
 GpuClient::GpuClient(std::unique_ptr<GpuClientDelegate> delegate,
                      int client_id,
                      uint64_t client_tracing_id,
-                     bool gpu_host_lives_on_ui_thread,
-                     scoped_refptr<base::SingleThreadTaskRunner> io_task_runner)
+                     scoped_refptr<base::SingleThreadTaskRunner> task_runner)
     : delegate_(std::move(delegate)),
       client_id_(client_id),
       client_tracing_id_(client_tracing_id),
-      gpu_host_lives_on_ui_thread_(gpu_host_lives_on_ui_thread),
-      io_task_runner_(std::move(io_task_runner)),
-      ui_task_runner_(base::ThreadTaskRunnerHandle::Get()) {
+      task_runner_(std::move(task_runner)) {
   DCHECK(delegate_);
   gpu_receivers_.set_disconnect_handler(
       base::BindRepeating(&GpuClient::OnError, base::Unretained(this),
@@ -67,24 +42,18 @@
 }
 
 GpuClient::~GpuClient() {
-  DCHECK(io_task_runner_->RunsTasksInCurrentSequence());
+  DCHECK(task_runner_->RunsTasksInCurrentSequence());
   gpu_receivers_.Clear();
   OnError(ErrorReason::kInDestructor);
-
-  if (gpu_host_lives_on_ui_thread_) {
-    // This way it's safe to PostTask to the UI task runner and use
-    // |delegate_|.
-    ui_task_runner_->DeleteSoon(FROM_HERE, delegate_.release());
-  }
 }
 
 void GpuClient::Add(mojo::PendingReceiver<mojom::Gpu> receiver) {
-  DCHECK(io_task_runner_->RunsTasksInCurrentSequence());
+  DCHECK(task_runner_->RunsTasksInCurrentSequence());
   gpu_receivers_.Add(this, std::move(receiver));
 }
 
 void GpuClient::OnError(ErrorReason reason) {
-  DCHECK(io_task_runner_->RunsTasksInCurrentSequence());
+  DCHECK(task_runner_->RunsTasksInCurrentSequence());
   ClearCallback();
   if (gpu_receivers_.empty() && delegate_) {
     if (auto* gpu_memory_buffer_manager =
@@ -97,10 +66,10 @@
 }
 
 void GpuClient::PreEstablishGpuChannel() {
-  if (io_task_runner_->RunsTasksInCurrentSequence()) {
+  if (task_runner_->RunsTasksInCurrentSequence()) {
     EstablishGpuChannel(EstablishGpuChannelCallback());
   } else {
-    io_task_runner_->PostTask(
+    task_runner_->PostTask(
         FROM_HERE,
         base::BindOnce(&GpuClient::EstablishGpuChannel, base::Unretained(this),
                        EstablishGpuChannelCallback()));
@@ -160,7 +129,7 @@
 }
 
 void GpuClient::EstablishGpuChannel(EstablishGpuChannelCallback callback) {
-  DCHECK(io_task_runner_->RunsTasksInCurrentSequence());
+  DCHECK(task_runner_->RunsTasksInCurrentSequence());
   // At most one channel should be requested. So clear previous request first.
   ClearCallback();
 
@@ -177,16 +146,13 @@
     return;
   }
 
-  GpuHostImpl* gpu_host = nullptr;
-  if (!gpu_host_lives_on_ui_thread_) {
-    gpu_host = delegate_->EnsureGpuHost();
-    if (!gpu_host) {
-      if (callback) {
-        std::move(callback).Run(client_id_, mojo::ScopedMessagePipeHandle(),
-                                gpu::GPUInfo(), gpu::GpuFeatureInfo());
-      }
-      return;
+  GpuHostImpl* gpu_host = delegate_->EnsureGpuHost();
+  if (!gpu_host) {
+    if (callback) {
+      std::move(callback).Run(client_id_, mojo::ScopedMessagePipeHandle(),
+                              gpu::GPUInfo(), gpu::GpuFeatureInfo());
     }
+    return;
   }
 
   callback_ = std::move(callback);
@@ -194,31 +160,19 @@
     return;
   gpu_channel_requested_ = true;
   const bool is_gpu_host = false;
-  if (gpu_host_lives_on_ui_thread_) {
-    ui_task_runner_->PostTask(
-        FROM_HERE,
-        base::BindOnce(&GpuClient::EstablishGpuChannelOnUIThread,
-                       weak_factory_.GetWeakPtr(), io_task_runner_, client_id_,
-                       client_tracing_id_, is_gpu_host, delegate_.get()));
-  } else {
-    gpu_host->EstablishGpuChannel(
-        client_id_, client_tracing_id_, is_gpu_host, false,
-        base::BindOnce(&GpuClient::OnEstablishGpuChannel,
-                       weak_factory_.GetWeakPtr()));
-  }
+  gpu_host->EstablishGpuChannel(
+      client_id_, client_tracing_id_, is_gpu_host, false,
+      base::BindOnce(&GpuClient::OnEstablishGpuChannel,
+                     weak_factory_.GetWeakPtr()));
 }
 
 #if BUILDFLAG(IS_CHROMEOS_ASH)
 void GpuClient::CreateJpegDecodeAccelerator(
     mojo::PendingReceiver<chromeos_camera::mojom::MjpegDecodeAccelerator>
         jda_receiver) {
-  if (gpu_host_lives_on_ui_thread_) {
-    ui_task_runner_->PostTask(
-        FROM_HERE, base::BindOnce(&CreateJpegDecodeAcceleratorInternal,
-                                  std::move(jda_receiver), delegate_.get()));
-  } else {
-    CreateJpegDecodeAcceleratorInternal(std::move(jda_receiver),
-                                        delegate_.get());
+  if (auto* gpu_host = delegate_->EnsureGpuHost()) {
+    gpu_host->gpu_service()->CreateJpegDecodeAccelerator(
+        std::move(jda_receiver));
   }
 }
 #endif  // BUILDFLAG(IS_CHROMEOS_ASH)
@@ -226,14 +180,9 @@
 void GpuClient::CreateVideoEncodeAcceleratorProvider(
     mojo::PendingReceiver<media::mojom::VideoEncodeAcceleratorProvider>
         vea_provider_receiver) {
-  if (gpu_host_lives_on_ui_thread_) {
-    ui_task_runner_->PostTask(
-        FROM_HERE,
-        base::BindOnce(&CreateVideoEncodeAcceleratorProviderInternal,
-                       std::move(vea_provider_receiver), delegate_.get()));
-  } else {
-    CreateVideoEncodeAcceleratorProviderInternal(
-        std::move(vea_provider_receiver), delegate_.get());
+  if (auto* gpu_host = delegate_->EnsureGpuHost()) {
+    gpu_host->gpu_service()->CreateVideoEncodeAcceleratorProvider(
+        std::move(vea_provider_receiver));
   }
 }
 
@@ -285,39 +234,4 @@
   gpu_memory_buffer_factory_receivers_.Add(this, std::move(receiver));
 }
 
-void GpuClient::EstablishGpuChannelOnUIThread(
-    const base::WeakPtr<GpuClient>& weak_ref,
-    scoped_refptr<base::SingleThreadTaskRunner> task_runner,
-    int client_id,
-    uint64_t client_tracing_id,
-    bool is_gpu_host,
-    GpuClientDelegate* delegate) {
-  GpuHostImpl* gpu_host = delegate->EnsureGpuHost();
-  if (!gpu_host) {
-    OnEstablishedGpuChannelOnUIThread(
-        weak_ref, task_runner, mojo::ScopedMessagePipeHandle(), gpu::GPUInfo(),
-        gpu::GpuFeatureInfo(),
-        GpuHostImpl::EstablishChannelStatus::kGpuAccessDenied);
-    return;
-  }
-
-  gpu_host->EstablishGpuChannel(
-      client_id, client_tracing_id, is_gpu_host, false,
-      base::BindOnce(&GpuClient::OnEstablishedGpuChannelOnUIThread, weak_ref,
-                     task_runner));
-}
-
-void GpuClient::OnEstablishedGpuChannelOnUIThread(
-    const base::WeakPtr<GpuClient>& weak_ref,
-    scoped_refptr<base::SingleThreadTaskRunner> task_runner,
-    mojo::ScopedMessagePipeHandle channel_handle,
-    const gpu::GPUInfo& gpu_info,
-    const gpu::GpuFeatureInfo& gpu_feature_info,
-    GpuHostImpl::EstablishChannelStatus status) {
-  task_runner->PostTask(
-      FROM_HERE, base::BindOnce(&GpuClient::OnEstablishGpuChannel, weak_ref,
-                                std::move(channel_handle), gpu_info,
-                                gpu_feature_info, status));
-}
-
 }  // namespace viz
diff --git a/components/viz/host/gpu_client.h b/components/viz/host/gpu_client.h
index 4a4ba16..108b13f 100644
--- a/components/viz/host/gpu_client.h
+++ b/components/viz/host/gpu_client.h
@@ -25,20 +25,15 @@
   using ConnectionErrorHandlerClosure =
       base::OnceCallback<void(GpuClient* client)>;
 
-  // GpuClient must be destroyed on the thread associated with |io_task_runner|.
-  // If |gpu_host_lives_on_ui_thread| is true, then
-  // delegate->EnsureGpuHost() can only be called on the thread that GpuClient
-  // is constructed on. Regardless, GpuClient lives on the IO thread and
-  // always receives incoming Mojo calls on the IO thread.
+  // GpuClient must be destroyed on the thread associated with |task_runner|.
   GpuClient(std::unique_ptr<GpuClientDelegate> delegate,
             int client_id,
             uint64_t client_tracing_id,
-            bool gpu_host_lives_on_ui_thread,
-            scoped_refptr<base::SingleThreadTaskRunner> io_task_runner);
+            scoped_refptr<base::SingleThreadTaskRunner> task_runner);
 
   ~GpuClient() override;
 
-  // This needs to be run on the thread associated with |io_task_runner_|.
+  // This needs to be run on the thread associated with |task_runner_|.
   void Add(mojo::PendingReceiver<mojom::Gpu> receiver);
 
   void PreEstablishGpuChannel();
@@ -91,25 +86,9 @@
                                gfx::GpuMemoryBufferHandle handle);
   void ClearCallback();
 
-  static void EstablishGpuChannelOnUIThread(
-      const base::WeakPtr<GpuClient>& weak_ref,
-      scoped_refptr<base::SingleThreadTaskRunner> task_runner,
-      int client_id,
-      uint64_t client_tracing_id,
-      bool is_gpu_host,
-      GpuClientDelegate* delegate);
-  static void OnEstablishedGpuChannelOnUIThread(
-      const base::WeakPtr<GpuClient>& weak_ref,
-      scoped_refptr<base::SingleThreadTaskRunner> task_runner,
-      mojo::ScopedMessagePipeHandle channel_handle,
-      const gpu::GPUInfo& gpu_info,
-      const gpu::GpuFeatureInfo& gpu_feature_info,
-      GpuHostImpl::EstablishChannelStatus status);
-
   std::unique_ptr<GpuClientDelegate> delegate_;
   const int client_id_;
   const uint64_t client_tracing_id_;
-  const bool gpu_host_lives_on_ui_thread_;
   mojo::ReceiverSet<mojom::GpuMemoryBufferFactory>
       gpu_memory_buffer_factory_receivers_;
   mojo::ReceiverSet<mojom::Gpu> gpu_receivers_;
@@ -119,11 +98,10 @@
   gpu::GPUInfo gpu_info_;
   gpu::GpuFeatureInfo gpu_feature_info_;
   ConnectionErrorHandlerClosure connection_error_handler_;
-  // |io_task_runner_| is associated with the thread |gpu_bindings_| is bound
+  // |task_runner_| is associated with the thread |gpu_bindings_| is bound
   // on. GpuClient instance is bound to this thread, and must be destroyed on
   // this thread.
-  scoped_refptr<base::SingleThreadTaskRunner> io_task_runner_;
-  scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner_;
+  scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
   base::WeakPtrFactory<GpuClient> weak_factory_{this};
 
   DISALLOW_COPY_AND_ASSIGN(GpuClient);
diff --git a/components/viz/host/gpu_client_delegate.h b/components/viz/host/gpu_client_delegate.h
index 90398a9d..c12d4ef 100644
--- a/components/viz/host/gpu_client_delegate.h
+++ b/components/viz/host/gpu_client_delegate.h
@@ -14,17 +14,13 @@
 
 // Delegate interface that GpuClient uses to get the current GpuHost and/or
 // GpuMemoryBufferManager instances. These functions are guaranteed to be called
-// on the thread corresponding to GpuClient's task runner unless otherwise
-// noted.
+// on the thread corresponding to GpuClient's task runner.
 class GpuClientDelegate {
  public:
   virtual ~GpuClientDelegate() {}
 
   // Returns the current instance of GpuHostImpl. If GPU service is not running,
   // tries to launch it. If the launch is unsuccessful, returns nullptr.
-  // If GpuClient's constructor is passed gpu_host_lives_on_ui_thread==true,
-  // then this can only be called on the UI thread. Otherwise, it will be called
-  // on the GpuClient's IO thread.
   virtual GpuHostImpl* EnsureGpuHost() = 0;
 
   // Returns the current HostGpuMemoryBufferManager instance.
diff --git a/components/viz/host/host_gpu_memory_buffer_manager.cc b/components/viz/host/host_gpu_memory_buffer_manager.cc
index 609a65c..5a1dbb8 100644
--- a/components/viz/host/host_gpu_memory_buffer_manager.cc
+++ b/components/viz/host/host_gpu_memory_buffer_manager.cc
@@ -14,6 +14,7 @@
 #include "gpu/ipc/common/gpu_memory_buffer_impl.h"
 #include "gpu/ipc/common/gpu_memory_buffer_impl_shared_memory.h"
 #include "gpu/ipc/common/gpu_memory_buffer_support.h"
+#include "mojo/public/cpp/bindings/sync_call_restrictions.h"
 #include "services/viz/privileged/mojom/gl/gpu_service.mojom.h"
 #include "ui/base/ui_base_features.h"
 #include "ui/gfx/buffer_format_util.h"
@@ -143,13 +144,12 @@
     gfx::BufferFormat format,
     gfx::BufferUsage usage,
     gpu::SurfaceHandle surface_handle,
-    base::OnceCallback<void(gfx::GpuMemoryBufferHandle)> callback) {
+    base::OnceCallback<void(gfx::GpuMemoryBufferHandle)> callback,
+    bool call_sync) {
   DCHECK(task_runner_->BelongsToCurrentThread());
   if (!weak_ptr_)
     weak_ptr_ = weak_factory_.GetWeakPtr();
-  if (gpu_memory_buffer_support_->GetNativeGpuMemoryBufferType() !=
-          gfx::EMPTY_BUFFER &&
-      IsNativeGpuMemoryBufferConfiguration(format, usage)) {
+  if (CreateBufferUsesGpuService(format, usage)) {
     if (auto* gpu_service = GetGpuService()) {
       PendingBufferInfo buffer_info;
       buffer_info.size = size;
@@ -159,11 +159,23 @@
       buffer_info.callback = std::move(callback);
       pending_buffers_[client_id].insert(
           std::make_pair(id, std::move(buffer_info)));
-      gpu_service->CreateGpuMemoryBuffer(
-          id, size, format, usage, client_id, surface_handle,
-          base::BindOnce(
-              &HostGpuMemoryBufferManager::OnGpuMemoryBufferAllocated,
-              weak_ptr_, gpu_service_version_, client_id, id));
+      if (call_sync) {
+        DCHECK(runs_on_ui_thread_);
+        gfx::GpuMemoryBufferHandle handle;
+        {
+          mojo::SyncCallRestrictions::ScopedAllowSyncCall scoped_allow;
+          gpu_service->CreateGpuMemoryBuffer(id, size, format, usage, client_id,
+                                             surface_handle, &handle);
+        }
+        OnGpuMemoryBufferAllocated(gpu_service_version_, client_id, id,
+                                   std::move(handle));
+      } else {
+        gpu_service->CreateGpuMemoryBuffer(
+            id, size, format, usage, client_id, surface_handle,
+            base::BindOnce(
+                &HostGpuMemoryBufferManager::OnGpuMemoryBufferAllocated,
+                weak_ptr_, gpu_service_version_, client_id, id));
+      }
     } else {
       // GPU service failed to start. Run the callback with null handle.
       std::move(callback).Run(gfx::GpuMemoryBufferHandle());
@@ -213,7 +225,11 @@
   base::WaitableEvent wait_event(
       base::WaitableEvent::ResetPolicy::MANUAL,
       base::WaitableEvent::InitialState::NOT_SIGNALED);
-  DCHECK(!task_runner_->BelongsToCurrentThread());
+  DCHECK(runs_on_ui_thread_ || !task_runner_->BelongsToCurrentThread());
+
+  bool call_sync = runs_on_ui_thread_ &&
+                   task_runner_->BelongsToCurrentThread() &&
+                   CreateBufferUsesGpuService(format, usage);
 
   // A refcounted wrapper around a bool so that if the thread waiting on a
   // PostTask to the main thread is quit due to shutdown and the task runs
@@ -235,26 +251,30 @@
       cancelled, &handle, &wait_event);
   // We block with a WaitableEvent until the callback is run. So using
   // base::Unretained() is safe here.
-  auto allocate_callback =
-      base::BindOnce(&HostGpuMemoryBufferManager::AllocateGpuMemoryBuffer,
-                     base::Unretained(this), id, client_id_, size, format,
-                     usage, surface_handle, std::move(reply_callback));
-  task_runner_->PostTask(FROM_HERE, std::move(allocate_callback));
-  base::ScopedAllowBaseSyncPrimitivesOutsideBlockingScope
-      allow_base_sync_primitives;
-  if (runs_on_ui_thread_ && shutdown_event) {
-    // If this class is running on the UI thread then
-    // TileManager::FinishTasksAndCleanUp could block on the worker thread where
-    // this task is running. That could in turn block on a task posted to the UI
-    // thread. We avoid this deadlock by having an event that TileManager can
-    // set to cancel this wait.
-    base::WaitableEvent* waitables[] = {&wait_event, shutdown_event};
-    size_t index =
-        base::WaitableEvent::WaitMany(waitables, base::size(waitables));
-    if (index == 1)
-      cancelled->data = true;
+  auto allocate_callback = base::BindOnce(
+      &HostGpuMemoryBufferManager::AllocateGpuMemoryBuffer,
+      base::Unretained(this), id, client_id_, size, format, usage,
+      surface_handle, std::move(reply_callback), call_sync);
+  if (runs_on_ui_thread_ && task_runner_->BelongsToCurrentThread()) {
+    std::move(allocate_callback).Run();
   } else {
-    wait_event.Wait();
+    task_runner_->PostTask(FROM_HERE, std::move(allocate_callback));
+    base::ScopedAllowBaseSyncPrimitivesOutsideBlockingScope
+        allow_base_sync_primitives;
+    if (runs_on_ui_thread_ && shutdown_event) {
+      // If this class is running on the UI thread then
+      // TileManager::FinishTasksAndCleanUp could block on the worker thread
+      // where this task is running. That could in turn block on a task posted
+      // to the UI thread. We avoid this deadlock by having an event that
+      // TileManager can set to cancel this wait.
+      base::WaitableEvent* waitables[] = {&wait_event, shutdown_event};
+      size_t index =
+          base::WaitableEvent::WaitMany(waitables, base::size(waitables));
+      if (index == 1)
+        cancelled->data = true;
+    } else {
+      wait_event.Wait();
+    }
   }
 
   if (handle.is_null())
@@ -422,4 +442,12 @@
   std::move(pending_buffer.callback).Run(std::move(handle));
 }
 
+bool HostGpuMemoryBufferManager::CreateBufferUsesGpuService(
+    gfx::BufferFormat format,
+    gfx::BufferUsage usage) {
+  return gpu_memory_buffer_support_->GetNativeGpuMemoryBufferType() !=
+             gfx::EMPTY_BUFFER &&
+         IsNativeGpuMemoryBufferConfiguration(format, usage);
+}
+
 }  // namespace viz
diff --git a/components/viz/host/host_gpu_memory_buffer_manager.h b/components/viz/host/host_gpu_memory_buffer_manager.h
index 9a5f0d41..9de38d4 100644
--- a/components/viz/host/host_gpu_memory_buffer_manager.h
+++ b/components/viz/host/host_gpu_memory_buffer_manager.h
@@ -69,7 +69,8 @@
       gfx::BufferFormat format,
       gfx::BufferUsage usage,
       gpu::SurfaceHandle surface_handle,
-      base::OnceCallback<void(gfx::GpuMemoryBufferHandle)> callback);
+      base::OnceCallback<void(gfx::GpuMemoryBufferHandle)> callback,
+      bool call_sync = false);
 
   bool IsNativeGpuMemoryBufferConfiguration(gfx::BufferFormat format,
                                             gfx::BufferUsage usage) const;
@@ -135,6 +136,9 @@
                                   gfx::GpuMemoryBufferId id,
                                   gfx::GpuMemoryBufferHandle handle);
 
+  bool CreateBufferUsesGpuService(gfx::BufferFormat format,
+                                  gfx::BufferUsage usage);
+
   GpuServiceProvider gpu_service_provider_;
   mojom::GpuService* gpu_service_ = nullptr;
 
diff --git a/components/viz/service/display/surface_aggregator.cc b/components/viz/service/display/surface_aggregator.cc
index 61604c3..ae7c246 100644
--- a/components/viz/service/display/surface_aggregator.cc
+++ b/components/viz/service/display/surface_aggregator.cc
@@ -1552,6 +1552,8 @@
     surface->client()->RefResources(resource_list);
   provider_->ReceiveFromChild(child_id, resource_list);
 
+  stats_->declare_resources_count += resource_list.size();
+
   // Figure out which resources are actually used in the render pass.
   // Note that we first gather them in a vector, since ResourceIdSet (which we
   // actually need) is a flat_set, which means bulk insertion we do at the end
@@ -1615,8 +1617,11 @@
   base::flat_map<CompositorRenderPassId, RenderPassMapEntry> render_pass_map =
       GenerateRenderPassMap(frame.render_pass_list, IsRootSurface(surface));
 
+  base::ElapsedTimer timer;
   bool valid_frame = DeclareResourcesToProvider(surface, frame.resource_list,
                                                 frame.render_pass_list);
+  stats_->declare_resources_time += timer.Elapsed();
+
   if (!valid_frame)
     return gfx::Rect();
   valid_surfaces_.insert(surface->surface_id());
@@ -1947,6 +1952,9 @@
       stats_->prewalked_surface_count);
   UMA_HISTOGRAM_COUNTS_100("Compositing.SurfaceAggregator.CopiedSurfaceCount",
                            stats_->copied_surface_count);
+  UMA_HISTOGRAM_COUNTS_1000(
+      "Compositing.SurfaceAggregator.DeclareResourcesCount",
+      stats_->declare_resources_count);
 
   constexpr auto kMinTime = base::TimeDelta::FromMicroseconds(5);
   constexpr auto kMaxTime = base::TimeDelta::FromMilliseconds(10);
@@ -1957,6 +1965,9 @@
   UMA_HISTOGRAM_CUSTOM_MICROSECONDS_TIMES(
       "Compositing.SurfaceAggregator.CopyUs", stats_->copy_time, kMinTime,
       kMaxTime, kTimeBuckets);
+  UMA_HISTOGRAM_CUSTOM_MICROSECONDS_TIMES(
+      "Compositing.SurfaceAggregator.DeclareResourcesUs",
+      stats_->declare_resources_time, kMinTime, kMaxTime, kTimeBuckets);
 
   stats_.reset();
 }
diff --git a/components/viz/service/display/surface_aggregator.h b/components/viz/service/display/surface_aggregator.h
index ccc25f9..9e20dfff 100644
--- a/components/viz/service/display/surface_aggregator.h
+++ b/components/viz/service/display/surface_aggregator.h
@@ -99,9 +99,11 @@
   struct AggregateStatistics {
     int prewalked_surface_count = 0;
     int copied_surface_count = 0;
+    int declare_resources_count = 0;
 
     base::TimeDelta prewalk_time;
     base::TimeDelta copy_time;
+    base::TimeDelta declare_resources_time;
   };
 
   // Helper function that gets a list of render passes and returns a map from
diff --git a/content/browser/back_forward_cache_browsertest.cc b/content/browser/back_forward_cache_browsertest.cc
index 9cfa851..39152c5e 100644
--- a/content/browser/back_forward_cache_browsertest.cc
+++ b/content/browser/back_forward_cache_browsertest.cc
@@ -60,6 +60,7 @@
 #include "content/public/test/content_browser_test.h"
 #include "content/public/test/content_browser_test_utils.h"
 #include "content/public/test/idle_test_utils.h"
+#include "content/public/test/mock_web_contents_observer.h"
 #include "content/public/test/navigation_handle_observer.h"
 #include "content/public/test/test_navigation_observer.h"
 #include "content/public/test/test_navigation_throttle.h"
@@ -8575,7 +8576,22 @@
 
   // 2) Navigate to B, now A enters BackForwardCache. Check the
   // LifecycleStateImpl of both RenderFrameHost A and B.
-  EXPECT_TRUE(NavigateToURL(shell(), url_b));
+  {
+    testing::NiceMock<MockWebContentsObserver> state_change_observer(
+        web_contents());
+    EXPECT_CALL(state_change_observer,
+                RenderFrameHostStateChanged(
+                    rfh_a, RenderFrameHost::LifecycleState::kActive,
+                    RenderFrameHost::LifecycleState::kInBackForwardCache));
+    // We don't know |rfh_b| yet, so we'll match any frame.
+    EXPECT_CALL(state_change_observer,
+                RenderFrameHostStateChanged(
+                    testing::Not(rfh_a),
+                    RenderFrameHost::LifecycleState::kPendingCommit,
+                    RenderFrameHost::LifecycleState::kActive));
+
+    EXPECT_TRUE(NavigateToURL(shell(), url_b));
+  }
   RenderFrameHostImpl* rfh_b = current_frame_host();
   EXPECT_TRUE(rfh_a->IsInBackForwardCache());
   EXPECT_EQ(RenderFrameHostImpl::LifecycleStateImpl::kInBackForwardCache,
@@ -8589,8 +8605,21 @@
 
   // 3) Go back to A and check again the LifecycleStateImpl of both
   // RenderFrameHost A and B.
-  web_contents()->GetController().GoBack();
-  EXPECT_TRUE(WaitForLoadStop(shell()->web_contents()));
+  {
+    testing::NiceMock<MockWebContentsObserver> state_change_observer(
+        web_contents());
+    EXPECT_CALL(state_change_observer,
+                RenderFrameHostStateChanged(
+                    rfh_a, RenderFrameHost::LifecycleState::kInBackForwardCache,
+                    RenderFrameHost::LifecycleState::kActive));
+    EXPECT_CALL(state_change_observer,
+                RenderFrameHostStateChanged(
+                    rfh_b, RenderFrameHost::LifecycleState::kActive,
+                    RenderFrameHost::LifecycleState::kInBackForwardCache));
+
+    web_contents()->GetController().GoBack();
+    EXPECT_TRUE(WaitForLoadStop(shell()->web_contents()));
+  }
   EXPECT_EQ(RenderFrameHostImpl::LifecycleStateImpl::kActive,
             rfh_a->lifecycle_state());
   EXPECT_TRUE(rfh_b->IsInBackForwardCache());
@@ -8599,6 +8628,127 @@
 }
 
 IN_PROC_BROWSER_TEST_F(BackForwardCacheBrowserTest,
+                       CheckLifecycleStateTransitionWithSubframes) {
+  IsolateAllSitesForTesting(base::CommandLine::ForCurrentProcess());
+  ASSERT_TRUE(embedded_test_server()->Start());
+  GURL url_a(embedded_test_server()->GetURL(
+      "a.com", "/cross_site_iframe_factory.html?a(b)"));
+  GURL url_c(embedded_test_server()->GetURL(
+      "c.com", "/cross_site_iframe_factory.html?c(d)"));
+
+  // Navigate to A(B) and check the lifecycle states of A and B.
+  EXPECT_TRUE(NavigateToURL(shell(), url_a));
+  RenderFrameHostImpl* rfh_a = current_frame_host();
+  RenderFrameHostImpl* rfh_b = rfh_a->child_at(0)->current_frame_host();
+  EXPECT_FALSE(rfh_a->IsInBackForwardCache());
+  EXPECT_FALSE(rfh_b->IsInBackForwardCache());
+  EXPECT_EQ(RenderFrameHostImpl::LifecycleStateImpl::kActive,
+            rfh_a->lifecycle_state());
+  EXPECT_EQ(RenderFrameHost::LifecycleState::kActive,
+            rfh_a->GetLifecycleState());
+  EXPECT_EQ(RenderFrameHostImpl::LifecycleStateImpl::kActive,
+            rfh_b->lifecycle_state());
+  EXPECT_EQ(RenderFrameHost::LifecycleState::kActive,
+            rfh_b->GetLifecycleState());
+
+  // Navigate to C(D), now A(B) enters BackForwardCache.
+  {
+    testing::NiceMock<MockWebContentsObserver> state_change_observer(
+        web_contents());
+    EXPECT_CALL(state_change_observer,
+                RenderFrameHostStateChanged(
+                    rfh_a, RenderFrameHost::LifecycleState::kActive,
+                    RenderFrameHost::LifecycleState::kInBackForwardCache));
+    EXPECT_CALL(state_change_observer,
+                RenderFrameHostStateChanged(
+                    rfh_b, RenderFrameHost::LifecycleState::kActive,
+                    RenderFrameHost::LifecycleState::kInBackForwardCache));
+    // We don't know |rfh_c| and |rfh_d| yet, so we'll match any frame.
+    EXPECT_CALL(state_change_observer,
+                RenderFrameHostStateChanged(
+                    testing::Not(testing::AnyOf(rfh_a, rfh_b)),
+                    RenderFrameHost::LifecycleState::kPendingCommit,
+                    RenderFrameHost::LifecycleState::kActive))
+        .Times(2);
+    // Deletion of frame D's initial RFH.
+    EXPECT_CALL(state_change_observer,
+                RenderFrameHostStateChanged(
+                    testing::Not(testing::AnyOf(rfh_a, rfh_b)),
+                    RenderFrameHost::LifecycleState::kActive,
+                    RenderFrameHost::LifecycleState::kPendingDeletion));
+
+    EXPECT_TRUE(NavigateToURL(shell(), url_c));
+  }
+  RenderFrameHostImpl* rfh_c = current_frame_host();
+  RenderFrameHostImpl* rfh_d = rfh_c->child_at(0)->current_frame_host();
+  EXPECT_TRUE(rfh_a->IsInBackForwardCache());
+  EXPECT_TRUE(rfh_b->IsInBackForwardCache());
+  EXPECT_FALSE(rfh_c->IsInBackForwardCache());
+  EXPECT_FALSE(rfh_d->IsInBackForwardCache());
+  EXPECT_EQ(RenderFrameHostImpl::LifecycleStateImpl::kInBackForwardCache,
+            rfh_a->lifecycle_state());
+  EXPECT_EQ(RenderFrameHost::LifecycleState::kInBackForwardCache,
+            rfh_a->GetLifecycleState());
+  EXPECT_EQ(RenderFrameHostImpl::LifecycleStateImpl::kInBackForwardCache,
+            rfh_b->lifecycle_state());
+  EXPECT_EQ(RenderFrameHost::LifecycleState::kInBackForwardCache,
+            rfh_b->GetLifecycleState());
+  EXPECT_EQ(RenderFrameHostImpl::LifecycleStateImpl::kActive,
+            rfh_c->lifecycle_state());
+  EXPECT_EQ(RenderFrameHost::LifecycleState::kActive,
+            rfh_c->GetLifecycleState());
+  EXPECT_EQ(RenderFrameHostImpl::LifecycleStateImpl::kActive,
+            rfh_d->lifecycle_state());
+  EXPECT_EQ(RenderFrameHost::LifecycleState::kActive,
+            rfh_d->GetLifecycleState());
+
+  // Go back to A(B), A(B) is restored and C(D) enters BackForwardCache.
+  {
+    testing::NiceMock<MockWebContentsObserver> state_change_observer(
+        web_contents());
+    EXPECT_CALL(state_change_observer,
+                RenderFrameHostStateChanged(
+                    rfh_a, RenderFrameHost::LifecycleState::kInBackForwardCache,
+                    RenderFrameHost::LifecycleState::kActive));
+    EXPECT_CALL(state_change_observer,
+                RenderFrameHostStateChanged(
+                    rfh_b, RenderFrameHost::LifecycleState::kInBackForwardCache,
+                    RenderFrameHost::LifecycleState::kActive));
+    EXPECT_CALL(state_change_observer,
+                RenderFrameHostStateChanged(
+                    rfh_c, RenderFrameHost::LifecycleState::kActive,
+                    RenderFrameHost::LifecycleState::kInBackForwardCache));
+    EXPECT_CALL(state_change_observer,
+                RenderFrameHostStateChanged(
+                    rfh_d, RenderFrameHost::LifecycleState::kActive,
+                    RenderFrameHost::LifecycleState::kInBackForwardCache));
+
+    web_contents()->GetController().GoBack();
+    EXPECT_TRUE(WaitForLoadStop(shell()->web_contents()));
+  }
+  EXPECT_FALSE(rfh_a->IsInBackForwardCache());
+  EXPECT_FALSE(rfh_b->IsInBackForwardCache());
+  EXPECT_TRUE(rfh_c->IsInBackForwardCache());
+  EXPECT_TRUE(rfh_d->IsInBackForwardCache());
+  EXPECT_EQ(RenderFrameHostImpl::LifecycleStateImpl::kActive,
+            rfh_a->lifecycle_state());
+  EXPECT_EQ(RenderFrameHost::LifecycleState::kActive,
+            rfh_a->GetLifecycleState());
+  EXPECT_EQ(RenderFrameHostImpl::LifecycleStateImpl::kActive,
+            rfh_b->lifecycle_state());
+  EXPECT_EQ(RenderFrameHost::LifecycleState::kActive,
+            rfh_b->GetLifecycleState());
+  EXPECT_EQ(RenderFrameHostImpl::LifecycleStateImpl::kInBackForwardCache,
+            rfh_c->lifecycle_state());
+  EXPECT_EQ(RenderFrameHost::LifecycleState::kInBackForwardCache,
+            rfh_c->GetLifecycleState());
+  EXPECT_EQ(RenderFrameHostImpl::LifecycleStateImpl::kInBackForwardCache,
+            rfh_d->lifecycle_state());
+  EXPECT_EQ(RenderFrameHost::LifecycleState::kInBackForwardCache,
+            rfh_d->GetLifecycleState());
+}
+
+IN_PROC_BROWSER_TEST_F(BackForwardCacheBrowserTest,
                        DoesNotCacheIfSpeechRecognitionIsStarted) {
   ASSERT_TRUE(embedded_test_server()->Start());
   GURL url_a(embedded_test_server()->GetURL("a.com", "/title1.html"));
diff --git a/content/browser/gpu/browser_gpu_channel_host_factory.cc b/content/browser/gpu/browser_gpu_channel_host_factory.cc
index d28d16c..8affd72 100644
--- a/content/browser/gpu/browser_gpu_channel_host_factory.cc
+++ b/content/browser/gpu/browser_gpu_channel_host_factory.cc
@@ -305,7 +305,12 @@
     gpu_channel_ = nullptr;
   }
 
-  gpu_memory_buffer_manager_ = nullptr;
+  if (base::FeatureList::IsEnabled(features::kProcessHostOnUI) &&
+      gpu_memory_buffer_manager_) {
+    delete gpu_memory_buffer_manager_.release();
+  } else {
+    gpu_memory_buffer_manager_ = nullptr;
+  }
 }
 
 BrowserGpuChannelHostFactory::BrowserGpuChannelHostFactory()
@@ -358,6 +363,8 @@
 
   if (base::FeatureList::IsEnabled(features::kProcessHostOnUI) &&
       gpu_memory_buffer_manager_) {
+    // Delete this on the main thread (since otherwise the unique_ptr has a
+    // trait to delete it on the IO thread).
     delete gpu_memory_buffer_manager_.release();
   }
 }
diff --git a/content/browser/gpu/gpu_client.cc b/content/browser/gpu/gpu_client.cc
index 627507b..e92f0a8 100644
--- a/content/browser/gpu/gpu_client.cc
+++ b/content/browser/gpu/gpu_client.cc
@@ -6,22 +6,25 @@
 
 #include "content/browser/gpu/browser_gpu_client_delegate.h"
 #include "content/common/child_process_host_impl.h"
+#include "content/public/browser/browser_task_traits.h"
+#include "content/public/browser/browser_thread.h"
 #include "content/public/common/content_features.h"
 
 namespace content {
 
 std::unique_ptr<viz::GpuClient, base::OnTaskRunnerDeleter> CreateGpuClient(
     mojo::PendingReceiver<viz::mojom::Gpu> receiver,
-    viz::GpuClient::ConnectionErrorHandlerClosure connection_error_handler,
-    scoped_refptr<base::SingleThreadTaskRunner> task_runner) {
+    viz::GpuClient::ConnectionErrorHandlerClosure connection_error_handler) {
   const int client_id = ChildProcessHostImpl::GenerateChildProcessUniqueId();
   const uint64_t client_tracing_id =
       ChildProcessHostImpl::ChildProcessUniqueIdToTracingProcessId(client_id);
+  auto task_runner = base::FeatureList::IsEnabled(features::kProcessHostOnUI)
+                         ? GetUIThreadTaskRunner({})
+                         : GetIOThreadTaskRunner({});
   std::unique_ptr<viz::GpuClient, base::OnTaskRunnerDeleter> gpu_client(
       new viz::GpuClient(
           std::make_unique<BrowserGpuClientDelegate>(), client_id,
           client_tracing_id,
-          base::FeatureList::IsEnabled(features::kProcessHostOnUI),
           task_runner),
       base::OnTaskRunnerDeleter(task_runner));
   gpu_client->SetConnectionErrorHandler(std::move(connection_error_handler));
diff --git a/content/browser/gpu/gpu_memory_buffer_manager_singleton.cc b/content/browser/gpu/gpu_memory_buffer_manager_singleton.cc
index f26c586b..7b61ee6 100644
--- a/content/browser/gpu/gpu_memory_buffer_manager_singleton.cc
+++ b/content/browser/gpu/gpu_memory_buffer_manager_singleton.cc
@@ -54,7 +54,9 @@
           base::BindRepeating(&content::GetGpuService),
           client_id,
           std::make_unique<gpu::GpuMemoryBufferSupport>(),
-          GetIOThreadTaskRunner({})),
+          base::FeatureList::IsEnabled(features::kProcessHostOnUI)
+              ? GetUIThreadTaskRunner({})
+              : GetIOThreadTaskRunner({})),
       gpu_data_manager_impl_(GpuDataManagerImpl::GetInstance()) {
   DCHECK(!g_gpu_memory_buffer_manager);
   g_gpu_memory_buffer_manager = this;
diff --git a/content/browser/gpu/in_process_gpu_thread_browsertests.cc b/content/browser/gpu/in_process_gpu_thread_browsertests.cc
index fe502914..eddc512 100644
--- a/content/browser/gpu/in_process_gpu_thread_browsertests.cc
+++ b/content/browser/gpu/in_process_gpu_thread_browsertests.cc
@@ -10,6 +10,7 @@
 #include "content/gpu/in_process_gpu_thread.h"
 #include "content/public/browser/browser_task_traits.h"
 #include "content/public/browser/browser_thread.h"
+#include "content/public/common/content_features.h"
 #include "content/public/common/content_switches.h"
 #include "content/public/test/browser_test.h"
 #include "content/public/test/content_browser_test.h"
@@ -33,7 +34,10 @@
 
 void WaitUntilGpuProcessHostIsCreated() {
   base::RunLoop run_loop;
-  content::GetIOThreadTaskRunner({})->PostTaskAndReply(
+  auto task_runner = base::FeatureList::IsEnabled(features::kProcessHostOnUI)
+                         ? content::GetUIThreadTaskRunner({})
+                         : content::GetIOThreadTaskRunner({});
+  task_runner->PostTaskAndReply(
       FROM_HERE, base::BindOnce(&CreateGpuProcessHost), run_loop.QuitClosure());
   run_loop.Run();
 }
diff --git a/content/browser/media/encrypted_media_browsertest.cc b/content/browser/media/encrypted_media_browsertest.cc
index 849f902..6f52b8b3 100644
--- a/content/browser/media/encrypted_media_browsertest.cc
+++ b/content/browser/media/encrypted_media_browsertest.cc
@@ -111,10 +111,8 @@
     // TODO(xhwang): Even when config change is not supported we still start
     // content shell only to return directly here. We probably should not run
     // these test cases at all.
-    if (CurrentSourceType() != SrcType::MSE) {
-      DVLOG(0) << "Config change only happens when using MSE.";
-      return;
-    }
+    if (CurrentSourceType() != SrcType::MSE)
+      GTEST_SKIP() << "Config change only happens when using MSE.";
 
     base::StringPairs query_params;
     query_params.emplace_back("keySystem", CurrentKeySystem());
@@ -149,10 +147,8 @@
   void RunMultipleFileTest(const std::string& video_file,
                            const std::string& audio_file,
                            const std::string& expected_title) {
-    if (CurrentSourceType() != SrcType::MSE) {
-      DVLOG(0) << "Skipping test; Can only play MP4 encrypted streams by MSE.";
-      return;
-    }
+    if (CurrentSourceType() != SrcType::MSE)
+      GTEST_SKIP() << "Can only play MP4 encrypted streams by MSE.";
 
     base::StringPairs query_params;
     query_params.emplace_back("keySystem", CurrentKeySystem());
@@ -249,7 +245,7 @@
 IN_PROC_BROWSER_TEST_P(EncryptedMediaTest, Playback_AudioOnly_WebM_Opus) {
 #if defined(OS_ANDROID)
   if (!media::MediaCodecUtil::IsOpusDecoderAvailable())
-    return;
+    GTEST_SKIP() << "Opus decoder not available";
 #endif
   TestSimplePlayback("bear-320x240-opus-a_enc-a.webm");
 }
@@ -257,7 +253,7 @@
 IN_PROC_BROWSER_TEST_P(EncryptedMediaTest, Playback_VideoAudio_WebM_Opus) {
 #if defined(OS_ANDROID)
   if (!media::MediaCodecUtil::IsOpusDecoderAvailable())
-    return;
+    GTEST_SKIP() << "Opus decoder not available";
 #endif
   TestSimplePlayback("bear-320x240-opus-av_enc-av.webm");
 }
@@ -265,7 +261,7 @@
 IN_PROC_BROWSER_TEST_P(EncryptedMediaTest, Playback_VideoClearAudio_WebM_Opus) {
 #if defined(OS_ANDROID)
   if (!media::MediaCodecUtil::IsOpusDecoderAvailable())
-    return;
+    GTEST_SKIP() << "Opus decoder not available";
 #endif
   TestSimplePlayback("bear-320x240-opus-av_enc-v.webm");
 }
@@ -277,17 +273,16 @@
 IN_PROC_BROWSER_TEST_P(EncryptedMediaTest, Playback_AudioOnly_MP4_OPUS) {
 #if defined(OS_ANDROID)
   if (!media::MediaCodecUtil::IsOpusDecoderAvailable())
-    return;
+    GTEST_SKIP() << "Opus decoder not available";
 #endif
   RunMultipleFileTest(std::string(), "bear-opus-cenc.mp4", media::kEnded);
 }
 
 IN_PROC_BROWSER_TEST_P(EncryptedMediaTest, Playback_VideoOnly_MP4_VP9) {
   // MP4 without MSE is not support yet, http://crbug.com/170793.
-  if (CurrentSourceType() != SrcType::MSE) {
-    DVLOG(0) << "Skipping test; Can only play MP4 encrypted streams by MSE.";
-    return;
-  }
+  if (CurrentSourceType() != SrcType::MSE)
+    GTEST_SKIP() << "Can only play MP4 encrypted streams by MSE.";
+
   TestSimplePlayback("bear-320x240-v_frag-vp9-cenc.mp4");
 }
 
@@ -300,10 +295,9 @@
 
 IN_PROC_BROWSER_TEST_P(EncryptedMediaTest, Playback_VideoOnly_MP4_VP9Profile2) {
   // MP4 without MSE is not support yet, http://crbug.com/170793.
-  if (CurrentSourceType() != SrcType::MSE) {
-    DVLOG(0) << "Skipping test; Can only play MP4 encrypted streams by MSE.";
-    return;
-  }
+  if (CurrentSourceType() != SrcType::MSE)
+    GTEST_SKIP() << "Can only play MP4 encrypted streams by MSE.";
+
   TestSimplePlayback("bear-320x240-v-vp9_profile2_subsample_cenc-v.mp4");
 }
 #endif  // !defined(OS_ANDROID)
@@ -319,19 +313,17 @@
 
 IN_PROC_BROWSER_TEST_P(EncryptedMediaTest, Playback_VideoOnly_MP4_AV1) {
   // MP4 without MSE is not support yet, http://crbug.com/170793.
-  if (CurrentSourceType() != SrcType::MSE) {
-    DVLOG(0) << "Skipping test; Can only play MP4 encrypted streams by MSE.";
-    return;
-  }
+  if (CurrentSourceType() != SrcType::MSE)
+    GTEST_SKIP() << "Can only play MP4 encrypted streams by MSE.";
+
   TestSimplePlayback("bear-av1-cenc.mp4");
 }
 
 IN_PROC_BROWSER_TEST_P(EncryptedMediaTest, Playback_VideoOnly_MP4_AV1_10bit) {
   // MP4 without MSE is not support yet, http://crbug.com/170793.
-  if (CurrentSourceType() != SrcType::MSE) {
-    DVLOG(0) << "Skipping test; Can only play MP4 encrypted streams by MSE.";
-    return;
-  }
+  if (CurrentSourceType() != SrcType::MSE)
+    GTEST_SKIP() << "Can only play MP4 encrypted streams by MSE.";
+
   TestSimplePlayback("bear-av1-320x180-10bit-cenc.mp4");
 }
 #endif  // BUILDFLAG(ENABLE_AV1_DECODER)
diff --git a/content/browser/renderer_host/render_frame_host_impl.cc b/content/browser/renderer_host/render_frame_host_impl.cc
index f2026c9..b5648ed 100644
--- a/content/browser/renderer_host/render_frame_host_impl.cc
+++ b/content/browser/renderer_host/render_frame_host_impl.cc
@@ -10922,8 +10922,9 @@
     LifecycleState old_lifecycle_state = GetLifecycleStateFromImpl(old_state);
     LifecycleState new_lifecycle_state = GetLifecycleState();
 
-    // old and new lifecycle state can be equal for kPendingDeletion state.
-    // Don't notify the observers in such cases.
+    // Old and new lifecycle states can be equal due to the same LifecycleState
+    // representing multiple LifecycleStateImpls, for example the
+    // kPendingDeletion state. Don't notify the observers in such cases.
     if (old_lifecycle_state != new_lifecycle_state) {
       delegate_->RenderFrameHostStateChanged(this, old_lifecycle_state,
                                              new_lifecycle_state);
diff --git a/content/browser/renderer_host/render_process_host_impl.cc b/content/browser/renderer_host/render_process_host_impl.cc
index 3ae3008..bdf1850 100644
--- a/content/browser/renderer_host/render_process_host_impl.cc
+++ b/content/browser/renderer_host/render_process_host_impl.cc
@@ -1646,10 +1646,12 @@
   const int id = GetID();
   const uint64_t tracing_id =
       ChildProcessHostImpl::ChildProcessUniqueIdToTracingProcessId(id);
-  gpu_client_.reset(new viz::GpuClient(
-      std::make_unique<BrowserGpuClientDelegate>(), id, tracing_id,
-      base::FeatureList::IsEnabled(features::kProcessHostOnUI),
-      GetIOThreadTaskRunner({})));
+  auto task_runner = base::FeatureList::IsEnabled(features::kProcessHostOnUI)
+                         ? GetUIThreadTaskRunner({})
+                         : GetIOThreadTaskRunner({});
+  gpu_client_.reset(
+      new viz::GpuClient(std::make_unique<BrowserGpuClientDelegate>(), id,
+                         tracing_id, task_runner));
 }
 
 // static
@@ -1746,6 +1748,12 @@
                                   "render_process_host", this);
   TRACE_EVENT_NESTABLE_ASYNC_END1("shutdown", "Browser.RenderProcessHostImpl",
                                   this, "render_process_host", this);
+
+  // Manually delete here in order to avoid DeleteOnIOThread trait when
+  // kProcessHostOnUI is enabled.
+  if (base::FeatureList::IsEnabled(features::kProcessHostOnUI) && gpu_client_) {
+    delete gpu_client_.release();
+  }
 }
 
 bool RenderProcessHostImpl::Init() {
@@ -2376,8 +2384,15 @@
   if (gpu_client_) {
     // |gpu_client_| outlives the registry, because its destruction is posted to
     // IO thread from the destructor of |this|.
-    registry->AddInterface(base::BindRepeating(
-        &viz::GpuClient::Add, base::Unretained(gpu_client_.get())));
+    if (base::FeatureList::IsEnabled(features::kProcessHostOnUI)) {
+      AddUIThreadInterface(
+          registry.get(),
+          base::BindRepeating(&viz::GpuClient::Add,
+                              base::Unretained(gpu_client_.get())));
+    } else {
+      registry->AddInterface(base::BindRepeating(
+          &viz::GpuClient::Add, base::Unretained(gpu_client_.get())));
+    }
   }
 
   registry->AddInterface(
diff --git a/content/browser/service_worker/service_worker_registry_unittest.cc b/content/browser/service_worker/service_worker_registry_unittest.cc
index 5496d6c4..c2f6f34c 100644
--- a/content/browser/service_worker/service_worker_registry_unittest.cc
+++ b/content/browser/service_worker/service_worker_registry_unittest.cc
@@ -562,12 +562,12 @@
   std::unique_ptr<EmbeddedWorkerTestHelper> helper_;
 };
 
-TEST_F(ServiceWorkerRegistryTest, RegisteredOriginCount) {
+TEST_F(ServiceWorkerRegistryTest, RegisteredStorageKeyCount) {
   {
     base::HistogramTester histogram_tester;
     EXPECT_TRUE(GetRegisteredOrigins().empty());
-    histogram_tester.ExpectUniqueSample("ServiceWorker.RegisteredOriginCount",
-                                        0, 1);
+    histogram_tester.ExpectUniqueSample(
+        "ServiceWorker.RegisteredStorageKeyCount", 0, 1);
   }
 
   std::pair<GURL, GURL> scope_and_script_pairs[] = {
@@ -599,15 +599,16 @@
   {
     base::HistogramTester histogram_tester;
     EXPECT_EQ(3UL, GetRegisteredOrigins().size());
-    histogram_tester.ExpectUniqueSample("ServiceWorker.RegisteredOriginCount",
-                                        3, 1);
+    histogram_tester.ExpectUniqueSample(
+        "ServiceWorker.RegisteredStorageKeyCount", 3, 1);
   }
 
   // Re-initializing shouldn't re-record the histogram.
   {
     base::HistogramTester histogram_tester;
     EXPECT_EQ(3UL, GetRegisteredOrigins().size());
-    histogram_tester.ExpectTotalCount("ServiceWorker.RegisteredOriginCount", 0);
+    histogram_tester.ExpectTotalCount("ServiceWorker.RegisteredStorageKeyCount",
+                                      0);
   }
 }
 
diff --git a/content/browser/site_per_process_browsertest.cc b/content/browser/site_per_process_browsertest.cc
index 49c7bf5..57253fe 100644
--- a/content/browser/site_per_process_browsertest.cc
+++ b/content/browser/site_per_process_browsertest.cc
@@ -697,6 +697,12 @@
       ->UpdateViewportIntersectionInternal(intersection_state);
 }
 
+void SitePerProcessBrowserTestBase::RunPostedTasks() {
+  base::RunLoop loop;
+  base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, loop.QuitClosure());
+  loop.Run();
+}
+
 //
 // SitePerProcessBrowserTest
 //
@@ -8442,6 +8448,10 @@
                               GlobalRoutingID(process1->GetID(), routing_id1)));
   EXPECT_FALSE(base::Contains(web_contents()->pending_widgets_,
                               GlobalRoutingID(process2->GetID(), routing_id2)));
+
+  // There are posted tasks that must be run before the test shuts down, lest
+  // they access deleted state.
+  RunPostedTasks();
 }
 #endif
 
diff --git a/content/browser/site_per_process_browsertest.h b/content/browser/site_per_process_browsertest.h
index f07db60..105c813 100644
--- a/content/browser/site_per_process_browsertest.h
+++ b/content/browser/site_per_process_browsertest.h
@@ -37,6 +37,8 @@
       FrameTreeNode* frame_tree_node,
       const blink::mojom::ViewportIntersectionState& intersection_state);
 
+  void RunPostedTasks();
+
  private:
   FrameTreeVisualizer visualizer_;
   base::test::ScopedFeatureList feature_list_;
diff --git a/content/browser/site_per_process_hit_test_browsertest.cc b/content/browser/site_per_process_hit_test_browsertest.cc
index cf5ab229..4101d0265 100644
--- a/content/browser/site_per_process_hit_test_browsertest.cc
+++ b/content/browser/site_per_process_hit_test_browsertest.cc
@@ -5995,6 +5995,10 @@
   // diverted to a different RenderWidgetHostView due to mouse capture.
   EXPECT_TRUE(popup_monitor.EventWasReceived());
 #endif  // defined(OS_LINUX) || defined(OS_CHROMEOS)
+
+  // There are posted tasks that must be run before the test shuts down, lest
+  // they access deleted state.
+  RunPostedTasks();
 }
 
 // Test that clicking a select element in a nested out-of-process iframe creates
@@ -6122,6 +6126,10 @@
     EXPECT_EQ(popup_rect.y() - rwhv_root->GetViewBounds().y(), 248);
   }
 #endif
+
+  // There are posted tasks that must be run before the test shuts down, lest
+  // they access deleted state.
+  RunPostedTasks();
 }
 
 // Verify that scrolling the main frame correctly updates the position to
@@ -6235,6 +6243,10 @@
   popup_waiter->Wait();
   EXPECT_EQ(unscrolled_popup_rect.y(),
             popup_waiter->last_initial_rect().y() + 20);
+
+  // There are posted tasks that must be run before the test shuts down, lest
+  // they access deleted state.
+  RunPostedTasks();
 }
 #endif  // !defined(OS_ANDROID)
 
diff --git a/content/browser/web_contents/web_contents_impl.cc b/content/browser/web_contents/web_contents_impl.cc
index 0ce7aa7b..621e92d 100644
--- a/content/browser/web_contents/web_contents_impl.cc
+++ b/content/browser/web_contents/web_contents_impl.cc
@@ -8674,11 +8674,8 @@
                           dict.Add("old", old_state);
                           dict.Add("new", new_state);
                         });
-  if (render_frame_host->GetParent())
-    return;
 
-  if (old_state == LifecycleState::kActive &&
-      new_state != LifecycleState::kActive) {
+  if (old_state == LifecycleState::kActive && !render_frame_host->GetParent()) {
     // TODO(sreejakshetty): Remove this reset when ColorChooser becomes
     // per-frame.
     // Close the color chooser popup when RenderFrameHost changes state from
diff --git a/content/browser/webid/federated_auth_request_impl.cc b/content/browser/webid/federated_auth_request_impl.cc
index c5cdf39..9cb9eb2 100644
--- a/content/browser/webid/federated_auth_request_impl.cc
+++ b/content/browser/webid/federated_auth_request_impl.cc
@@ -441,12 +441,8 @@
 
 void FederatedAuthRequestImpl::OnLogoutCompleted(
     IdpNetworkRequestManager::LogoutResponse status) {
-  // Return an error overall for the call if any one request returns an error.
-  if (logout_status_ == blink::mojom::LogoutStatus::kSuccess &&
-      status != IdpNetworkRequestManager::LogoutResponse::kSuccess) {
-    logout_status_ = blink::mojom::LogoutStatus::kError;
-  }
-
+  // |status| is deliberately ignored because we don't want to tell the
+  // calling page whether this cross-origin load succeeded or not.
   if (logout_endpoints_.empty()) {
     CompleteLogoutRequest(logout_status_);
     return;
diff --git a/content/browser/webid/federated_auth_request_impl_unittest.cc b/content/browser/webid/federated_auth_request_impl_unittest.cc
index 8b77db3..46cbe93 100644
--- a/content/browser/webid/federated_auth_request_impl_unittest.cc
+++ b/content/browser/webid/federated_auth_request_impl_unittest.cc
@@ -507,13 +507,13 @@
   // Expectations have to be set explicitly in advance using
   // logout_return_status() and logout_endpoints().
   void SetLogoutMockExpectations() {
-    static int count = 0;
+    logout_count_ = 0;
     EXPECT_CALL(*mock_request_manager_, SendLogout(_, _))
         .Times(logout_endpoints_.size())
         .WillRepeatedly(
             Invoke([&](const GURL& logout_endpoint,
                        IdpNetworkRequestManager::LogoutCallback callback) {
-              std::move(callback).Run(logout_return_status_[count++]);
+              std::move(callback).Run(logout_return_status_[logout_count_++]);
             }));
   }
 
@@ -535,6 +535,7 @@
   // Test case storage for Logout tests.
   std::vector<LogoutResponse> logout_return_status_;
   std::vector<std::string> logout_endpoints_;
+  int logout_count_;
 
   GURL provider_;
 };
@@ -603,7 +604,7 @@
 
   SetLogoutMockExpectations();
   auto logout_response = PerformLogoutRequest(logout_endpoints());
-  EXPECT_EQ(logout_response, LogoutStatus::kError);
+  EXPECT_EQ(logout_response, LogoutStatus::kSuccess);
 }
 
 // Test Logout method with an empty endpoint vector.
diff --git a/content/browser/xr/service/xr_device_service.cc b/content/browser/xr/service/xr_device_service.cc
index b239d79..4b727e4 100644
--- a/content/browser/xr/service/xr_device_service.cc
+++ b/content/browser/xr/service/xr_device_service.cc
@@ -8,7 +8,6 @@
 #include "base/no_destructor.h"
 #include "build/build_config.h"
 #include "content/browser/service_sandbox_type.h"
-#include "content/public/browser/browser_task_traits.h"
 #include "content/public/browser/gpu_client.h"
 #include "content/public/browser/service_process_host.h"
 #include "mojo/public/cpp/bindings/self_owned_receiver.h"
@@ -33,8 +32,7 @@
   // process.
   void BindGpu(::mojo::PendingReceiver<::viz::mojom::Gpu> receiver) override {
     gpu_client_ =
-        content::CreateGpuClient(std::move(receiver), base::DoNothing(),
-                                 content::GetIOThreadTaskRunner({}));
+        content::CreateGpuClient(std::move(receiver), base::DoNothing());
   }
 
  private:
@@ -43,12 +41,6 @@
   std::unique_ptr<viz::GpuClient, base::OnTaskRunnerDeleter> gpu_client_;
 };
 
-void BindHost(
-    mojo::PendingReceiver<device::mojom::XRDeviceServiceHost> receiver) {
-  mojo::MakeSelfOwnedReceiver(std::make_unique<XRDeviceServiceHostImpl>(),
-                              std::move(receiver));
-}
-
 }  // namespace
 
 const mojo::Remote<device::mojom::XRDeviceService>& GetXRDeviceService() {
@@ -79,14 +71,10 @@
 
 mojo::PendingRemote<device::mojom::XRDeviceServiceHost>
 CreateXRDeviceServiceHost() {
-  // XRDeviceServiceHostImpl doesn't need to live on the IO thread but GpuClient
-  // does and it will own GpuClient. Might as well have them both live on the IO
-  // thread.
   mojo::PendingRemote<device::mojom::XRDeviceServiceHost> device_service_host;
-  content::GetIOThreadTaskRunner({})->PostTask(
-      FROM_HERE,
-      base::BindOnce(&BindHost,
-                     device_service_host.InitWithNewPipeAndPassReceiver()));
+  mojo::MakeSelfOwnedReceiver(
+      std::make_unique<XRDeviceServiceHostImpl>(),
+      device_service_host.InitWithNewPipeAndPassReceiver());
 
   return device_service_host;
 }
diff --git a/content/public/browser/gpu_client.h b/content/public/browser/gpu_client.h
index b9f751c..7c566de 100644
--- a/content/public/browser/gpu_client.h
+++ b/content/public/browser/gpu_client.h
@@ -17,8 +17,7 @@
 CONTENT_EXPORT
 std::unique_ptr<viz::GpuClient, base::OnTaskRunnerDeleter> CreateGpuClient(
     mojo::PendingReceiver<viz::mojom::Gpu> receiver,
-    viz::GpuClient::ConnectionErrorHandlerClosure connection_error_handler,
-    scoped_refptr<base::SingleThreadTaskRunner> task_runner);
+    viz::GpuClient::ConnectionErrorHandlerClosure connection_error_handler);
 
 }  // namespace content
 
diff --git a/content/public/browser/render_document_host_user_data.h b/content/public/browser/render_document_host_user_data.h
index bcde6ac..3666dc8 100644
--- a/content/public/browser/render_document_host_user_data.h
+++ b/content/public/browser/render_document_host_user_data.h
@@ -72,10 +72,11 @@
 template <typename T>
 class RenderDocumentHostUserData : public base::SupportsUserData::Data {
  public:
-  static void CreateForCurrentDocument(RenderFrameHost* rfh) {
+  template <typename... Args>
+  static void CreateForCurrentDocument(RenderFrameHost* rfh, Args&&... args) {
     DCHECK(rfh);
     if (!GetForCurrentDocument(rfh)) {
-      T* data = new T(rfh);
+      T* data = new T(rfh, std::forward<Args>(args)...);
       SetRenderDocumentHostUserData(rfh, UserDataKey(), base::WrapUnique(data));
     }
   }
diff --git a/content/public/test/mock_web_contents_observer.cc b/content/public/test/mock_web_contents_observer.cc
new file mode 100644
index 0000000..cc24803
--- /dev/null
+++ b/content/public/test/mock_web_contents_observer.cc
@@ -0,0 +1,13 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "content/public/test/mock_web_contents_observer.h"
+
+namespace content {
+
+MockWebContentsObserver::MockWebContentsObserver(WebContents* web_contents)
+    : WebContentsObserver(web_contents) {}
+MockWebContentsObserver::~MockWebContentsObserver() = default;
+
+}  // namespace content
diff --git a/content/public/test/mock_web_contents_observer.h b/content/public/test/mock_web_contents_observer.h
new file mode 100644
index 0000000..f37cc15a44
--- /dev/null
+++ b/content/public/test/mock_web_contents_observer.h
@@ -0,0 +1,372 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CONTENT_PUBLIC_TEST_MOCK_WEB_CONTENTS_OBSERVER_H_
+#define CONTENT_PUBLIC_TEST_MOCK_WEB_CONTENTS_OBSERVER_H_
+
+#include "content/public/browser/ax_event_notification_details.h"
+#include "content/public/browser/web_contents_observer.h"
+#include "testing/gmock/include/gmock/gmock.h"
+
+namespace content {
+
+// A mock WebContentsObserver with which tests can set expectations for how
+// observer methods are called in response to actions taken in the test.
+//
+// For example:
+//  GURL url = ...
+//  testing::NiceMock<MockWebContentsObserver> observer(web_contents());
+//  EXPECT_CALL(observer,
+//              DidStartNavigation(
+//                  testing::Truly([&](NavigationHandle* navigation_handle) {
+//                    return navigation_handle->GetURL() == url;
+//                  })));
+//  EXPECT_TRUE(NavigateToURL(shell(), url));
+//
+class MockWebContentsObserver : public WebContentsObserver {
+ public:
+  explicit MockWebContentsObserver(WebContents* web_contents);
+  ~MockWebContentsObserver() override;
+
+  MOCK_METHOD(void,
+              RenderFrameCreated,
+              (RenderFrameHost* render_frame_host),
+              (override));
+  MOCK_METHOD(void,
+              RenderFrameDeleted,
+              (RenderFrameHost* render_frame_host),
+              (override));
+  MOCK_METHOD(void,
+              RenderFrameHostChanged,
+              (RenderFrameHost* old_host, RenderFrameHost* new_host),
+              (override));
+  MOCK_METHOD(void, FrameDeleted, (int frame_tree_node_id), (override));
+  MOCK_METHOD(void,
+              RenderFrameHostStateChanged,
+              (RenderFrameHost* render_frame_host,
+               RenderFrameHost::LifecycleState old_state,
+               RenderFrameHost::LifecycleState new_state),
+              (override));
+  MOCK_METHOD(void, CaptureTargetChanged, (), (override));
+  MOCK_METHOD(void, RenderViewReady, (), (override));
+  MOCK_METHOD(void,
+              RenderViewDeleted,
+              (RenderViewHost* render_view_host),
+              (override));
+  MOCK_METHOD(void,
+              RenderProcessGone,
+              (base::TerminationStatus status),
+              (override));
+  MOCK_METHOD(void,
+              RenderViewHostChanged,
+              (RenderViewHost* old_host, RenderViewHost* new_host),
+              (override));
+  MOCK_METHOD(void,
+              OnRendererUnresponsive,
+              (RenderProcessHost* render_process_host),
+              (override));
+  MOCK_METHOD(void,
+              OnRendererResponsive,
+              (RenderProcessHost* render_process_host),
+              (override));
+  MOCK_METHOD(void,
+              DidStartNavigation,
+              (NavigationHandle* navigation_handle),
+              (override));
+  MOCK_METHOD(void,
+              DidRedirectNavigation,
+              (NavigationHandle* navigation_handle),
+              (override));
+  MOCK_METHOD(void,
+              ReadyToCommitNavigation,
+              (NavigationHandle* navigation_handle),
+              (override));
+  MOCK_METHOD(void,
+              DidFinishNavigation,
+              (NavigationHandle* navigation_handle),
+              (override));
+  MOCK_METHOD(void,
+              DidActivatePortal,
+              (WebContents* predecessor_web_contents,
+               base::TimeTicks activation_time),
+              (override));
+  MOCK_METHOD(void, DidStartLoading, (), (override));
+  MOCK_METHOD(void, DidReceiveResponse, (), (override));
+  MOCK_METHOD(void, DidStopLoading, (), (override));
+  MOCK_METHOD(void, LoadProgressChanged, (double progress), (override));
+  MOCK_METHOD(void,
+              DocumentAvailableInMainFrame,
+              (RenderFrameHost* render_frame_host),
+              (override));
+  MOCK_METHOD(void,
+              DocumentOnLoadCompletedInMainFrame,
+              (RenderFrameHost* render_frame_host),
+              (override));
+  MOCK_METHOD(void,
+              DOMContentLoaded,
+              (RenderFrameHost* render_frame_host),
+              (override));
+  MOCK_METHOD(void,
+              DidFinishLoad,
+              (RenderFrameHost* render_frame_host, const GURL& validated_url),
+              (override));
+  MOCK_METHOD(void,
+              DidFailLoad,
+              (RenderFrameHost* render_frame_host,
+               const GURL& validated_url,
+               int error_code),
+              (override));
+  MOCK_METHOD(void, DidChangeVisibleSecurityState, (), (override));
+  MOCK_METHOD(void,
+              DidLoadResourceFromMemoryCache,
+              (RenderFrameHost* render_frame_host,
+               const GURL& url,
+               const std::string& mime_type,
+               network::mojom::RequestDestination request_destination),
+              (override));
+  MOCK_METHOD(void,
+              ResourceLoadComplete,
+              (RenderFrameHost* render_frame_host,
+               const GlobalRequestID& request_id,
+               const blink::mojom::ResourceLoadInfo& resource_load_info),
+              (override));
+  MOCK_METHOD(void,
+              OnCookiesAccessed,
+              (RenderFrameHost* render_frame_host,
+               const CookieAccessDetails& details),
+              (override));
+  MOCK_METHOD(void,
+              OnCookiesAccessed,
+              (NavigationHandle* navigation_handle,
+               const CookieAccessDetails& details),
+              (override));
+  MOCK_METHOD(void,
+              NavigationEntryCommitted,
+              (const LoadCommittedDetails& load_details),
+              (override));
+  MOCK_METHOD(void,
+              NavigationListPruned,
+              (const PrunedDetails& pruned_details),
+              (override));
+  MOCK_METHOD(void, NavigationEntriesDeleted, (), (override));
+  MOCK_METHOD(void,
+              NavigationEntryChanged,
+              (const EntryChangedDetails& change_details),
+              (override));
+  MOCK_METHOD(void,
+              DidOpenRequestedURL,
+              (WebContents* new_contents,
+               RenderFrameHost* source_render_frame_host,
+               const GURL& url,
+               const Referrer& referrer,
+               WindowOpenDisposition disposition,
+               ui::PageTransition transition,
+               bool started_from_context_menu,
+               bool renderer_initiated),
+              (override));
+  MOCK_METHOD(void, DidFirstVisuallyNonEmptyPaint, (), (override));
+  MOCK_METHOD(void, NavigationStopped, (), (override));
+  MOCK_METHOD(void,
+              DidGetUserInteraction,
+              (const blink::WebInputEvent& event),
+              (override));
+  MOCK_METHOD(void, DidGetIgnoredUIEvent, (), (override));
+  MOCK_METHOD(void, OnVisibilityChanged, (Visibility visibility), (override));
+  MOCK_METHOD(void, MainFrameWasResized, (bool width_changed), (override));
+  MOCK_METHOD(void,
+              FrameNameChanged,
+              (RenderFrameHost* render_frame_host, const std::string& name),
+              (override));
+  MOCK_METHOD(void,
+              FrameReceivedUserActivation,
+              (RenderFrameHost* render_frame_host),
+              (override));
+  MOCK_METHOD(void,
+              FrameDisplayStateChanged,
+              (RenderFrameHost* render_frame_host, bool is_display_none),
+              (override));
+  MOCK_METHOD(void,
+              FrameSizeChanged,
+              (RenderFrameHost* render_frame_host,
+               const gfx::Size& frame_size),
+              (override));
+  MOCK_METHOD(void, TitleWasSet, (NavigationEntry* entry), (override));
+  MOCK_METHOD(void,
+              AppCacheAccessed,
+              (const GURL& manifest_url, bool blocked_by_policy),
+              (override));
+  MOCK_METHOD(void, PepperInstanceCreated, (), (override));
+  MOCK_METHOD(void, PepperInstanceDeleted, (), (override));
+  MOCK_METHOD(void,
+              ViewportFitChanged,
+              (blink::mojom::ViewportFit value),
+              (override));
+  MOCK_METHOD(void,
+              PluginCrashed,
+              (const base::FilePath& plugin_path, base::ProcessId plugin_pid),
+              (override));
+  MOCK_METHOD(void,
+              PluginHungStatusChanged,
+              (int plugin_child_id,
+               const base::FilePath& plugin_path,
+               bool is_hung),
+              (override));
+  MOCK_METHOD(void,
+              InnerWebContentsCreated,
+              (WebContents* inner_web_contents),
+              (override));
+  MOCK_METHOD(void,
+              InnerWebContentsAttached,
+              (WebContents* inner_web_contents,
+               RenderFrameHost* render_frame_host,
+               bool is_full_page),
+              (override));
+  MOCK_METHOD(void,
+              InnerWebContentsDetached,
+              (WebContents* inner_web_contents),
+              (override));
+  MOCK_METHOD(void,
+              DidCloneToNewWebContents,
+              (WebContents* old_web_contents, WebContents* new_web_contents),
+              (override));
+  MOCK_METHOD(void, WebContentsDestroyed, (), (override));
+  MOCK_METHOD(void,
+              UserAgentOverrideSet,
+              (const blink::UserAgentOverride& ua_override),
+              (override));
+  MOCK_METHOD(void,
+              DidUpdateFaviconURL,
+              (RenderFrameHost* render_frame_host,
+               const std::vector<blink::mojom::FaviconURLPtr>& candidates),
+              (override));
+  MOCK_METHOD(void, OnAudioStateChanged, (bool audible), (override));
+  MOCK_METHOD(void,
+              OnFrameAudioStateChanged,
+              (RenderFrameHost* rfh, bool audible),
+              (override));
+  MOCK_METHOD(void,
+              OnIsConnectedToBluetoothDeviceChanged,
+              (bool is_connected_to_bluetooth_device),
+              (override));
+  MOCK_METHOD(void, DidUpdateAudioMutingState, (bool muted), (override));
+  MOCK_METHOD(void,
+              DidToggleFullscreenModeForTab,
+              (bool entered_fullscreen, bool will_cause_resize),
+              (override));
+  MOCK_METHOD(void, DidAcquireFullscreen, (RenderFrameHost* rfh), (override));
+  MOCK_METHOD(void,
+              DidChangeVerticalScrollDirection,
+              (viz::VerticalScrollDirection scroll_direction),
+              (override));
+  MOCK_METHOD(void, BeforeFormRepostWarningShow, (), (override));
+  MOCK_METHOD(void,
+              BeforeUnloadFired,
+              (bool proceed, const base::TimeTicks& proceed_time),
+              (override));
+  MOCK_METHOD(void, BeforeUnloadDialogCancelled, (), (override));
+  MOCK_METHOD(void, AXTreeIDForMainFrameHasChanged, (), (override));
+  MOCK_METHOD(void,
+              AccessibilityEventReceived,
+              (const AXEventNotificationDetails& details),
+              (override));
+  MOCK_METHOD(void,
+              AccessibilityLocationChangesReceived,
+              (const std::vector<AXLocationChangeNotificationDetails>& details),
+              (override));
+  MOCK_METHOD(void, DidChangeThemeColor, (), (override));
+  MOCK_METHOD(void, OnBackgroundColorChanged, (), (override));
+  MOCK_METHOD(void,
+              OnDidAddMessageToConsole,
+              (RenderFrameHost* source_frame,
+               blink::mojom::ConsoleMessageLevel log_level,
+               const std::u16string& message,
+               int32_t line_no,
+               const std::u16string& source_id,
+               const base::Optional<std::u16string>& untrusted_stack_trace),
+              (override));
+  MOCK_METHOD(void,
+              MediaStartedPlaying,
+              (const MediaPlayerInfo& video_type, const MediaPlayerId& id),
+              (override));
+  MOCK_METHOD(void,
+              MediaStoppedPlaying,
+              (const MediaPlayerInfo& video_type,
+               const MediaPlayerId& id,
+               WebContentsObserver::MediaStoppedReason reason),
+              (override));
+  MOCK_METHOD(void,
+              MediaResized,
+              (const gfx::Size& size, const MediaPlayerId& id),
+              (override));
+  MOCK_METHOD(void,
+              MediaEffectivelyFullscreenChanged,
+              (bool is_fullscreen),
+              (override));
+  MOCK_METHOD(void,
+              MediaPictureInPictureChanged,
+              (bool is_picture_in_picture),
+              (override));
+  MOCK_METHOD(void,
+              MediaMutedStatusChanged,
+              (const MediaPlayerId& id, bool muted),
+              (override));
+  MOCK_METHOD(void,
+              MediaBufferUnderflow,
+              (const MediaPlayerId& id),
+              (override));
+  MOCK_METHOD(void, MediaPlayerSeek, (const MediaPlayerId& id), (override));
+  MOCK_METHOD(void, MediaDestroyed, (const MediaPlayerId& id), (override));
+  MOCK_METHOD(void,
+              OnPageScaleFactorChanged,
+              (float page_scale_factor),
+              (override));
+  MOCK_METHOD(void, OnPaste, (), (override));
+  MOCK_METHOD(void,
+              OnWebContentsFocused,
+              (RenderWidgetHost* render_widget_host),
+              (override));
+  MOCK_METHOD(void,
+              OnWebContentsLostFocus,
+              (RenderWidgetHost* render_widget_host),
+              (override));
+  MOCK_METHOD(void,
+              OnFocusChangedInPage,
+              (FocusedNodeDetails* details),
+              (override));
+  MOCK_METHOD(void,
+              DidUpdateWebManifestURL,
+              (RenderFrameHost* target_frame,
+               const base::Optional<GURL>& manifest_url),
+              (override));
+  MOCK_METHOD(void,
+              OnInterfaceRequestFromFrame,
+              (RenderFrameHost* render_frame_host,
+               const std::string& interface_name,
+               mojo::ScopedMessagePipeHandle* interface_pipe),
+              (override));
+  MOCK_METHOD(void,
+              AudioContextPlaybackStarted,
+              (const AudioContextId& audio_context_id),
+              (override));
+  MOCK_METHOD(void,
+              AudioContextPlaybackStopped,
+              (const AudioContextId& audio_context_id),
+              (override));
+  MOCK_METHOD(void,
+              OnServiceWorkerAccessed,
+              (RenderFrameHost* render_frame_host,
+               const GURL& scope,
+               AllowServiceWorkerResult allowed),
+              (override));
+  MOCK_METHOD(void,
+              OnServiceWorkerAccessed,
+              (NavigationHandle* navigation_handle,
+               const GURL& scope,
+               AllowServiceWorkerResult allowed),
+              (override));
+};
+
+}  // namespace content
+
+#endif  // CONTENT_PUBLIC_TEST_MOCK_WEB_CONTENTS_OBSERVER_H_
diff --git a/content/shell/browser/shell_platform_delegate_views.cc b/content/shell/browser/shell_platform_delegate_views.cc
index 1bd226f..1c556eec 100644
--- a/content/shell/browser/shell_platform_delegate_views.cc
+++ b/content/shell/browser/shell_platform_delegate_views.cc
@@ -42,6 +42,7 @@
 #if BUILDFLAG(IS_CHROMEOS_ASH)
 #include "ui/wm/test/wm_test_helper.h"
 #else  // !BUILDFLAG(IS_CHROMEOS_ASH)
+#include "ui/display/screen.h"
 #include "ui/views/widget/desktop_aura/desktop_screen.h"
 #include "ui/wm/core/wm_state.h"
 #endif
@@ -64,6 +65,7 @@
   std::unique_ptr<wm::WMTestHelper> wm_test_helper;
 #else
   std::unique_ptr<wm::WMState> wm_state;
+  std::unique_ptr<display::Screen> screen;
 #endif
 
   // TODO(danakj): This looks unused?
@@ -331,7 +333,8 @@
       std::make_unique<wm::WMTestHelper>(default_window_size);
 #else
   platform_->wm_state = std::make_unique<wm::WMState>();
-  views::InstallDesktopScreenIfNecessary();
+  CHECK(!display::Screen::GetScreen());
+  platform_->screen = views::CreateDesktopScreen();
 #endif
 
   platform_->views_delegate =
diff --git a/content/test/BUILD.gn b/content/test/BUILD.gn
index 781c590e..f65a17a2 100644
--- a/content/test/BUILD.gn
+++ b/content/test/BUILD.gn
@@ -181,6 +181,8 @@
     "../public/test/mock_render_thread.h",
     "../public/test/mock_resource_context.cc",
     "../public/test/mock_resource_context.h",
+    "../public/test/mock_web_contents_observer.cc",
+    "../public/test/mock_web_contents_observer.h",
     "../public/test/multiple_pages_per_webcontents_helper.cc",
     "../public/test/multiple_pages_per_webcontents_helper.h",
     "../public/test/navigation_handle_observer.cc",
diff --git a/content/test/gpu/gpu_tests/test_expectations/info_collection_expectations.txt b/content/test/gpu/gpu_tests/test_expectations/info_collection_expectations.txt
index 7f242c9..0598a50 100644
--- a/content/test/gpu/gpu_tests/test_expectations/info_collection_expectations.txt
+++ b/content/test/gpu/gpu_tests/test_expectations/info_collection_expectations.txt
@@ -64,3 +64,5 @@
 # NVIDIA bots need 456.38+ drivers to use SOFTWARE overlays.
 crbug.com/1113893 [ win nvidia ] InfoCollection_direct_composition [ Failure ]
 
+# InfoCollection_basic is flaky on Fuchsia.
+crbug.com/1154597 [ fuchsia ] InfoCollection_basic [ RetryOnFailure ]
diff --git a/docs/testing/chromeos_debugging_tips.md b/docs/testing/chromeos_debugging_tips.md
index 9cc3892..26f5927 100644
--- a/docs/testing/chromeos_debugging_tips.md
+++ b/docs/testing/chromeos_debugging_tips.md
@@ -41,13 +41,18 @@
 
 There a couple ways to disable a test on Chrome's builders:
 - **With a full CrOS checkout**: If you have a full CrOS checkout, you can add
-the `informational` attribute to the test's definition (see [Tast attributes]
-for more info). This can take time (ie: many hours) to land and propagate onto
-Chrome's builders. So if you need the test disabled ASAP, consult the next
-option.
+the `informational` [attribute] to the test's definition. (You may be able to
+bypass the need for a full CrOS checkout by using the `Edit code` button in
+codesearch UI, but this flow is unverified.) This can take time (ie: many hours)
+to land and propagate onto Chrome's builders. So if you need the test disabled
+ASAP, consult the next option.
 - **With only a Chromium checkout**: You can also add the test to the list of
 disabled tests for the step's GN target. For example, to disable a test in the
-`chrome_all_tast_tests` step, add it to [this list].
+`chrome_all_tast_tests` step, add it to [this list]. **Note**: If the test is
+failing consistently, and you only disable it here, it will likely start to fail
+in the next [Chrome uprev] on CrOS's builders, which can lead to further
+problems down the road. So please make sure you pursue the first option as well
+in that case.
 
 In both cases, please make sure a bug is filed for the test, and route it to
 the appropriate owners.
@@ -114,7 +119,7 @@
 >TODO: Add instructions for rerunning these tests locally.
 
 
-[linux-chromeos]: https://chromium.googlesource.com/chromium/src/+/master/docs/chromeos_build_instructions.md
+[linux-chromeos]: https://chromium.googlesource.com/chromium/src/+/HEAD/docs/chromeos_build_instructions.md
 [Tast]: https://chromium.googlesource.com/chromiumos/platform/tast/+/HEAD/README.md
 [failed build]: https://ci.chromium.org/p/chromium/builders/ci/chromeos-kevin-rel/29791
 [ui.WindowControl]: https://logs.chromium.org/logs/chromium/buildbucket/cr-buildbucket.appspot.com/8865053459542681936/+/steps/chrome_all_tast_tests_on_ChromeOS/0/logs/Deterministic_failure:_ui.WindowControl__status_FAILURE_/0
@@ -122,8 +127,9 @@
 [shard #0 isolated out]: https://isolateserver.appspot.com/browse?namespace=default-gzip&hash=3d35c273195f640c69b1cf0d15d19d9868e3f593
 [tests/ui.WindowControl/messages]: https://isolateserver.appspot.com/browse?namespace=default-gzip&digest=baefbcfd24c02b3ada4617d259dc6b4220b413b9&as=messages
 [system_logs/chrome/chrome_20201029-195153]: https://isolateserver.appspot.com/browse?namespace=default-gzip&digest=272166c85f190c336a9885f0267cbdea912e31da&as=chrome_20201029-195153
-[Tast attributes]: https://chromium.googlesource.com/chromiumos/platform/tast/+/HEAD/docs/test_attributes.md
+[attribute]: https://chromium.googlesource.com/chromiumos/platform/tast/+/HEAD/docs/test_attributes.md
 [this list]: https://codesearch.chromium.org/chromium/src/chromeos/tast_control.gni
-[Simple Chrome]: https://chromium.googlesource.com/chromiumos/docs/+/master/simple_chrome_workflow.md
-[deploy_chrome.py]: https://chromium.googlesource.com/chromiumos/docs/+/master/simple_chrome_workflow.md#Deploying-Chrome-to-the-device
-[here]: https://chromium.googlesource.com/chromiumos/docs/+/master/cros_vm.md#in-simple-chrome
+[Chrome uprev]: https://chromium.googlesource.com/chromiumos/docs/+/HEAD/chrome_commit_pipeline.md#the-chrome-os-commit-pipeline-for-chrome-changes
+[Simple Chrome]: https://chromium.googlesource.com/chromiumos/docs/+/HEAD/simple_chrome_workflow.md
+[deploy_chrome.py]: https://chromium.googlesource.com/chromiumos/docs/+/HEAD/simple_chrome_workflow.md#Deploying-Chrome-to-the-device
+[here]: https://chromium.googlesource.com/chromiumos/docs/+/HEAD/cros_vm.md#in-simple-chrome
diff --git a/extensions/browser/api/storage/storage_api.cc b/extensions/browser/api/storage/storage_api.cc
index 35efd5575..ee06084c 100644
--- a/extensions/browser/api/storage/storage_api.cc
+++ b/extensions/browser/api/storage/storage_api.cc
@@ -22,95 +22,15 @@
 
 namespace extensions {
 
-// SettingsFunction
-
-SettingsFunction::SettingsFunction()
-    : settings_namespace_(settings_namespace::INVALID) {}
-
-SettingsFunction::~SettingsFunction() {}
-
-bool SettingsFunction::ShouldSkipQuotaLimiting() const {
-  // Only apply quota if this is for sync storage.
-  std::string settings_namespace_string;
-  if (!args_->GetString(0, &settings_namespace_string)) {
-    // This should be EXTENSION_FUNCTION_VALIDATE(false) but there is no way
-    // to signify that from this function. It will be caught in Run().
-    return false;
-  }
-  return settings_namespace_string != "sync";
-}
-
-ExtensionFunction::ResponseAction SettingsFunction::Run() {
-  std::string settings_namespace_string;
-  EXTENSION_FUNCTION_VALIDATE(args_->GetString(0, &settings_namespace_string));
-  args_->Remove(0, NULL);
-  settings_namespace_ =
-      settings_namespace::FromString(settings_namespace_string);
-  EXTENSION_FUNCTION_VALIDATE(settings_namespace_ !=
-                              settings_namespace::INVALID);
-
-  if (extension()->is_login_screen_extension() &&
-      settings_namespace_ != settings_namespace::MANAGED) {
-    // Login screen extensions are not allowed to use local/sync storage for
-    // security reasons (see crbug.com/978443).
-    return RespondNow(Error(base::StringPrintf(
-        "\"%s\" is not available for login screen extensions",
-        settings_namespace_string.c_str())));
-  }
-
-  StorageFrontend* frontend = StorageFrontend::Get(browser_context());
-  if (!frontend->IsStorageEnabled(settings_namespace_)) {
-    return RespondNow(Error(
-        base::StringPrintf("\"%s\" is not available in this instance of Chrome",
-                           settings_namespace_string.c_str())));
-  }
-
-  observers_ = frontend->GetObservers();
-  frontend->RunWithStorage(
-      extension(), settings_namespace_,
-      base::BindOnce(&SettingsFunction::AsyncRunWithStorage, this));
-  return RespondLater();
-}
-
-void SettingsFunction::AsyncRunWithStorage(ValueStore* storage) {
-  ResponseValue response = RunWithStorage(storage);
-  content::GetUIThreadTaskRunner({})->PostTask(
-      FROM_HERE,
-      base::BindOnce(&SettingsFunction::Respond, this, std::move(response)));
-}
-
-ExtensionFunction::ResponseValue SettingsFunction::UseReadResult(
-    ValueStore::ReadResult result) {
-  TRACE_EVENT2("browser", "SettingsFunction::UseReadResult", "extension_id",
-               extension_id(), "namespace", settings_namespace_);
-  if (!result.status().ok())
-    return Error(result.status().message);
-
-  std::unique_ptr<base::DictionaryValue> dict(new base::DictionaryValue());
-  dict->Swap(&result.settings());
-  return OneArgument(base::Value::FromUniquePtrValue(std::move(dict)));
-}
-
-ExtensionFunction::ResponseValue SettingsFunction::UseWriteResult(
-    ValueStore::WriteResult result) {
-  TRACE_EVENT2("browser", "SettingsFunction::UseWriteResult", "extension_id",
-               extension_id(), "namespace", settings_namespace_);
-  if (!result.status().ok())
-    return Error(result.status().message);
-
-  if (!result.changes().empty()) {
-    observers_->Notify(FROM_HERE, &SettingsObserver::OnSettingsChanged,
-                       extension_id(), settings_namespace_,
-                       ValueStoreChange::ToValue(result.PassChanges()));
-  }
-
-  return NoArguments();
-}
-
 // Concrete settings functions
 
 namespace {
 
+constexpr char kLocalString[] = "local";
+constexpr char kSyncString[] = "sync";
+constexpr char kManagedString[] = "managed";
+constexpr char kSessionString[] = "session";
+
 // Adds all StringValues from a ListValue to a vector of strings.
 void AddAllStringValues(const base::ListValue& from,
                         std::vector<std::string>* to) {
@@ -151,8 +71,131 @@
       "MAX_WRITE_OPERATIONS_PER_HOUR"));
 }
 
+// Returns the settings namespace of `storage_area`, or `Namespace::INVALID` if
+// the StorageArea doesn't map to one.
+settings_namespace::Namespace StorageAreaToSettingsNamespace(
+    StorageAreaNamespace storage_area) {
+  switch (storage_area) {
+    case StorageAreaNamespace::kLocal:
+      return settings_namespace::LOCAL;
+    case StorageAreaNamespace::kSync:
+      return settings_namespace::SYNC;
+    case StorageAreaNamespace::kManaged:
+      return settings_namespace::MANAGED;
+    case StorageAreaNamespace::kSession:
+      return settings_namespace::INVALID;
+    case StorageAreaNamespace::kInvalid:
+      NOTREACHED();
+      return settings_namespace::INVALID;
+  }
+}
+
+// Returns the StorageArea of `storage_area_string`, or
+// `StorageAreaNamespace::kInvalid` if the string doesn't map to one.
+StorageAreaNamespace StorageAreaFromString(
+    const std::string& storage_area_string) {
+  if (storage_area_string == kLocalString)
+    return StorageAreaNamespace::kLocal;
+  if (storage_area_string == kSyncString)
+    return StorageAreaNamespace::kSync;
+  if (storage_area_string == kManagedString)
+    return StorageAreaNamespace::kManaged;
+  if (storage_area_string == kSessionString)
+    return StorageAreaNamespace::kSession;
+  return StorageAreaNamespace::kInvalid;
+}
+
 }  // namespace
 
+// SettingsFunction
+
+SettingsFunction::SettingsFunction() = default;
+
+SettingsFunction::~SettingsFunction() = default;
+
+bool SettingsFunction::ShouldSkipQuotaLimiting() const {
+  // Only apply quota if this is for sync storage.
+  std::string storage_area_string;
+  if (!args_->GetString(0, &storage_area_string)) {
+    // This should be EXTENSION_FUNCTION_VALIDATE(false) but there is no way
+    // to signify that from this function. It will be caught in Run().
+    return false;
+  }
+  return storage_area_string != kSyncString;
+}
+
+ExtensionFunction::ResponseAction SettingsFunction::Run() {
+  std::string storage_area_string;
+  EXTENSION_FUNCTION_VALIDATE(args_->GetString(0, &storage_area_string));
+  args_->Remove(0, nullptr);
+  storage_area_ = StorageAreaFromString(storage_area_string);
+  EXTENSION_FUNCTION_VALIDATE(storage_area_ != StorageAreaNamespace::kInvalid);
+  settings_namespace_ = StorageAreaToSettingsNamespace(storage_area_);
+  // TODO(emiliapaz): Currently we only have sync, local and managed implemented
+  // in this function. This check will change after we introduce `session`.
+  EXTENSION_FUNCTION_VALIDATE(
+      settings_namespace_ == settings_namespace::SYNC ||
+      settings_namespace_ == settings_namespace::LOCAL ||
+      settings_namespace_ == settings_namespace::MANAGED);
+
+  if (extension()->is_login_screen_extension() &&
+      storage_area_ != StorageAreaNamespace::kManaged) {
+    // Login screen extensions are not allowed to use local/sync storage for
+    // security reasons (see crbug.com/978443).
+    return RespondNow(Error(base::StringPrintf(
+        "\"%s\" is not available for login screen extensions",
+        storage_area_string.c_str())));
+  }
+
+  StorageFrontend* frontend = StorageFrontend::Get(browser_context());
+  if (!frontend->IsStorageEnabled(settings_namespace_)) {
+    return RespondNow(Error(
+        base::StringPrintf("\"%s\" is not available in this instance of Chrome",
+                           storage_area_string.c_str())));
+  }
+
+  observers_ = frontend->GetObservers();
+  frontend->RunWithStorage(
+      extension(), settings_namespace_,
+      base::BindOnce(&SettingsFunction::AsyncRunWithStorage, this));
+  return RespondLater();
+}
+
+void SettingsFunction::AsyncRunWithStorage(ValueStore* storage) {
+  ResponseValue response = RunWithStorage(storage);
+  content::GetUIThreadTaskRunner({})->PostTask(
+      FROM_HERE,
+      base::BindOnce(&SettingsFunction::Respond, this, std::move(response)));
+}
+
+ExtensionFunction::ResponseValue SettingsFunction::UseReadResult(
+    ValueStore::ReadResult result) {
+  TRACE_EVENT2("browser", "SettingsFunction::UseReadResult", "extension_id",
+               extension_id(), "namespace", storage_area_);
+  if (!result.status().ok())
+    return Error(result.status().message);
+
+  std::unique_ptr<base::DictionaryValue> dict(new base::DictionaryValue());
+  dict->Swap(&result.settings());
+  return OneArgument(base::Value::FromUniquePtrValue(std::move(dict)));
+}
+
+ExtensionFunction::ResponseValue SettingsFunction::UseWriteResult(
+    ValueStore::WriteResult result) {
+  TRACE_EVENT2("browser", "SettingsFunction::UseWriteResult", "extension_id",
+               extension_id(), "namespace", storage_area_);
+  if (!result.status().ok())
+    return Error(result.status().message);
+
+  if (!result.changes().empty()) {
+    observers_->Notify(FROM_HERE, &SettingsObserver::OnSettingsChanged,
+                       extension_id(), settings_namespace_,
+                       ValueStoreChange::ToValue(result.PassChanges()));
+  }
+
+  return NoArguments();
+}
+
 ExtensionFunction::ResponseValue StorageStorageAreaGetFunction::RunWithStorage(
     ValueStore* storage) {
   TRACE_EVENT1("browser", "StorageStorageAreaGetFunction::RunWithStorage",
diff --git a/extensions/browser/api/storage/storage_api.h b/extensions/browser/api/storage/storage_api.h
index fce167d..547afeae 100644
--- a/extensions/browser/api/storage/storage_api.h
+++ b/extensions/browser/api/storage/storage_api.h
@@ -16,6 +16,15 @@
 
 namespace extensions {
 
+// Enumerates all the namespaces of the storage areas.
+enum class StorageAreaNamespace {
+  kLocal,    // "local"    i.e. chrome.storage.local
+  kSync,     // "sync"     i.e. chrome.storage.sync
+  kManaged,  // "managed"  i.e. chrome.storage.managed
+  kSession,  // "session"  i.e. chrome.storage.session
+  kInvalid,
+};
+
 // Superclass of all settings functions.
 class SettingsFunction : public ExtensionFunction {
  protected:
@@ -46,9 +55,14 @@
   // SendResponse with its success value.
   void AsyncRunWithStorage(ValueStore* storage);
 
-  // The settings namespace the call was for.  For example, SYNC if the API
-  // call was chrome.settings.experimental.sync..., LOCAL if .local, etc.
-  settings_namespace::Namespace settings_namespace_;
+  // The Storage Area the call was for. For example: kLocal if the API call was
+  // chrome.storage.local, kSync if the API call was chrome.storage.sync, etc.
+  StorageAreaNamespace storage_area_ = StorageAreaNamespace::kInvalid;
+
+  // The settings namespace the call was for. Only includes
+  // StorageAreaNamespace's that use ValueStore.
+  settings_namespace::Namespace settings_namespace_ =
+      settings_namespace::INVALID;
 
   // Observers, cached so that it's only grabbed from the UI thread.
   scoped_refptr<SettingsObserverList> observers_;
diff --git a/extensions/browser/value_store/settings_namespace.h b/extensions/browser/value_store/settings_namespace.h
index 3101bde..83224029 100644
--- a/extensions/browser/value_store/settings_namespace.h
+++ b/extensions/browser/value_store/settings_namespace.h
@@ -11,7 +11,7 @@
 
 namespace settings_namespace {
 
-// The namespaces of the storage areas.
+// The namespaces of the storage areas that have ValueStore.
 enum Namespace {
   LOCAL,    // "local"    i.e. chrome.storage.local
   SYNC,     // "sync"     i.e. chrome.storage.sync
diff --git a/extensions/common/extension.cc b/extensions/common/extension.cc
index d08655b..3e6b0b2c 100644
--- a/extensions/common/extension.cc
+++ b/extensions/common/extension.cc
@@ -18,7 +18,6 @@
 #include "base/files/file_path.h"
 #include "base/i18n/rtl.h"
 #include "base/json/json_writer.h"
-#include "base/logging.h"
 #include "base/memory/singleton.h"
 #include "base/stl_util.h"
 #include "base/strings/strcat.h"
@@ -58,15 +57,18 @@
 
 namespace {
 
-const int kModernManifestVersion = 2;
-const int kPEMOutputColumns = 64;
+constexpr int kModernManifestVersion = 2;
+constexpr int kMaximumSupportedManifestVersion = 3;
+constexpr int kPEMOutputColumns = 64;
+static_assert(kMaximumSupportedManifestVersion >= kModernManifestVersion,
+              "The modern manifest version must be supported.");
 
 // KEY MARKERS
-const char kKeyBeginHeaderMarker[] = "-----BEGIN";
-const char kKeyBeginFooterMarker[] = "-----END";
-const char kKeyInfoEndMarker[] = "KEY-----";
-const char kPublic[] = "PUBLIC";
-const char kPrivate[] = "PRIVATE";
+constexpr char kKeyBeginHeaderMarker[] = "-----BEGIN";
+constexpr char kKeyBeginFooterMarker[] = "-----END";
+constexpr char kKeyInfoEndMarker[] = "KEY-----";
+constexpr char kPublic[] = "PUBLIC";
+constexpr char kPrivate[] = "PRIVATE";
 
 bool ContainsReservedCharacters(const base::FilePath& path) {
   // We should disallow backslash '\\' as file path separator even on Windows,
@@ -86,10 +88,6 @@
                          Manifest::Type type,
                          int creation_flags,
                          std::string* warning) {
-  static constexpr int kMaximumSupportedManifestVersion = 3;
-  static_assert(kMaximumSupportedManifestVersion >= kModernManifestVersion,
-                "The modern manifest version must be supported.");
-
   // The ultimate short-circuit: If the feature for MV3 is disabled, it's not
   // supported.
   if (manifest_version == 3 &&
@@ -178,6 +176,26 @@
   return true;
 }
 
+std::u16string InvalidManifestVersionError(const char* manifest_version_error,
+                                           bool is_platform_app) {
+  std::string valid_version;
+  if (kModernManifestVersion == kMaximumSupportedManifestVersion) {
+    valid_version = base::NumberToString(kModernManifestVersion);
+  } else if (kMaximumSupportedManifestVersion - kModernManifestVersion == 1) {
+    valid_version = base::StrCat(
+        {"either ", base::NumberToString(kModernManifestVersion), " or ",
+         base::NumberToString(kMaximumSupportedManifestVersion)});
+  } else {
+    valid_version = base::StrCat(
+        {"between ", base::NumberToString(kModernManifestVersion), " and ",
+         base::NumberToString(kMaximumSupportedManifestVersion)});
+  }
+
+  return ErrorUtils::FormatErrorMessageUTF16(
+      manifest_version_error, valid_version,
+      is_platform_app ? "apps" : "extensions");
+}
+
 }  // namespace
 
 const int Extension::kInitFromValueFlagBits = 15;
@@ -760,11 +778,13 @@
 bool Extension::LoadManifestVersion(std::u16string* error) {
   // Get the original value out of the dictionary so that we can validate it
   // more strictly.
-  if (manifest_->available_values().HasKey(keys::kManifestVersion)) {
+  bool key_exists =
+      manifest_->available_values().HasKey(keys::kManifestVersion);
+  if (key_exists) {
     int manifest_version = 1;
-    if (!manifest_->GetInteger(keys::kManifestVersion, &manifest_version) ||
-        manifest_version < 1) {
-      *error = base::ASCIIToUTF16(errors::kInvalidManifestVersion);
+    if (!manifest_->GetInteger(keys::kManifestVersion, &manifest_version)) {
+      *error = InvalidManifestVersionError(
+          errors::kInvalidManifestVersionUnsupported, is_platform_app());
       return false;
     }
   }
@@ -776,10 +796,10 @@
     std::string json;
     base::JSONWriter::Write(*manifest_->value(), &json);
     LOG(WARNING) << "Failed to load extension.  Manifest JSON: " << json;
-    *error = ErrorUtils::FormatErrorMessageUTF16(
-        errors::kInvalidManifestVersionOld,
-        base::NumberToString(kModernManifestVersion),
-        is_platform_app() ? "apps" : "extensions");
+    *error = InvalidManifestVersionError(
+        key_exists ? errors::kInvalidManifestVersionUnsupported
+                   : errors::kInvalidManifestVersionMissingKey,
+        is_platform_app());
     return false;
   }
 
diff --git a/extensions/common/manifest_constants.cc b/extensions/common/manifest_constants.cc
index 090ab28..d370fbc 100644
--- a/extensions/common/manifest_constants.cc
+++ b/extensions/common/manifest_constants.cc
@@ -489,12 +489,12 @@
     "Invalid 'app.linked_icons'. Must be an array";
 const char kInvalidManifest[] = "Manifest file is invalid";
 const char kInvalidManifestKey[] = "Invalid value for '*'.";
-const char kInvalidManifestVersion[] =
-    "Invalid value for 'manifest_version'. Must be an integer greater than "
-    "zero.";
-const char kInvalidManifestVersionOld[] =
-    "The 'manifest_version' key must be present and set to * (without quotes). "
-    "See developer.chrome.com/*/manifestVersion.html for details.";
+const char kInvalidManifestVersionMissingKey[] =
+    "Missing 'manifest_version' key. Its value must be an integer *. "
+    "See developer.chrome.com/*/manifestVersion for details.";
+const char kInvalidManifestVersionUnsupported[] =
+    "Invalid value for 'manifest_version'. Must be an integer *. "
+    "See developer.chrome.com/*/manifestVersion for details.";
 const char kInvalidMatch[] =
     "Invalid value for 'content_scripts[*].matches[*]': *";
 const char kInvalidMatchCount[] =
diff --git a/extensions/common/manifest_constants.h b/extensions/common/manifest_constants.h
index abb6cfe..b42da5be 100644
--- a/extensions/common/manifest_constants.h
+++ b/extensions/common/manifest_constants.h
@@ -364,8 +364,8 @@
 extern const char kInvalidLinkedAppIcons[];
 extern const char kInvalidManifest[];
 extern const char kInvalidManifestKey[];
-extern const char kInvalidManifestVersion[];
-extern const char kInvalidManifestVersionOld[];
+extern const char kInvalidManifestVersionMissingKey[];
+extern const char kInvalidManifestVersionUnsupported[];
 extern const char kInvalidMatch[];
 extern const char kInvalidMatchCount[];
 extern const char kInvalidMatches[];
diff --git a/extensions/shell/browser/shell_desktop_controller_aura.cc b/extensions/shell/browser/shell_desktop_controller_aura.cc
index 7cb6715..a0dc584 100644
--- a/extensions/shell/browser/shell_desktop_controller_aura.cc
+++ b/extensions/shell/browser/shell_desktop_controller_aura.cc
@@ -322,7 +322,7 @@
     display::Screen::SetScreenInstance(screen_.get());
 #else
     // TODO(crbug.com/756680): Refactor DesktopScreen out of views.
-    screen_.reset(views::CreateDesktopScreen());
+    screen_ = views::CreateDesktopScreen();
 #endif
   }
 
diff --git a/infra/config/generated/cr-buildbucket.cfg b/infra/config/generated/cr-buildbucket.cfg
index 6f973804..350d948f 100644
--- a/infra/config/generated/cr-buildbucket.cfg
+++ b/infra/config/generated/cr-buildbucket.cfg
@@ -772,7 +772,7 @@
       dimensions: "builderless:1"
       dimensions: "cores:8"
       dimensions: "cpu:x86-64"
-      dimensions: "os:Ubuntu-16.04"
+      dimensions: "os:Ubuntu-18.04"
       dimensions: "pool:luci.chromium.gpu.ci"
       dimensions: "ssd:0"
       exe {
@@ -834,7 +834,7 @@
       dimensions: "builderless:1"
       dimensions: "cores:8"
       dimensions: "cpu:x86-64"
-      dimensions: "os:Ubuntu-16.04"
+      dimensions: "os:Ubuntu-18.04"
       dimensions: "pool:luci.chromium.gpu.ci"
       dimensions: "ssd:0"
       exe {
@@ -958,7 +958,7 @@
       dimensions: "builderless:1"
       dimensions: "cores:8"
       dimensions: "cpu:x86-64"
-      dimensions: "os:Ubuntu-16.04"
+      dimensions: "os:Ubuntu-18.04"
       dimensions: "pool:luci.chromium.gpu.ci"
       dimensions: "ssd:0"
       exe {
@@ -1020,7 +1020,7 @@
       dimensions: "builderless:1"
       dimensions: "cores:8"
       dimensions: "cpu:x86-64"
-      dimensions: "os:Ubuntu-16.04"
+      dimensions: "os:Ubuntu-18.04"
       dimensions: "pool:luci.chromium.gpu.ci"
       dimensions: "ssd:0"
       exe {
@@ -1082,7 +1082,7 @@
       dimensions: "builderless:1"
       dimensions: "cores:8"
       dimensions: "cpu:x86-64"
-      dimensions: "os:Ubuntu-16.04"
+      dimensions: "os:Ubuntu-18.04"
       dimensions: "pool:luci.chromium.gpu.ci"
       dimensions: "ssd:0"
       exe {
@@ -1144,7 +1144,7 @@
       dimensions: "builderless:1"
       dimensions: "cores:8"
       dimensions: "cpu:x86-64"
-      dimensions: "os:Ubuntu-16.04"
+      dimensions: "os:Ubuntu-18.04"
       dimensions: "pool:luci.chromium.gpu.ci"
       dimensions: "ssd:0"
       exe {
@@ -1206,7 +1206,7 @@
       dimensions: "builderless:1"
       dimensions: "cores:8"
       dimensions: "cpu:x86-64"
-      dimensions: "os:Ubuntu-16.04"
+      dimensions: "os:Ubuntu-18.04"
       dimensions: "pool:luci.chromium.gpu.ci"
       dimensions: "ssd:0"
       exe {
@@ -1268,7 +1268,7 @@
       dimensions: "builderless:1"
       dimensions: "cores:8"
       dimensions: "cpu:x86-64"
-      dimensions: "os:Ubuntu-16.04"
+      dimensions: "os:Ubuntu-18.04"
       dimensions: "pool:luci.chromium.gpu.ci"
       dimensions: "ssd:0"
       exe {
@@ -1330,7 +1330,7 @@
       dimensions: "builderless:1"
       dimensions: "cores:8"
       dimensions: "cpu:x86-64"
-      dimensions: "os:Ubuntu-16.04"
+      dimensions: "os:Ubuntu-18.04"
       dimensions: "pool:luci.chromium.gpu.ci"
       dimensions: "ssd:0"
       exe {
@@ -1392,7 +1392,7 @@
       dimensions: "builderless:1"
       dimensions: "cores:8"
       dimensions: "cpu:x86-64"
-      dimensions: "os:Ubuntu-16.04"
+      dimensions: "os:Ubuntu-18.04"
       dimensions: "pool:luci.chromium.gpu.ci"
       dimensions: "ssd:0"
       exe {
@@ -1454,7 +1454,7 @@
       dimensions: "builderless:1"
       dimensions: "cores:8"
       dimensions: "cpu:x86-64"
-      dimensions: "os:Ubuntu-16.04"
+      dimensions: "os:Ubuntu-18.04"
       dimensions: "pool:luci.chromium.gpu.ci"
       dimensions: "ssd:0"
       exe {
@@ -1516,7 +1516,7 @@
       dimensions: "builderless:1"
       dimensions: "cores:8"
       dimensions: "cpu:x86-64"
-      dimensions: "os:Ubuntu-16.04"
+      dimensions: "os:Ubuntu-18.04"
       dimensions: "pool:luci.chromium.gpu.ci"
       dimensions: "ssd:0"
       exe {
@@ -1578,7 +1578,7 @@
       dimensions: "builderless:1"
       dimensions: "cores:8"
       dimensions: "cpu:x86-64"
-      dimensions: "os:Ubuntu-16.04"
+      dimensions: "os:Ubuntu-18.04"
       dimensions: "pool:luci.chromium.gpu.ci"
       dimensions: "ssd:0"
       exe {
@@ -1640,7 +1640,7 @@
       dimensions: "builderless:1"
       dimensions: "cores:8"
       dimensions: "cpu:x86-64"
-      dimensions: "os:Ubuntu-16.04"
+      dimensions: "os:Ubuntu-18.04"
       dimensions: "pool:luci.chromium.gpu.ci"
       dimensions: "ssd:0"
       exe {
@@ -1702,7 +1702,7 @@
       dimensions: "builderless:1"
       dimensions: "cores:8"
       dimensions: "cpu:x86-64"
-      dimensions: "os:Ubuntu-16.04"
+      dimensions: "os:Ubuntu-18.04"
       dimensions: "pool:luci.chromium.gpu.ci"
       dimensions: "ssd:0"
       exe {
@@ -2822,7 +2822,7 @@
       dimensions: "builderless:1"
       dimensions: "cores:8"
       dimensions: "cpu:x86-64"
-      dimensions: "os:Ubuntu-16.04"
+      dimensions: "os:Ubuntu-18.04"
       dimensions: "pool:luci.chromium.gpu.ci"
       dimensions: "ssd:0"
       exe {
@@ -2884,7 +2884,7 @@
       dimensions: "builderless:1"
       dimensions: "cores:8"
       dimensions: "cpu:x86-64"
-      dimensions: "os:Ubuntu-16.04"
+      dimensions: "os:Ubuntu-18.04"
       dimensions: "pool:luci.chromium.gpu.ci"
       dimensions: "ssd:0"
       exe {
@@ -3132,7 +3132,7 @@
       dimensions: "builderless:1"
       dimensions: "cores:8"
       dimensions: "cpu:x86-64"
-      dimensions: "os:Ubuntu-16.04"
+      dimensions: "os:Ubuntu-18.04"
       dimensions: "pool:luci.chromium.gpu.ci"
       dimensions: "ssd:0"
       exe {
@@ -3194,7 +3194,7 @@
       dimensions: "builderless:1"
       dimensions: "cores:8"
       dimensions: "cpu:x86-64"
-      dimensions: "os:Ubuntu-16.04"
+      dimensions: "os:Ubuntu-18.04"
       dimensions: "pool:luci.chromium.gpu.ci"
       dimensions: "ssd:0"
       exe {
@@ -5120,7 +5120,7 @@
       dimensions: "builderless:1"
       dimensions: "cores:8"
       dimensions: "cpu:x86-64"
-      dimensions: "os:Ubuntu-16.04"
+      dimensions: "os:Ubuntu-18.04"
       dimensions: "pool:luci.chromium.gpu.ci"
       dimensions: "ssd:0"
       exe {
@@ -5182,7 +5182,7 @@
       dimensions: "builderless:1"
       dimensions: "cores:8"
       dimensions: "cpu:x86-64"
-      dimensions: "os:Ubuntu-16.04"
+      dimensions: "os:Ubuntu-18.04"
       dimensions: "pool:luci.chromium.gpu.ci"
       dimensions: "ssd:0"
       exe {
@@ -5244,7 +5244,7 @@
       dimensions: "builderless:1"
       dimensions: "cores:8"
       dimensions: "cpu:x86-64"
-      dimensions: "os:Ubuntu-16.04"
+      dimensions: "os:Ubuntu-18.04"
       dimensions: "pool:luci.chromium.gpu.ci"
       dimensions: "ssd:0"
       exe {
@@ -5306,7 +5306,7 @@
       dimensions: "builderless:1"
       dimensions: "cores:8"
       dimensions: "cpu:x86-64"
-      dimensions: "os:Ubuntu-16.04"
+      dimensions: "os:Ubuntu-18.04"
       dimensions: "pool:luci.chromium.gpu.ci"
       dimensions: "ssd:0"
       exe {
@@ -5496,7 +5496,7 @@
       dimensions: "builderless:1"
       dimensions: "cores:8"
       dimensions: "cpu:x86-64"
-      dimensions: "os:Ubuntu-16.04"
+      dimensions: "os:Ubuntu-18.04"
       dimensions: "pool:luci.chromium.gpu.ci"
       dimensions: "ssd:0"
       exe {
@@ -5930,7 +5930,7 @@
       dimensions: "builderless:1"
       dimensions: "cores:8"
       dimensions: "cpu:x86-64"
-      dimensions: "os:Ubuntu-16.04"
+      dimensions: "os:Ubuntu-18.04"
       dimensions: "pool:luci.chromium.gpu.ci"
       dimensions: "ssd:0"
       exe {
@@ -5992,7 +5992,7 @@
       dimensions: "builderless:1"
       dimensions: "cores:8"
       dimensions: "cpu:x86-64"
-      dimensions: "os:Ubuntu-16.04"
+      dimensions: "os:Ubuntu-18.04"
       dimensions: "pool:luci.chromium.gpu.ci"
       dimensions: "ssd:0"
       exe {
@@ -7361,7 +7361,7 @@
       dimensions: "builderless:1"
       dimensions: "cores:8"
       dimensions: "cpu:x86-64"
-      dimensions: "os:Ubuntu-16.04"
+      dimensions: "os:Ubuntu-18.04"
       dimensions: "pool:luci.chromium.ci"
       dimensions: "ssd:1"
       exe {
@@ -7423,7 +7423,7 @@
       dimensions: "builderless:1"
       dimensions: "cores:8"
       dimensions: "cpu:x86-64"
-      dimensions: "os:Ubuntu-16.04"
+      dimensions: "os:Ubuntu-18.04"
       dimensions: "pool:luci.chromium.ci"
       dimensions: "ssd:0"
       exe {
@@ -8481,7 +8481,7 @@
       dimensions: "builderless:1"
       dimensions: "cores:8"
       dimensions: "cpu:x86-64"
-      dimensions: "os:Ubuntu-16.04"
+      dimensions: "os:Ubuntu-18.04"
       dimensions: "pool:luci.chromium.gpu.ci"
       dimensions: "ssd:0"
       exe {
@@ -8791,7 +8791,7 @@
       dimensions: "builderless:1"
       dimensions: "cores:8"
       dimensions: "cpu:x86-64"
-      dimensions: "os:Ubuntu-16.04"
+      dimensions: "os:Ubuntu-18.04"
       dimensions: "pool:luci.chromium.gpu.ci"
       dimensions: "ssd:0"
       exe {
@@ -18257,7 +18257,7 @@
       dimensions: "builderless:1"
       dimensions: "cores:8"
       dimensions: "cpu:x86-64"
-      dimensions: "os:Ubuntu-16.04"
+      dimensions: "os:Ubuntu-18.04"
       dimensions: "pool:luci.chromium.gpu.ci"
       dimensions: "ssd:0"
       exe {
@@ -18381,7 +18381,7 @@
       dimensions: "builderless:1"
       dimensions: "cores:8"
       dimensions: "cpu:x86-64"
-      dimensions: "os:Ubuntu-16.04"
+      dimensions: "os:Ubuntu-18.04"
       dimensions: "pool:luci.chromium.gpu.ci"
       dimensions: "ssd:0"
       exe {
@@ -18505,7 +18505,7 @@
       dimensions: "builderless:1"
       dimensions: "cores:8"
       dimensions: "cpu:x86-64"
-      dimensions: "os:Ubuntu-16.04"
+      dimensions: "os:Ubuntu-18.04"
       dimensions: "pool:luci.chromium.gpu.ci"
       dimensions: "ssd:0"
       exe {
@@ -18629,7 +18629,7 @@
       dimensions: "builderless:1"
       dimensions: "cores:8"
       dimensions: "cpu:x86-64"
-      dimensions: "os:Ubuntu-16.04"
+      dimensions: "os:Ubuntu-18.04"
       dimensions: "pool:luci.chromium.gpu.ci"
       dimensions: "ssd:0"
       exe {
@@ -18753,7 +18753,7 @@
       dimensions: "builderless:1"
       dimensions: "cores:8"
       dimensions: "cpu:x86-64"
-      dimensions: "os:Ubuntu-16.04"
+      dimensions: "os:Ubuntu-18.04"
       dimensions: "pool:luci.chromium.gpu.ci"
       dimensions: "ssd:0"
       exe {
@@ -22345,7 +22345,7 @@
       dimensions: "builderless:1"
       dimensions: "cores:8"
       dimensions: "cpu:x86-64"
-      dimensions: "os:Ubuntu-16.04"
+      dimensions: "os:Ubuntu-18.04"
       dimensions: "pool:luci.chromium.gpu.ci"
       dimensions: "ssd:0"
       exe {
@@ -24234,7 +24234,7 @@
       dimensions: "builderless:1"
       dimensions: "cores:8"
       dimensions: "cpu:x86-64"
-      dimensions: "os:Ubuntu-16.04"
+      dimensions: "os:Ubuntu-18.04"
       dimensions: "pool:luci.chromium.gpu.ci"
       dimensions: "ssd:0"
       exe {
@@ -24296,7 +24296,7 @@
       dimensions: "builderless:1"
       dimensions: "cores:8"
       dimensions: "cpu:x86-64"
-      dimensions: "os:Ubuntu-16.04"
+      dimensions: "os:Ubuntu-18.04"
       dimensions: "pool:luci.chromium.gpu.ci"
       dimensions: "ssd:0"
       exe {
@@ -26589,7 +26589,7 @@
       dimensions: "builderless:1"
       dimensions: "cores:8"
       dimensions: "cpu:x86-64"
-      dimensions: "os:Ubuntu-16.04"
+      dimensions: "os:Ubuntu-18.04"
       dimensions: "pool:luci.chromium.gpu.ci"
       dimensions: "ssd:0"
       exe {
@@ -26651,7 +26651,7 @@
       dimensions: "builderless:1"
       dimensions: "cores:8"
       dimensions: "cpu:x86-64"
-      dimensions: "os:Ubuntu-16.04"
+      dimensions: "os:Ubuntu-18.04"
       dimensions: "pool:luci.chromium.gpu.ci"
       dimensions: "ssd:0"
       exe {
@@ -26713,7 +26713,7 @@
       dimensions: "builderless:1"
       dimensions: "cores:8"
       dimensions: "cpu:x86-64"
-      dimensions: "os:Ubuntu-16.04"
+      dimensions: "os:Ubuntu-18.04"
       dimensions: "pool:luci.chromium.gpu.ci"
       dimensions: "ssd:0"
       exe {
@@ -26775,7 +26775,7 @@
       dimensions: "builderless:1"
       dimensions: "cores:8"
       dimensions: "cpu:x86-64"
-      dimensions: "os:Ubuntu-16.04"
+      dimensions: "os:Ubuntu-18.04"
       dimensions: "pool:luci.chromium.gpu.ci"
       dimensions: "ssd:0"
       exe {
@@ -48833,7 +48833,7 @@
       dimensions: "builderless:1"
       dimensions: "cores:8"
       dimensions: "cpu:x86-64"
-      dimensions: "os:Ubuntu-16.04|Ubuntu-18.04"
+      dimensions: "os:Ubuntu-18.04"
       dimensions: "pool:luci.chromium.try"
       dimensions: "ssd:1"
       exe {
diff --git a/infra/config/lib/ci.star b/infra/config/lib/ci.star
index 059f01b..7435849 100644
--- a/infra/config/lib/ci.star
+++ b/infra/config/lib/ci.star
@@ -202,12 +202,11 @@
         *,
         name,
         goma_backend = builders.goma.backend.RBE_PROD,
-        os = builders.os.LINUX_DEFAULT,
         **kwargs):
     return angle_builder(
         name = name,
         goma_backend = goma_backend,
-        os = os,
+        os = builders.os.LINUX_BIONIC_SWITCH_TO_DEFAULT,
         pool = "luci.chromium.gpu.ci",
         **kwargs
     )
@@ -234,7 +233,6 @@
         # Setting goma_backend for testers is a no-op, but better to be explicit
         # here and also leave the generated configs unchanged for these testers.
         goma_backend = None,
-        os = builders.os.LINUX_BIONIC_SWITCH_TO_DEFAULT,
         **kwargs
     )
 
@@ -329,13 +327,12 @@
         *,
         name,
         goma_backend = builders.goma.backend.RBE_PROD,
-        os = builders.os.LINUX_DEFAULT,
         **kwargs):
     return dawn_builder(
         name = name,
         builderless = True,
         goma_backend = goma_backend,
-        os = os,
+        os = builders.os.LINUX_BIONIC_SWITCH_TO_DEFAULT,
         pool = "luci.chromium.gpu.ci",
         **kwargs
     )
@@ -361,7 +358,6 @@
         cores = 2,
         # Setting goma_backend for testers is a no-op, but better to be explicit
         goma_backend = None,
-        os = builders.os.LINUX_BIONIC_SWITCH_TO_DEFAULT,
         **kwargs
     )
 
@@ -495,13 +491,12 @@
         name,
         execution_timeout = 6 * time.hour,
         goma_backend = builders.goma.backend.RBE_PROD,
-        os = builders.os.LINUX_DEFAULT,
         **kwargs):
     return gpu_fyi_builder(
         name = name,
         execution_timeout = execution_timeout,
         goma_backend = goma_backend,
-        os = os,
+        os = builders.os.LINUX_BIONIC_SWITCH_TO_DEFAULT,
         pool = "luci.chromium.gpu.ci",
         **kwargs
     )
@@ -531,7 +526,6 @@
         # Setting goma_backend for testers is a no-op, but better to be explicit
         # here and also leave the generated configs unchanged for these testers.
         goma_backend = None,
-        os = builders.os.LINUX_BIONIC_SWITCH_TO_DEFAULT,
         **kwargs
     )
 
@@ -560,13 +554,12 @@
         *,
         name,
         goma_backend = builders.goma.backend.RBE_PROD,
-        os = builders.os.LINUX_DEFAULT,
         **kwargs):
     return gpu_builder(
         name = name,
         builderless = True,
         goma_backend = goma_backend,
-        os = os,
+        os = builders.os.LINUX_BIONIC_SWITCH_TO_DEFAULT,
         pool = "luci.chromium.gpu.ci",
         **kwargs
     )
@@ -590,7 +583,6 @@
         tree_closing = tree_closing,
         # Setting goma_backend for testers is a no-op, but better to be explicit
         goma_backend = None,
-        os = builders.os.LINUX_BIONIC_SWITCH_TO_DEFAULT,
         **kwargs
     )
 
@@ -711,7 +703,7 @@
     return swangle_builder(
         name = name,
         goma_backend = builders.goma.backend.RBE_PROD,
-        os = builders.os.LINUX_DEFAULT,
+        os = builders.os.LINUX_BIONIC_SWITCH_TO_DEFAULT,
         pool = "luci.chromium.gpu.ci",
         **kwargs
     )
diff --git a/infra/config/subprojects/chromium/ci.star b/infra/config/subprojects/chromium/ci.star
index 26fb3002..d219ce7 100644
--- a/infra/config/subprojects/chromium/ci.star
+++ b/infra/config/subprojects/chromium/ci.star
@@ -5419,6 +5419,7 @@
     ),
     cq_mirrors_console_view = "mirrors",
     main_console_view = "main",
+    os = os.LINUX_BIONIC,
     ssd = True,
 )
 
@@ -5432,6 +5433,7 @@
     cq_mirrors_console_view = "mirrors",
     main_console_view = "main",
     triggered_by = ["ci/Linux ASan LSan Builder"],
+    os = os.LINUX_BIONIC,
 )
 
 ci.memory_builder(
diff --git a/infra/config/subprojects/chromium/try.star b/infra/config/subprojects/chromium/try.star
index 38a5f3c..1ace47f4 100644
--- a/infra/config/subprojects/chromium/try.star
+++ b/infra/config/subprojects/chromium/try.star
@@ -1337,7 +1337,7 @@
     ssd = True,
     main_list_view = "try",
     tryjob = try_.job(),
-    os = os.LINUX_XENIAL_OR_BIONIC,
+    os = os.LINUX_BIONIC,
 )
 
 # TODO(crbug.com/1200574): Remove after migration.
diff --git a/ios/chrome/app/main_controller.mm b/ios/chrome/app/main_controller.mm
index d467c037..aed69f78 100644
--- a/ios/chrome/app/main_controller.mm
+++ b/ios/chrome/app/main_controller.mm
@@ -615,12 +615,18 @@
       [self startUpBrowserBasicInitialization];
       break;
     case InitStageSafeMode:
+      [self addPostSafeModeAgents];
       break;
     case InitStageFinal:
       break;
   }
 }
 
+- (void)addPostSafeModeAgents {
+  [self.appState addAgent:[[ContentSuggestionsSchedulerAppAgent alloc] init]];
+  [self.appState addAgent:[[IncognitoUsageAppStateAgent alloc] init]];
+}
+
 #pragma mark - Property implementation.
 
 - (void)setAppState:(AppState*)appState {
@@ -630,8 +636,6 @@
 
   // Create app state agents.
   [appState addAgent:[[AppMetricsAppStateAgent alloc] init]];
-  [appState addAgent:[[ContentSuggestionsSchedulerAppAgent alloc] init]];
-  [appState addAgent:[[IncognitoUsageAppStateAgent alloc] init]];
   [appState addAgent:[[SafeModeAppAgent alloc] init]];
 
   // Create the window accessibility agent only when multuple windows are
diff --git a/ios/chrome/browser/crash_report/breadcrumbs/BUILD.gn b/ios/chrome/browser/crash_report/breadcrumbs/BUILD.gn
index 016cb1e..663a9277 100644
--- a/ios/chrome/browser/crash_report/breadcrumbs/BUILD.gn
+++ b/ios/chrome/browser/crash_report/breadcrumbs/BUILD.gn
@@ -55,9 +55,9 @@
 
   deps = [
     ":breadcrumbs",
-    ":generate_not_user_triggered_actions",
     "//base",
     "//components/breadcrumbs/core",
+    "//components/breadcrumbs/core:generate_not_user_triggered_actions",
     "//ios/chrome/browser/crash_report:crash_report_internal",
     "//ios/chrome/browser/crash_report/breadcrumbs",
   ]
@@ -69,7 +69,6 @@
   deps = [
     ":application_breadcrumbs_logger",
     ":breadcrumbs",
-    ":generate_not_user_triggered_actions",
     "//base/test:test_support",
     "//components/breadcrumbs/core",
     "//ios/chrome/browser:chrome_url_constants",
@@ -96,21 +95,8 @@
 
   sources = [
     "application_breadcrumbs_logger_unittest.mm",
-    "application_breadcrumbs_not_user_action_unittest.mm",
     "breadcrumb_manager_browser_agent_unittest.mm",
     "breadcrumb_manager_tab_helper_unittest.mm",
     "breadcrumb_persistent_storage_manager_unittest.mm",
   ]
 }
-
-action("generate_not_user_triggered_actions") {
-  script = "generate_not_user_triggered_actions.py"
-  sources = [ "//tools/metrics/actions/actions.xml" ]
-  outputs = [ "$target_gen_dir/application_breadcrumbs_not_user_action.inc" ]
-  args = [
-    "--actions",
-    rebase_path(sources[0], root_build_dir),
-    "--output",
-    rebase_path(outputs[0], root_build_dir),
-  ]
-}
diff --git a/ios/chrome/browser/crash_report/breadcrumbs/application_breadcrumbs_logger.mm b/ios/chrome/browser/crash_report/breadcrumbs/application_breadcrumbs_logger.mm
index 234cb95f..f929b95 100644
--- a/ios/chrome/browser/crash_report/breadcrumbs/application_breadcrumbs_logger.mm
+++ b/ios/chrome/browser/crash_report/breadcrumbs/application_breadcrumbs_logger.mm
@@ -6,9 +6,9 @@
 
 #include "base/bind.h"
 #include "base/strings/stringprintf.h"
+#include "components/breadcrumbs/core/application_breadcrumbs_not_user_action.inc"
 #include "components/breadcrumbs/core/breadcrumb_manager.h"
 #include "components/breadcrumbs/core/breadcrumb_persistent_storage_manager.h"
-#include "ios/chrome/browser/crash_report/breadcrumbs/application_breadcrumbs_not_user_action.inc"
 #import "ios/chrome/browser/crash_report/crash_report_helper.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
diff --git a/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/autofill_address_profile/save_address_profile_infobar_banner_interaction_handler.h b/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/autofill_address_profile/save_address_profile_infobar_banner_interaction_handler.h
index bc145bb..cb628ba7 100644
--- a/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/autofill_address_profile/save_address_profile_infobar_banner_interaction_handler.h
+++ b/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/autofill_address_profile/save_address_profile_infobar_banner_interaction_handler.h
@@ -10,7 +10,7 @@
 #import "ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/common/infobar_banner_interaction_handler.h"
 
 namespace autofill {
-class AutofillSaveAddressProfileDelegateIOS;
+class AutofillSaveUpdateAddressProfileDelegateIOS;
 }
 
 // Helper object that updates the model layer for interaction events with the
@@ -29,7 +29,7 @@
 
  private:
   // Returns the SaveAddressProfile delegate from |infobar|.
-  autofill::AutofillSaveAddressProfileDelegateIOS* GetInfobarDelegate(
+  autofill::AutofillSaveUpdateAddressProfileDelegateIOS* GetInfobarDelegate(
       InfoBarIOS* infobar);
 };
 
diff --git a/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/autofill_address_profile/save_address_profile_infobar_banner_interaction_handler.mm b/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/autofill_address_profile/save_address_profile_infobar_banner_interaction_handler.mm
index 1f5a7bf..0e876159 100644
--- a/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/autofill_address_profile/save_address_profile_infobar_banner_interaction_handler.mm
+++ b/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/autofill_address_profile/save_address_profile_infobar_banner_interaction_handler.mm
@@ -5,7 +5,7 @@
 #import "ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/autofill_address_profile/save_address_profile_infobar_banner_interaction_handler.h"
 
 #include "base/check.h"
-#include "components/autofill/core/browser/autofill_save_address_profile_delegate_ios.h"
+#include "components/autofill/core/browser/autofill_save_update_address_profile_delegate_ios.h"
 #include "ios/chrome/browser/infobars/infobar_ios.h"
 #import "ios/chrome/browser/infobars/overlays/infobar_overlay_request_cancel_handler.h"
 #import "ios/chrome/browser/infobars/overlays/infobar_overlay_request_inserter.h"
@@ -60,12 +60,12 @@
 
 #pragma mark - Private
 
-autofill::AutofillSaveAddressProfileDelegateIOS*
+autofill::AutofillSaveUpdateAddressProfileDelegateIOS*
 SaveAddressProfileInfobarBannerInteractionHandler::GetInfobarDelegate(
     InfoBarIOS* infobar) {
-  autofill::AutofillSaveAddressProfileDelegateIOS* delegate =
-      autofill::AutofillSaveAddressProfileDelegateIOS::FromInfobarDelegate(
-          infobar->delegate());
+  autofill::AutofillSaveUpdateAddressProfileDelegateIOS* delegate =
+      autofill::AutofillSaveUpdateAddressProfileDelegateIOS::
+          FromInfobarDelegate(infobar->delegate());
   DCHECK(delegate);
   return delegate;
 }
diff --git a/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/autofill_address_profile/save_address_profile_infobar_banner_interaction_handler_unittest.mm b/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/autofill_address_profile/save_address_profile_infobar_banner_interaction_handler_unittest.mm
index 2e43e38..dc0de4c 100644
--- a/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/autofill_address_profile/save_address_profile_infobar_banner_interaction_handler_unittest.mm
+++ b/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/autofill_address_profile/save_address_profile_infobar_banner_interaction_handler_unittest.mm
@@ -6,7 +6,7 @@
 
 #include "base/guid.h"
 #include "ios/chrome/browser/infobars/infobar_manager_impl.h"
-#include "ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/test/mock_autofill_save_address_profile_delegate_ios.h"
+#include "ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/test/mock_autofill_save_update_address_profile_delegate_ios.h"
 #import "ios/chrome/browser/infobars/overlays/infobar_overlay_request_inserter.h"
 #import "ios/chrome/browser/infobars/test/fake_infobar_ios.h"
 #include "ios/chrome/browser/infobars/test/mock_infobar_delegate.h"
@@ -26,18 +26,19 @@
         profile_(base::GenerateGUID(), "https://www.example.com/") {
     infobar_ = std::make_unique<InfoBarIOS>(
         InfobarType::kInfobarTypeSaveAutofillAddressProfile,
-        MockAutofillSaveAddressProfileDelegateIOSFactory::
-            CreateMockAutofillSaveAddressProfileDelegateIOSFactory(profile_));
+        MockAutofillSaveUpdateAddressProfileDelegateIOSFactory::
+            CreateMockAutofillSaveUpdateAddressProfileDelegateIOSFactory(
+                profile_));
   }
 
-  MockAutofillSaveAddressProfileDelegateIOS& mock_delegate() {
-    return *static_cast<MockAutofillSaveAddressProfileDelegateIOS*>(
+  MockAutofillSaveUpdateAddressProfileDelegateIOS& mock_delegate() {
+    return *static_cast<MockAutofillSaveUpdateAddressProfileDelegateIOS*>(
         infobar_->delegate());
   }
 
  protected:
   SaveAddressProfileInfobarBannerInteractionHandler handler_;
-  MockAutofillSaveAddressProfileDelegateIOSFactory delegate_factory_;
+  MockAutofillSaveUpdateAddressProfileDelegateIOSFactory delegate_factory_;
   autofill::AutofillProfile profile_;
   std::unique_ptr<InfoBarIOS> infobar_;
 };
diff --git a/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/autofill_address_profile/save_address_profile_infobar_modal_interaction_handler.h b/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/autofill_address_profile/save_address_profile_infobar_modal_interaction_handler.h
index 3cf0863d..10df2a4 100644
--- a/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/autofill_address_profile/save_address_profile_infobar_modal_interaction_handler.h
+++ b/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/autofill_address_profile/save_address_profile_infobar_modal_interaction_handler.h
@@ -11,7 +11,7 @@
 class InfoBarIOS;
 
 namespace autofill {
-class AutofillSaveAddressProfileDelegateIOS;
+class AutofillSaveUpdateAddressProfileDelegateIOS;
 }
 
 // Helper object that updates the model layer for interaction events with the
@@ -37,7 +37,7 @@
   CreateModalInstaller() override;
 
   // Returns the SaveAddressProfile delegate from |infobar|.
-  autofill::AutofillSaveAddressProfileDelegateIOS* GetInfoBarDelegate(
+  autofill::AutofillSaveUpdateAddressProfileDelegateIOS* GetInfoBarDelegate(
       InfoBarIOS* infobar);
 
   // The Browser passed on initialization.
diff --git a/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/autofill_address_profile/save_address_profile_infobar_modal_interaction_handler.mm b/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/autofill_address_profile/save_address_profile_infobar_modal_interaction_handler.mm
index 2b38148..1f48d847 100644
--- a/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/autofill_address_profile/save_address_profile_infobar_modal_interaction_handler.mm
+++ b/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/autofill_address_profile/save_address_profile_infobar_modal_interaction_handler.mm
@@ -4,7 +4,7 @@
 
 #import "ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/autofill_address_profile/save_address_profile_infobar_modal_interaction_handler.h"
 
-#include "components/autofill/core/browser/autofill_save_address_profile_delegate_ios.h"
+#include "components/autofill/core/browser/autofill_save_update_address_profile_delegate_ios.h"
 #include "ios/chrome/browser/infobars/infobar_ios.h"
 #import "ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/autofill_address_profile/save_address_profile_infobar_modal_overlay_request_callback_installer.h"
 #include "ios/chrome/browser/main/browser.h"
@@ -52,12 +52,12 @@
       SaveAddressProfileInfobarModalOverlayRequestCallbackInstaller>(this);
 }
 
-autofill::AutofillSaveAddressProfileDelegateIOS*
+autofill::AutofillSaveUpdateAddressProfileDelegateIOS*
 SaveAddressProfileInfobarModalInteractionHandler::GetInfoBarDelegate(
     InfoBarIOS* infobar) {
-  autofill::AutofillSaveAddressProfileDelegateIOS* delegate =
-      autofill::AutofillSaveAddressProfileDelegateIOS::FromInfobarDelegate(
-          infobar->delegate());
+  autofill::AutofillSaveUpdateAddressProfileDelegateIOS* delegate =
+      autofill::AutofillSaveUpdateAddressProfileDelegateIOS::
+          FromInfobarDelegate(infobar->delegate());
   DCHECK(delegate);
   return delegate;
 }
diff --git a/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/autofill_address_profile/save_address_profile_infobar_modal_interaction_handler_unittest.mm b/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/autofill_address_profile/save_address_profile_infobar_modal_interaction_handler_unittest.mm
index 701389ce..84b0a4a 100644
--- a/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/autofill_address_profile/save_address_profile_infobar_modal_interaction_handler_unittest.mm
+++ b/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/autofill_address_profile/save_address_profile_infobar_modal_interaction_handler_unittest.mm
@@ -12,7 +12,7 @@
 #include "components/autofill/core/browser/data_model/autofill_profile.h"
 #include "ios/chrome/browser/infobars/infobar_ios.h"
 #import "ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/common/infobar_banner_interaction_handler.h"
-#include "ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/test/mock_autofill_save_address_profile_delegate_ios.h"
+#include "ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/test/mock_autofill_save_update_address_profile_delegate_ios.h"
 #import "ios/chrome/browser/main/test_browser.h"
 #include "ios/web/public/test/web_task_environment.h"
 #include "testing/platform_test.h"
@@ -31,22 +31,23 @@
         profile_(base::GenerateGUID(), "https://www.example.com/") {
     infobar_ = std::make_unique<InfoBarIOS>(
         InfobarType::kInfobarTypeSaveAutofillAddressProfile,
-        MockAutofillSaveAddressProfileDelegateIOSFactory::
-            CreateMockAutofillSaveAddressProfileDelegateIOSFactory(profile_));
+        MockAutofillSaveUpdateAddressProfileDelegateIOSFactory::
+            CreateMockAutofillSaveUpdateAddressProfileDelegateIOSFactory(
+                profile_));
     handler_ =
         std::make_unique<SaveAddressProfileInfobarModalInteractionHandler>(
             &browser_);
   }
 
-  MockAutofillSaveAddressProfileDelegateIOS& mock_delegate() {
-    return *static_cast<MockAutofillSaveAddressProfileDelegateIOS*>(
+  MockAutofillSaveUpdateAddressProfileDelegateIOS& mock_delegate() {
+    return *static_cast<MockAutofillSaveUpdateAddressProfileDelegateIOS*>(
         infobar_->delegate());
   }
 
  protected:
   web::WebTaskEnvironment task_environment_;
   std::unique_ptr<SaveAddressProfileInfobarModalInteractionHandler> handler_;
-  MockAutofillSaveAddressProfileDelegateIOSFactory delegate_factory_;
+  MockAutofillSaveUpdateAddressProfileDelegateIOSFactory delegate_factory_;
   autofill::AutofillProfile profile_;
   std::unique_ptr<InfoBarIOS> infobar_;
   TestBrowser browser_;
diff --git a/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/test/BUILD.gn b/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/test/BUILD.gn
index 26333f5..c7ba1abb0 100644
--- a/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/test/BUILD.gn
+++ b/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/test/BUILD.gn
@@ -5,10 +5,10 @@
 source_set("test") {
   testonly = true
   sources = [
-    "mock_autofill_save_address_profile_delegate_ios.h",
-    "mock_autofill_save_address_profile_delegate_ios.mm",
     "mock_autofill_save_card_infobar_delegate_mobile.h",
     "mock_autofill_save_card_infobar_delegate_mobile.mm",
+    "mock_autofill_save_update_address_profile_delegate_ios.h",
+    "mock_autofill_save_update_address_profile_delegate_ios.mm",
     "mock_infobar_interaction_handler.h",
     "mock_infobar_interaction_handler.mm",
     "mock_save_card_banner_infobar_interaction_handler.h",
diff --git a/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/test/mock_autofill_save_address_profile_delegate_ios.h b/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/test/mock_autofill_save_address_profile_delegate_ios.h
deleted file mode 100644
index 3b31dbc..0000000
--- a/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/test/mock_autofill_save_address_profile_delegate_ios.h
+++ /dev/null
@@ -1,42 +0,0 @@
-// Copyright 2021 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef IOS_CHROME_BROWSER_INFOBARS_OVERLAYS_BROWSER_AGENT_INTERACTION_HANDLERS_TEST_MOCK_AUTOFILL_SAVE_ADDRESS_PROFILE_DELEGATE_IOS_H_
-#define IOS_CHROME_BROWSER_INFOBARS_OVERLAYS_BROWSER_AGENT_INTERACTION_HANDLERS_TEST_MOCK_AUTOFILL_SAVE_ADDRESS_PROFILE_DELEGATE_IOS_H_
-
-#include <memory>
-#include <string>
-
-#include "components/autofill/core/browser/autofill_save_address_profile_delegate_ios.h"
-
-#include "components/autofill/core/browser/autofill_client.h"
-#include "components/autofill/core/browser/data_model/autofill_profile.h"
-#include "testing/gmock/include/gmock/gmock.h"
-
-class MockAutofillSaveAddressProfileDelegateIOS
-    : public autofill::AutofillSaveAddressProfileDelegateIOS {
- public:
-  MockAutofillSaveAddressProfileDelegateIOS(
-      const autofill::AutofillProfile& profile,
-      autofill::AutofillClient::AddressProfileSavePromptCallback callback);
-  ~MockAutofillSaveAddressProfileDelegateIOS() override;
-
-  MOCK_METHOD0(Accept, bool());
-  MOCK_METHOD0(InfoBarDismissed, void());
-};
-
-class MockAutofillSaveAddressProfileDelegateIOSFactory {
- public:
-  MockAutofillSaveAddressProfileDelegateIOSFactory();
-  ~MockAutofillSaveAddressProfileDelegateIOSFactory();
-
-  static std::unique_ptr<MockAutofillSaveAddressProfileDelegateIOS>
-  CreateMockAutofillSaveAddressProfileDelegateIOSFactory(
-      autofill::AutofillProfile profile);
-
- private:
-  autofill::AutofillProfile profile_;
-};
-
-#endif  // IOS_CHROME_BROWSER_INFOBARS_OVERLAYS_BROWSER_AGENT_INTERACTION_HANDLERS_TEST_MOCK_AUTOFILL_SAVE_ADDRESS_PROFILE_DELEGATE_IOS_H_
diff --git a/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/test/mock_autofill_save_address_profile_delegate_ios.mm b/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/test/mock_autofill_save_address_profile_delegate_ios.mm
deleted file mode 100644
index 54051aa9..0000000
--- a/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/test/mock_autofill_save_address_profile_delegate_ios.mm
+++ /dev/null
@@ -1,39 +0,0 @@
-// Copyright 2021 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/test/mock_autofill_save_address_profile_delegate_ios.h"
-
-#if !defined(__has_feature) || !__has_feature(objc_arc)
-#error "This file requires ARC support."
-#endif
-
-#import "base/bind.h"
-#include "base/guid.h"
-#include "components/autofill/core/browser/autofill_test_utils.h"
-
-MockAutofillSaveAddressProfileDelegateIOS::
-    MockAutofillSaveAddressProfileDelegateIOS(
-        const autofill::AutofillProfile& profile,
-        autofill::AutofillClient::AddressProfileSavePromptCallback callback)
-    : AutofillSaveAddressProfileDelegateIOS(profile, std::move(callback)) {}
-
-MockAutofillSaveAddressProfileDelegateIOS::
-    ~MockAutofillSaveAddressProfileDelegateIOS() = default;
-
-#pragma mark - MockAutofillSaveAddressProfileDelegateIOSFactory
-
-MockAutofillSaveAddressProfileDelegateIOSFactory::
-    MockAutofillSaveAddressProfileDelegateIOSFactory()
-    : profile_(base::GenerateGUID(), "https://www.example.com/") {}
-
-MockAutofillSaveAddressProfileDelegateIOSFactory::
-    ~MockAutofillSaveAddressProfileDelegateIOSFactory() = default;
-
-std::unique_ptr<MockAutofillSaveAddressProfileDelegateIOS>
-MockAutofillSaveAddressProfileDelegateIOSFactory::
-    CreateMockAutofillSaveAddressProfileDelegateIOSFactory(
-        autofill::AutofillProfile profile) {
-  return std::make_unique<MockAutofillSaveAddressProfileDelegateIOS>(
-      profile, autofill::AutofillClient::AddressProfileSavePromptCallback());
-}
diff --git a/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/test/mock_autofill_save_update_address_profile_delegate_ios.h b/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/test/mock_autofill_save_update_address_profile_delegate_ios.h
new file mode 100644
index 0000000..bd83364b
--- /dev/null
+++ b/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/test/mock_autofill_save_update_address_profile_delegate_ios.h
@@ -0,0 +1,43 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef IOS_CHROME_BROWSER_INFOBARS_OVERLAYS_BROWSER_AGENT_INTERACTION_HANDLERS_TEST_MOCK_AUTOFILL_SAVE_UPDATE_ADDRESS_PROFILE_DELEGATE_IOS_H_
+#define IOS_CHROME_BROWSER_INFOBARS_OVERLAYS_BROWSER_AGENT_INTERACTION_HANDLERS_TEST_MOCK_AUTOFILL_SAVE_UPDATE_ADDRESS_PROFILE_DELEGATE_IOS_H_
+
+#include <memory>
+#include <string>
+
+#include "components/autofill/core/browser/autofill_save_update_address_profile_delegate_ios.h"
+
+#include "components/autofill/core/browser/autofill_client.h"
+#include "components/autofill/core/browser/data_model/autofill_profile.h"
+#include "testing/gmock/include/gmock/gmock.h"
+
+class MockAutofillSaveUpdateAddressProfileDelegateIOS
+    : public autofill::AutofillSaveUpdateAddressProfileDelegateIOS {
+ public:
+  MockAutofillSaveUpdateAddressProfileDelegateIOS(
+      const autofill::AutofillProfile& profile,
+      const autofill::AutofillProfile* original_profile,
+      autofill::AutofillClient::AddressProfileSavePromptCallback callback);
+  ~MockAutofillSaveUpdateAddressProfileDelegateIOS() override;
+
+  MOCK_METHOD0(Accept, bool());
+  MOCK_METHOD0(InfoBarDismissed, void());
+};
+
+class MockAutofillSaveUpdateAddressProfileDelegateIOSFactory {
+ public:
+  MockAutofillSaveUpdateAddressProfileDelegateIOSFactory();
+  ~MockAutofillSaveUpdateAddressProfileDelegateIOSFactory();
+
+  static std::unique_ptr<MockAutofillSaveUpdateAddressProfileDelegateIOS>
+  CreateMockAutofillSaveUpdateAddressProfileDelegateIOSFactory(
+      autofill::AutofillProfile profile);
+
+ private:
+  autofill::AutofillProfile profile_;
+};
+
+#endif  // IOS_CHROME_BROWSER_INFOBARS_OVERLAYS_BROWSER_AGENT_INTERACTION_HANDLERS_TEST_MOCK_AUTOFILL_SAVE_UPDATE_ADDRESS_PROFILE_DELEGATE_IOS_H_
diff --git a/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/test/mock_autofill_save_update_address_profile_delegate_ios.mm b/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/test/mock_autofill_save_update_address_profile_delegate_ios.mm
new file mode 100644
index 0000000..bd76acc
--- /dev/null
+++ b/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/test/mock_autofill_save_update_address_profile_delegate_ios.mm
@@ -0,0 +1,43 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/test/mock_autofill_save_update_address_profile_delegate_ios.h"
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
+#import "base/bind.h"
+#include "base/guid.h"
+#include "components/autofill/core/browser/autofill_test_utils.h"
+
+MockAutofillSaveUpdateAddressProfileDelegateIOS::
+    MockAutofillSaveUpdateAddressProfileDelegateIOS(
+        const autofill::AutofillProfile& profile,
+        const autofill::AutofillProfile* original_profile,
+        autofill::AutofillClient::AddressProfileSavePromptCallback callback)
+    : AutofillSaveUpdateAddressProfileDelegateIOS(profile,
+                                                  original_profile,
+                                                  std::move(callback)) {}
+
+MockAutofillSaveUpdateAddressProfileDelegateIOS::
+    ~MockAutofillSaveUpdateAddressProfileDelegateIOS() = default;
+
+#pragma mark - MockAutofillSaveUpdateAddressProfileDelegateIOSFactory
+
+MockAutofillSaveUpdateAddressProfileDelegateIOSFactory::
+    MockAutofillSaveUpdateAddressProfileDelegateIOSFactory()
+    : profile_(base::GenerateGUID(), "https://www.example.com/") {}
+
+MockAutofillSaveUpdateAddressProfileDelegateIOSFactory::
+    ~MockAutofillSaveUpdateAddressProfileDelegateIOSFactory() = default;
+
+std::unique_ptr<MockAutofillSaveUpdateAddressProfileDelegateIOS>
+MockAutofillSaveUpdateAddressProfileDelegateIOSFactory::
+    CreateMockAutofillSaveUpdateAddressProfileDelegateIOSFactory(
+        autofill::AutofillProfile profile) {
+  return std::make_unique<MockAutofillSaveUpdateAddressProfileDelegateIOS>(
+      profile, /*original_profile=*/nullptr,
+      autofill::AutofillClient::AddressProfileSavePromptCallback());
+}
diff --git a/ios/chrome/browser/infobars/overlays/infobar_overlay_request_factory_impl_unittest.mm b/ios/chrome/browser/infobars/overlays/infobar_overlay_request_factory_impl_unittest.mm
index e8d3593..608c25f7d 100644
--- a/ios/chrome/browser/infobars/overlays/infobar_overlay_request_factory_impl_unittest.mm
+++ b/ios/chrome/browser/infobars/overlays/infobar_overlay_request_factory_impl_unittest.mm
@@ -14,8 +14,8 @@
 #include "components/password_manager/core/browser/password_form.h"
 #include "components/translate/core/browser/mock_translate_infobar_delegate.h"
 #include "ios/chrome/browser/infobars/infobar_ios.h"
-#include "ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/test/mock_autofill_save_address_profile_delegate_ios.h"
 #include "ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/test/mock_autofill_save_card_infobar_delegate_mobile.h"
+#include "ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/test/mock_autofill_save_update_address_profile_delegate_ios.h"
 #include "ios/chrome/browser/infobars/test/mock_infobar_delegate.h"
 #import "ios/chrome/browser/overlays/public/infobar_banner/confirm_infobar_banner_overlay_request_config.h"
 #import "ios/chrome/browser/overlays/public/infobar_banner/save_address_profile_infobar_banner_overlay_request_config.h"
@@ -177,8 +177,9 @@
   GURL url("https://chromium.test");
   InfoBarIOS infobar(
       InfobarType::kInfobarTypeSaveAutofillAddressProfile,
-      MockAutofillSaveAddressProfileDelegateIOSFactory::
-          CreateMockAutofillSaveAddressProfileDelegateIOSFactory(profile_));
+      MockAutofillSaveUpdateAddressProfileDelegateIOSFactory::
+          CreateMockAutofillSaveUpdateAddressProfileDelegateIOSFactory(
+              profile_));
 
   // Test banner request creation.
   std::unique_ptr<OverlayRequest> banner_request =
diff --git a/ios/chrome/browser/overlays/public/infobar_banner/save_address_profile_infobar_banner_overlay_request_config.h b/ios/chrome/browser/overlays/public/infobar_banner/save_address_profile_infobar_banner_overlay_request_config.h
index 5d3fbe2e..ad12a03 100644
--- a/ios/chrome/browser/overlays/public/infobar_banner/save_address_profile_infobar_banner_overlay_request_config.h
+++ b/ios/chrome/browser/overlays/public/infobar_banner/save_address_profile_infobar_banner_overlay_request_config.h
@@ -17,7 +17,7 @@
 namespace save_address_profile_infobar_overlays {
 
 // Configuration object for OverlayRequests for the banner UI for an InfoBar
-// with a AutofillSaveAddressProfileDelegateIOS.
+// with a AutofillSaveUpdateAddressProfileDelegateIOS.
 class SaveAddressProfileBannerRequestConfig
     : public OverlayRequestConfig<SaveAddressProfileBannerRequestConfig> {
  public:
diff --git a/ios/chrome/browser/overlays/public/infobar_banner/save_address_profile_infobar_banner_overlay_request_config.mm b/ios/chrome/browser/overlays/public/infobar_banner/save_address_profile_infobar_banner_overlay_request_config.mm
index 4990410d..40bc03e 100644
--- a/ios/chrome/browser/overlays/public/infobar_banner/save_address_profile_infobar_banner_overlay_request_config.mm
+++ b/ios/chrome/browser/overlays/public/infobar_banner/save_address_profile_infobar_banner_overlay_request_config.mm
@@ -4,7 +4,7 @@
 
 #import "ios/chrome/browser/overlays/public/infobar_banner/save_address_profile_infobar_banner_overlay_request_config.h"
 
-#include "components/autofill/core/browser/autofill_save_address_profile_delegate_ios.h"
+#include "components/autofill/core/browser/autofill_save_update_address_profile_delegate_ios.h"
 #include "components/infobars/core/infobar.h"
 #include "ios/chrome/browser/infobars/infobar_ios.h"
 #import "ios/chrome/browser/infobars/overlays/infobar_overlay_type.h"
@@ -24,9 +24,9 @@
     infobars::InfoBar* infobar)
     : infobar_(infobar) {
   DCHECK(infobar_);
-  autofill::AutofillSaveAddressProfileDelegateIOS* delegate =
-      autofill::AutofillSaveAddressProfileDelegateIOS::FromInfobarDelegate(
-          infobar_->delegate());
+  autofill::AutofillSaveUpdateAddressProfileDelegateIOS* delegate =
+      autofill::AutofillSaveUpdateAddressProfileDelegateIOS::
+          FromInfobarDelegate(infobar_->delegate());
   message_text_ = delegate->GetMessageText();
   button_label_text_ = delegate->GetMessageActionText();
   message_sub_text_ = delegate->GetMessageDescriptionText();
diff --git a/ios/chrome/browser/overlays/public/infobar_modal/save_address_profile_infobar_modal_overlay_request_config.mm b/ios/chrome/browser/overlays/public/infobar_modal/save_address_profile_infobar_modal_overlay_request_config.mm
index e2ae3458..5c8369d 100644
--- a/ios/chrome/browser/overlays/public/infobar_modal/save_address_profile_infobar_modal_overlay_request_config.mm
+++ b/ios/chrome/browser/overlays/public/infobar_modal/save_address_profile_infobar_modal_overlay_request_config.mm
@@ -6,7 +6,7 @@
 
 #include "base/check.h"
 #include "base/strings/sys_string_conversions.h"
-#include "components/autofill/core/browser/autofill_save_address_profile_delegate_ios.h"
+#include "components/autofill/core/browser/autofill_save_update_address_profile_delegate_ios.h"
 #include "ios/chrome/browser/infobars/infobar_ios.h"
 #import "ios/chrome/browser/overlays/public/common/infobars/infobar_overlay_request_config.h"
 
@@ -22,8 +22,8 @@
     InfoBarIOS* infobar)
     : infobar_(infobar) {
   DCHECK(infobar_);
-  autofill::AutofillSaveAddressProfileDelegateIOS* delegate =
-      static_cast<autofill::AutofillSaveAddressProfileDelegateIOS*>(
+  autofill::AutofillSaveUpdateAddressProfileDelegateIOS* delegate =
+      static_cast<autofill::AutofillSaveUpdateAddressProfileDelegateIOS*>(
           infobar_->delegate());
   profile_ = delegate->GetProfile();
   current_address_profile_saved_ = infobar->accepted();
diff --git a/ios/chrome/browser/policy/reporting/reporting_delegate_factory_ios.h b/ios/chrome/browser/policy/reporting/reporting_delegate_factory_ios.h
index bf1f478f..ef028899 100644
--- a/ios/chrome/browser/policy/reporting/reporting_delegate_factory_ios.h
+++ b/ios/chrome/browser/policy/reporting/reporting_delegate_factory_ios.h
@@ -11,6 +11,7 @@
 
 #include "components/enterprise/browser/reporting/browser_report_generator.h"
 #include "components/enterprise/browser/reporting/profile_report_generator.h"
+#include "components/enterprise/browser/reporting/real_time_report_generator.h"
 #include "components/enterprise/browser/reporting/report_generator.h"
 #include "components/enterprise/browser/reporting/report_scheduler.h"
 
@@ -37,6 +38,9 @@
 
   std::unique_ptr<ReportScheduler::Delegate> GetReportSchedulerDelegate()
       override;
+
+  std::unique_ptr<RealTimeReportGenerator::Delegate>
+  GetRealTimeReportGeneratorDelegate() override;
 };
 
 }  // namespace enterprise_reporting
diff --git a/ios/chrome/browser/policy/reporting/reporting_delegate_factory_ios.mm b/ios/chrome/browser/policy/reporting/reporting_delegate_factory_ios.mm
index 4b9a34b..97fa890 100644
--- a/ios/chrome/browser/policy/reporting/reporting_delegate_factory_ios.mm
+++ b/ios/chrome/browser/policy/reporting/reporting_delegate_factory_ios.mm
@@ -35,4 +35,10 @@
   return std::make_unique<ReportSchedulerIOS>();
 }
 
+std::unique_ptr<RealTimeReportGenerator::Delegate>
+ReportingDelegateFactoryIOS::GetRealTimeReportGeneratorDelegate() {
+  // Using nullptr as the new pipeline is not supported on iOS.
+  return nullptr;
+}
+
 }  // namespace enterprise_reporting
diff --git a/ios/chrome/browser/ui/authentication/signin/consistency_promo_signin/consistency_promo_signin_coordinator.mm b/ios/chrome/browser/ui/authentication/signin/consistency_promo_signin/consistency_promo_signin_coordinator.mm
index b2ca6ac8..01eaf969 100644
--- a/ios/chrome/browser/ui/authentication/signin/consistency_promo_signin/consistency_promo_signin_coordinator.mm
+++ b/ios/chrome/browser/ui/authentication/signin/consistency_promo_signin/consistency_promo_signin_coordinator.mm
@@ -56,8 +56,8 @@
 // Coordinator to select another identity.
 @property(nonatomic, strong)
     ConsistencyAccountChooserCoordinator* accountChooserCoordinator;
-// Sets with the selected identity, when the sign-in workflow is in progress.
-@property(nonatomic, strong) ChromeIdentity* signinIdentity;
+// |self.defaultAccountCoordinator.selectedIdentity|.
+@property(nonatomic, strong, readonly) ChromeIdentity* selectedIdentity;
 
 @end
 
@@ -112,6 +112,12 @@
                                       completion:nil];
 }
 
+#pragma mark - Properties
+
+- (ChromeIdentity*)selectedIdentity {
+  return self.defaultAccountCoordinator.selectedIdentity;
+}
+
 #pragma mark - Private
 
 // Dismisses the bottom sheet view controller.
@@ -135,6 +141,21 @@
                                completionInfo:completionInfo];
 }
 
+// Displays the error panel.
+- (void)displayCookieErrorWithState:(GoogleServiceAuthError::State)errorState {
+  DCHECK(!self.signinErrorCoordinator);
+  [self.defaultAccountCoordinator stopSigninSpinner];
+  self.signinErrorCoordinator = [[ConsistencySigninErrorCoordinator alloc]
+      initWithBaseViewController:self.navigationController
+                         browser:self.browser
+                      errorState:errorState];
+  self.signinErrorCoordinator.delegate = self;
+  [self.signinErrorCoordinator start];
+  [self.navigationController
+      pushViewController:self.signinErrorCoordinator.viewController
+                animated:YES];
+}
+
 #pragma mark - SwipeGesture
 
 // Called when the swipe gesture is active. This method controls the sliding
@@ -174,10 +195,9 @@
 }
 
 - (void)signinWithIdentity:(ChromeIdentity*)identity {
-  DCHECK(!self.signinIdentity);
-  self.signinIdentity = identity;
+  DCHECK([self.selectedIdentity isEqual:identity]);
   [self.defaultAccountCoordinator startSigninSpinner];
-  self.authenticationService->SignIn(self.signinIdentity);
+  self.authenticationService->SignIn(self.selectedIdentity);
   DCHECK(self.authenticationService->IsAuthenticated());
 }
 
@@ -235,51 +255,59 @@
 - (void)consistencyDefaultAccountCoordinatorSignin:
     (ConsistencyDefaultAccountCoordinator*)coordinator {
   DCHECK_EQ(coordinator, self.defaultAccountCoordinator);
-  ChromeIdentity* identity = self.defaultAccountCoordinator.selectedIdentity;
-  [self signinWithIdentity:identity];
+  [self signinWithIdentity:self.selectedIdentity];
 }
 
 #pragma mark - IdentityManagerObserverBridgeDelegate
 
 - (void)onPrimaryAccountChanged:
     (const signin::PrimaryAccountChangeEvent&)event {
-  // Since sign-in UI blocks all other Chrome screens until it is dismissed
-  // an account change event must come from the bottomsheet.
-  // TODO(crbug.com/1081764): Update if sign-in UI becomes non-blocking.
-  DCHECK(event.GetEventTypeFor(signin::ConsentLevel::kSignin) ==
-         signin::PrimaryAccountChangeEvent::Type::kSet);
-  ChromeIdentity* signedInIdentity =
-      self.authenticationService->GetAuthenticatedIdentity();
-  DCHECK([signedInIdentity isEqual:self.signinIdentity]);
+  switch (event.GetEventTypeFor(signin::ConsentLevel::kSignin)) {
+    case signin::PrimaryAccountChangeEvent::Type::kSet: {
+      // Since sign-in UI blocks all other Chrome screens until it is dismissed
+      // an account change event must come from the bottomsheet.
+      // TODO(crbug.com/1081764): Update if sign-in UI becomes non-blocking.
+      ChromeIdentity* signedInIdentity =
+          self.authenticationService->GetAuthenticatedIdentity();
+      DCHECK([signedInIdentity isEqual:self.selectedIdentity]);
+      break;
+    }
+    case signin::PrimaryAccountChangeEvent::Type::kCleared:
+      // Sign out can be triggered from |onAccountsInCookieUpdated:error:|,
+      // if there is cookie fetch error.
+      return;
+    case signin::PrimaryAccountChangeEvent::Type::kNone:
+      return;
+  }
 }
 
 - (void)onAccountsInCookieUpdated:
             (const signin::AccountsInCookieJarInfo&)accountsInCookieJarInfo
                             error:(const GoogleServiceAuthError&)error {
-  DCHECK(self.signinIdentity);
-  [self.defaultAccountCoordinator stopSigninSpinner];
+  DCHECK(!self.accountChooserCoordinator);
+  if (self.signinErrorCoordinator) {
+    // TODO(crbug.com/1204528): This case should not happen, but
+    // |onAccountsInCookieUpdated:error:| can be called twice when there is an
+    // error. Once this bug is fixed, this |if| should be replaced with
+    // |DCHECK(!self.signinErrorCoordinator)|.
+    return;
+  }
+  __weak __typeof(self) weakSelf = self;
   if (error.state() == GoogleServiceAuthError::State::NONE) {
-    __weak __typeof(self) weakSelf = self;
+    [self.defaultAccountCoordinator stopSigninSpinner];
     [self.navigationController
         dismissViewControllerAnimated:YES
                            completion:^() {
                              [weakSelf
                                  finishedWithResult:
                                      SigninCoordinatorResultSuccess
-                                           identity:weakSelf.signinIdentity];
+                                           identity:weakSelf.selectedIdentity];
                            }];
     return;
   }
-  self.signinIdentity = nil;
-  self.signinErrorCoordinator = [[ConsistencySigninErrorCoordinator alloc]
-      initWithBaseViewController:self.navigationController
-                         browser:self.browser
-                      errorState:error.state()];
-  self.signinErrorCoordinator.delegate = self;
-  [self.signinErrorCoordinator start];
-  [self.navigationController
-      pushViewController:self.signinErrorCoordinator.viewController
-                animated:YES];
+  self.authenticationService->SignOut(signin_metrics::ABORT_SIGNIN, false, ^() {
+    [weakSelf displayCookieErrorWithState:error.state()];
+  });
 }
 
 #pragma mark - UINavigationControllerDelegate
diff --git a/ios/chrome/browser/ui/authentication/views/identity_button_control.mm b/ios/chrome/browser/ui/authentication/views/identity_button_control.mm
index 37e4813..f7d0887f 100644
--- a/ios/chrome/browser/ui/authentication/views/identity_button_control.mm
+++ b/ios/chrome/browser/ui/authentication/views/identity_button_control.mm
@@ -185,9 +185,11 @@
 
 - (void)updateArrowDirection {
   UIImage* image = nil;
+  UIColor* tintColor = nil;
   switch (self.arrowDirection) {
     case IdentityButtonControlArrowRight:
       image = [UIImage imageNamed:@"identity_picker_view_arrow_right"];
+      tintColor = [UIColor colorNamed:kTextTertiaryColor];
       break;
     case IdentityButtonControlArrowDown:
       image = [UIImage imageNamed:@"identity_picker_view_arrow_down"];
@@ -196,6 +198,7 @@
   DCHECK(image);
   self.arrowImageView.image =
       [image imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate];
+  self.arrowImageView.tintColor = tintColor;
 }
 
 #pragma mark - UIResponder
diff --git a/ios/chrome/browser/ui/autofill/chrome_autofill_client_ios.mm b/ios/chrome/browser/ui/autofill/chrome_autofill_client_ios.mm
index 9a7a3cab..9de2b774 100644
--- a/ios/chrome/browser/ui/autofill/chrome_autofill_client_ios.mm
+++ b/ios/chrome/browser/ui/autofill/chrome_autofill_client_ios.mm
@@ -14,7 +14,7 @@
 #include "base/stl_util.h"
 #include "base/strings/string_util.h"
 #include "base/strings/utf_string_conversions.h"
-#include "components/autofill/core/browser/autofill_save_address_profile_delegate_ios.h"
+#include "components/autofill/core/browser/autofill_save_update_address_profile_delegate_ios.h"
 #include "components/autofill/core/browser/form_data_importer.h"
 #include "components/autofill/core/browser/logging/log_manager.h"
 #include "components/autofill/core/browser/payments/autofill_credit_card_filling_infobar_delegate_mobile.h"
@@ -328,14 +328,17 @@
   DCHECK(base::FeatureList::IsEnabled(
       features::kAutofillAddressProfileSavePrompt));
   if (IsInfobarOverlayUIEnabled()) {
-    auto delegate = std::make_unique<AutofillSaveAddressProfileDelegateIOS>(
-        profile, std::move(callback));
+    auto delegate =
+        std::make_unique<AutofillSaveUpdateAddressProfileDelegateIOS>(
+            profile, original_profile, std::move(callback));
     infobar_manager_->AddInfoBar(std::make_unique<InfoBarIOS>(
         InfobarType::kInfobarTypeSaveAutofillAddressProfile,
         std::move(delegate)));
   } else {
-    DCHECK(personal_data_manager_);
-    personal_data_manager_->SaveImportedProfile(profile);
+    // Fallback to the default behavior to saving without the confirmation.
+    std::move(callback).Run(
+        AutofillClient::SaveAddressProfileOfferUserDecision::kUserNotAsked,
+        profile);
   }
 }
 
diff --git a/ios/chrome/browser/ui/browser_view/BUILD.gn b/ios/chrome/browser/ui/browser_view/BUILD.gn
index 1281b59..70d508e 100644
--- a/ios/chrome/browser/ui/browser_view/BUILD.gn
+++ b/ios/chrome/browser/ui/browser_view/BUILD.gn
@@ -3,10 +3,7 @@
 # found in the LICENSE file.
 
 source_set("browser_view") {
-  configs += [
-    "//build/config/compiler:enable_arc",
-    "//build/config/ios:disable_implicit_retain_self_warning",
-  ]
+  configs += [ "//build/config/compiler:enable_arc" ]
   sources = [
     "browser_coordinator.h",
     "browser_coordinator.mm",
diff --git a/ios/chrome/browser/ui/browser_view/browser_coordinator.mm b/ios/chrome/browser/ui/browser_view/browser_coordinator.mm
index 6da861f..c9e015aa 100644
--- a/ios/chrome/browser/ui/browser_view/browser_coordinator.mm
+++ b/ios/chrome/browser/ui/browser_view/browser_coordinator.mm
@@ -50,6 +50,7 @@
 #import "ios/chrome/browser/ui/default_promo/default_browser_promo_non_modal_commands.h"
 #import "ios/chrome/browser/ui/default_promo/default_browser_promo_non_modal_coordinator.h"
 #import "ios/chrome/browser/ui/default_promo/default_browser_promo_non_modal_scheduler.h"
+#import "ios/chrome/browser/ui/default_promo/default_promo_non_modal_presentation_delegate.h"
 #import "ios/chrome/browser/ui/default_promo/tailored_promo_coordinator.h"
 #import "ios/chrome/browser/ui/download/ar_quick_look_coordinator.h"
 #import "ios/chrome/browser/ui/download/pass_kit_coordinator.h"
@@ -98,6 +99,7 @@
 @interface BrowserCoordinator () <ActivityServiceCommands,
                                   BrowserCoordinatorCommands,
                                   DefaultBrowserPromoCommands,
+                                  DefaultPromoNonModalPresentationDelegate,
                                   FormInputAccessoryCoordinatorNavigator,
                                   PageInfoCommands,
                                   PasswordBreachCommands,
@@ -540,10 +542,16 @@
   self.viewController.reauthHandler =
       HandlerForProtocol(self.dispatcher, IncognitoReauthCommands);
 
+  SceneState* sceneState =
+      SceneStateBrowserAgent::FromBrowser(self.browser)->GetSceneState();
+
+  self.viewController.nonModalPromoScheduler =
+      [DefaultBrowserSceneAgent agentFromScene:sceneState].nonModalScheduler;
+  self.viewController.nonModalPromoPresentationDelegate = self;
+
   if (self.browser->GetBrowserState()->IsOffTheRecord()) {
-    IncognitoReauthSceneAgent* reauthAgent = [IncognitoReauthSceneAgent
-        agentFromScene:SceneStateBrowserAgent::FromBrowser(self.browser)
-                           ->GetSceneState()];
+    IncognitoReauthSceneAgent* reauthAgent =
+        [IncognitoReauthSceneAgent agentFromScene:sceneState];
 
     self.incognitoAuthMediator =
         [[IncognitoReauthMediator alloc] initWithConsumer:self.viewController
@@ -1090,8 +1098,8 @@
                                                    completion:nil];
 }
 
-- (void)dismissDefaultBrowserNonModalPromo {
-  [self.nonModalPromoCoordinator dismissInfobarBannerAnimated:YES
+- (void)dismissDefaultBrowserNonModalPromoAnimated:(BOOL)animated {
+  [self.nonModalPromoCoordinator dismissInfobarBannerAnimated:animated
                                                    completion:nil];
 }
 
@@ -1105,4 +1113,16 @@
   self.nonModalPromoCoordinator = nil;
 }
 
+#pragma mark - DefaultPromoNonModalPresentationDelegate
+
+- (BOOL)defaultNonModalPromoIsShowing {
+  return self.nonModalPromoCoordinator != nil;
+}
+
+- (void)dismissDefaultNonModalPromoAnimated:(BOOL)animated
+                                 completion:(void (^)())completion {
+  [self.nonModalPromoCoordinator dismissInfobarBannerAnimated:animated
+                                                   completion:completion];
+}
+
 @end
diff --git a/ios/chrome/browser/ui/browser_view/browser_view_controller.h b/ios/chrome/browser/ui/browser_view/browser_view_controller.h
index 26a5391c..6084f9a 100644
--- a/ios/chrome/browser/ui/browser_view/browser_view_controller.h
+++ b/ios/chrome/browser/ui/browser_view/browser_view_controller.h
@@ -23,6 +23,8 @@
 @class BrowserContainerViewController;
 @class BrowserViewControllerDependencyFactory;
 @class CommandDispatcher;
+@class DefaultBrowserPromoNonModalScheduler;
+@protocol DefaultPromoNonModalPresentationDelegate;
 @class ToolbarAccessoryPresenter;
 @protocol IncognitoReauthCommands;
 
@@ -81,6 +83,15 @@
 @property(nonatomic, readonly) id<ActivityServicePositioner>
     activityServicePositioner;
 
+// Scheduler for the non-modal default browser promo.
+// TODO(crbug.com/1204120): The BVC should not need this model-level object.
+@property(nonatomic, weak)
+    DefaultBrowserPromoNonModalScheduler* nonModalPromoScheduler;
+
+// Presentation delegate for the non-modal default browser promo.
+@property(nonatomic, weak) id<DefaultPromoNonModalPresentationDelegate>
+    nonModalPromoPresentationDelegate;
+
 // Whether the receiver is currently the primary BVC.
 - (void)setPrimary:(BOOL)primary;
 
diff --git a/ios/chrome/browser/ui/browser_view/browser_view_controller.mm b/ios/chrome/browser/ui/browser_view/browser_view_controller.mm
index a47349b1..6ace97a 100644
--- a/ios/chrome/browser/ui/browser_view/browser_view_controller.mm
+++ b/ios/chrome/browser/ui/browser_view/browser_view_controller.mm
@@ -86,6 +86,8 @@
 #import "ios/chrome/browser/ui/commands/show_signin_command.h"
 #import "ios/chrome/browser/ui/commands/text_zoom_commands.h"
 #import "ios/chrome/browser/ui/content_suggestions/ntp_home_constant.h"
+#import "ios/chrome/browser/ui/default_promo/default_browser_promo_non_modal_scheduler.h"
+#import "ios/chrome/browser/ui/default_promo/default_promo_non_modal_presentation_delegate.h"
 #import "ios/chrome/browser/ui/download/download_manager_coordinator.h"
 #import "ios/chrome/browser/ui/elements/activity_overlay_coordinator.h"
 #import "ios/chrome/browser/ui/first_run/first_run_util.h"
@@ -1704,25 +1706,11 @@
 
   [coordinator
       animateAlongsideTransition:^(
-          id<UIViewControllerTransitionCoordinatorContext> context) {
-        // Force updates of the toolbar updater as the toolbar height might
-        // change on rotation.
-        [_toolbarUIUpdater updateState];
-        // Resize horizontal viewport if Smooth Scrolling is on.
-        if (fullscreen::features::ShouldUseSmoothScrolling()) {
-          BrowserViewController* strongSelf = weakSelf;
-          if (strongSelf) {
-            strongSelf.fullscreenController->ResizeHorizontalViewport();
-          }
-        }
+          id<UIViewControllerTransitionCoordinatorContext>) {
+        [weakSelf animateTransition];
       }
-      completion:^(id<UIViewControllerTransitionCoordinatorContext> context) {
-        BrowserViewController* strongSelf = weakSelf;
-        if (!base::FeatureList::IsEnabled(kModernTabStrip)) {
-          if (strongSelf.tabStripView) {
-            [strongSelf.legacyTabStripCoordinator tabStripSizeDidChange];
-          }
-        }
+      completion:^(id<UIViewControllerTransitionCoordinatorContext>) {
+        [weakSelf completedTransition];
       }];
 
   id<CRWWebViewProxy> webViewProxy = self.currentWebState->GetWebViewProxy();
@@ -1732,6 +1720,24 @@
                                     [[UIDevice currentDevice] orientation]);
 }
 
+- (void)animateTransition {
+  // Force updates of the toolbar updater as the toolbar height might
+  // change on rotation.
+  [_toolbarUIUpdater updateState];
+  // Resize horizontal viewport if Smooth Scrolling is on.
+  if (fullscreen::features::ShouldUseSmoothScrolling()) {
+    self.fullscreenController->ResizeHorizontalViewport();
+  }
+}
+
+- (void)completedTransition {
+  if (!base::FeatureList::IsEnabled(kModernTabStrip)) {
+    if (self.tabStripView) {
+      [self.legacyTabStripCoordinator tabStripSizeDidChange];
+    }
+  }
+}
+
 - (void)dismissViewControllerAnimated:(BOOL)flag
                            completion:(void (^)())completion {
   if (!self.presentedViewController) {
@@ -1830,25 +1836,28 @@
     [self.sideSwipeController resetContentView];
   }
 
-  // TODO(crbug.com/965688): An Infobar message is currently the only presented
-  // controller that allows interaction with the rest of the App while its being
-  // presented. Dismiss it in case the user or system has triggered another
-  // presentation.
-  if (!base::FeatureList::IsEnabled(kInfobarOverlayUI) &&
-      (self.infobarContainerCoordinator.infobarBannerState !=
-       InfobarBannerPresentationState::NotPresented)) {
-    [self.infobarContainerCoordinator
-        dismissInfobarBannerAnimated:NO
-                          completion:^{
-                            [super
-                                presentViewController:viewControllerToPresent
-                                             animated:flag
-                                           completion:finalCompletionHandler];
-                          }];
-  } else {
+  void (^superCall)() = ^{
     [super presentViewController:viewControllerToPresent
                         animated:flag
                       completion:finalCompletionHandler];
+  };
+  // TODO(crbug.com/965688): An Infobar message or the Default Browser Promo are
+  // currently the only presented controller that allow interaction with the
+  // rest of the App while they are being presented. Dismiss it in case the user
+  // or system has triggered another presentation.
+  if (!base::FeatureList::IsEnabled(kInfobarOverlayUI) &&
+      (self.infobarContainerCoordinator.infobarBannerState !=
+       InfobarBannerPresentationState::NotPresented)) {
+    [self.infobarContainerCoordinator dismissInfobarBannerAnimated:NO
+                                                        completion:superCall];
+  } else if ([self.nonModalPromoPresentationDelegate
+                     defaultNonModalPromoIsShowing]) {
+    [self.nonModalPromoPresentationDelegate
+        dismissDefaultNonModalPromoAnimated:NO
+                                 completion:superCall];
+
+  } else {
+    superCall();
   }
 }
 
@@ -4599,6 +4608,9 @@
   // Dismiss Find in Page focus.
   [self.dispatcher defocusFindInPage];
 
+  // Allow the non-modal promo scheduler to close the promo.
+  [self.nonModalPromoScheduler logPopupMenuEntered];
+
   if (type == PopupMenuCommandTypeToolsMenu) {
     [self.bubblePresenter toolsMenuDisplayed];
   }
@@ -4788,21 +4800,24 @@
   if (!self.visible || !self.webUsageEnabled)
     return;
 
-  // Block that starts voice search at the end of new Tab animation if
-  // necessary.
-  ProceduralBlock startVoiceSearchIfNecessary = ^void() {
-    if (_startVoiceSearchAfterNewTabAnimation) {
-      _startVoiceSearchAfterNewTabAnimation = NO;
-      [self startVoiceSearch];
-    }
-  };
-
   if (background) {
     self.inNewTabAnimation = NO;
   } else {
     self.inNewTabAnimation = YES;
+    __weak __typeof(self) weakSelf = self;
     [self animateNewTabForWebState:webState
-        inForegroundWithCompletion:startVoiceSearchIfNecessary];
+        inForegroundWithCompletion:^{
+          [weakSelf startVoiceSearchIfNecessary];
+        }];
+  }
+}
+
+// Helper which starts voice search at the end of new Tab animation if
+// necessary.
+- (void)startVoiceSearchIfNecessary {
+  if (_startVoiceSearchAfterNewTabAnimation) {
+    _startVoiceSearchAfterNewTabAnimation = NO;
+    [self startVoiceSearch];
   }
 }
 
@@ -4847,7 +4862,7 @@
   auto commonCompletion = ^{
     webStateView.frame = self.contentArea.bounds;
     newPage.userInteractionEnabled = YES;
-    if (currentAnimationIdentifier != _NTPAnimationIdentifier) {
+    if (currentAnimationIdentifier != self->_NTPAnimationIdentifier) {
       // Prevent the completion block from being executed if a new animation has
       // started in between. |self.foregroundTabWasAddedCompletionBlock| isn't
       // called because it is overridden when a new animation is started.
@@ -4942,7 +4957,7 @@
           // In an extreme case, this method can be called twice in quick
           // succession, before the animation completes. Check if the blocking
           // UI should be shown or the animation needs to be rolled back.
-          if (_itemsRequireAuthentication) {
+          if (self->_itemsRequireAuthentication) {
             self.blockingView.alpha = 1;
           } else {
             [self.blockingView removeFromSuperview];
diff --git a/ios/chrome/browser/ui/default_promo/BUILD.gn b/ios/chrome/browser/ui/default_promo/BUILD.gn
index 737eaf0..0e91485 100644
--- a/ios/chrome/browser/ui/default_promo/BUILD.gn
+++ b/ios/chrome/browser/ui/default_promo/BUILD.gn
@@ -30,6 +30,7 @@
     "default_browser_promo_view_controller.mm",
     "default_browser_string_util.h",
     "default_browser_string_util.mm",
+    "default_promo_non_modal_presentation_delegate.h",
     "tailored_promo_coordinator.h",
     "tailored_promo_coordinator.mm",
     "tailored_promo_util.h",
@@ -51,6 +52,8 @@
     "//ios/chrome/browser/ui/default_promo/resources",
     "//ios/chrome/browser/ui/infobars/banners",
     "//ios/chrome/browser/ui/infobars/coordinators",
+    "//ios/chrome/browser/ui/main:scene_state_header",
+    "//ios/chrome/browser/ui/main:scene_state_observer",
     "//ios/chrome/browser/web_state_list",
     "//ios/chrome/common/ui/confirmation_alert",
     "//ios/chrome/common/ui/elements:popover_label_view_controller",
diff --git a/ios/chrome/browser/ui/default_promo/default_browser_promo_non_modal_commands.h b/ios/chrome/browser/ui/default_promo/default_browser_promo_non_modal_commands.h
index 38d6123..41e4e70 100644
--- a/ios/chrome/browser/ui/default_promo/default_browser_promo_non_modal_commands.h
+++ b/ios/chrome/browser/ui/default_promo/default_browser_promo_non_modal_commands.h
@@ -11,7 +11,7 @@
 - (void)showDefaultBrowserNonModalPromo;
 
 // Dismisses the non-modal default promo.
-- (void)dismissDefaultBrowserNonModalPromo;
+- (void)dismissDefaultBrowserNonModalPromoAnimated:(BOOL)animated;
 
 // Alerts the command handler that the non-modal default promo was dismissed
 // from the UI.
diff --git a/ios/chrome/browser/ui/default_promo/default_browser_promo_non_modal_scheduler.h b/ios/chrome/browser/ui/default_promo/default_browser_promo_non_modal_scheduler.h
index cb6ee8c94..3f69e39 100644
--- a/ios/chrome/browser/ui/default_promo/default_browser_promo_non_modal_scheduler.h
+++ b/ios/chrome/browser/ui/default_promo/default_browser_promo_non_modal_scheduler.h
@@ -7,13 +7,15 @@
 
 #import <UIKit/UIKit.h>
 
+#import "ios/chrome/browser/ui/main/scene_state_observer.h"
+
 @class CommandDispatcher;
 class OverlayPresenter;
 class WebStateList;
 
 // A scheduler that determines when to show the non-modal default browser
 // promo based on many sources of data.
-@interface DefaultBrowserPromoNonModalScheduler : NSObject
+@interface DefaultBrowserPromoNonModalScheduler : NSObject <SceneStateObserver>
 
 @property(nonatomic, weak) CommandDispatcher* dispatcher;
 
@@ -34,6 +36,12 @@
 // Handles the promo being dismissed, either through user action or timeout.
 - (void)logPromoWasDismissed;
 
+// Handles entering the tab grid, dismissing the promo.
+- (void)logTabGridEntered;
+
+// Handles presenting the popup menu, dismissing the promo.
+- (void)logPopupMenuEntered;
+
 @end
 
 #endif  // IOS_CHROME_BROWSER_UI_DEFAULT_PROMO_DEFAULT_BROWSER_PROMO_NON_MODAL_SCHEDULER_H_
diff --git a/ios/chrome/browser/ui/default_promo/default_browser_promo_non_modal_scheduler.mm b/ios/chrome/browser/ui/default_promo/default_browser_promo_non_modal_scheduler.mm
index 74acc7e..1eb23b6 100644
--- a/ios/chrome/browser/ui/default_promo/default_browser_promo_non_modal_scheduler.mm
+++ b/ios/chrome/browser/ui/default_promo/default_browser_promo_non_modal_scheduler.mm
@@ -9,6 +9,7 @@
 #import "ios/chrome/browser/ui/commands/command_dispatcher.h"
 #import "ios/chrome/browser/ui/default_promo/default_browser_promo_non_modal_commands.h"
 #import "ios/chrome/browser/ui/default_promo/default_browser_utils.h"
+#import "ios/chrome/browser/ui/main/scene_state.h"
 #import "ios/chrome/browser/web_state_list/active_web_state_observation_forwarder.h"
 #include "ios/chrome/browser/web_state_list/web_state_list.h"
 #import "ios/chrome/browser/web_state_list/web_state_list_observer_bridge.h"
@@ -80,9 +81,21 @@
 - (void)logUserFinishedActivityFlow {
 }
 
-- (void)dismissPromo {
+- (void)logPromoWasDismissed {
+  self.promoIsShowing = NO;
+}
+
+- (void)logTabGridEntered {
+  [self dismissPromoAnimated:YES];
+}
+
+- (void)logPopupMenuEntered {
+  [self dismissPromoAnimated:YES];
+}
+
+- (void)dismissPromoAnimated:(BOOL)animated {
   [self cancelDismissPromoTimer];
-  [self.handler dismissDefaultBrowserNonModalPromo];
+  [self.handler dismissDefaultBrowserNonModalPromoAnimated:animated];
 }
 
 - (void)setWebStateList:(WebStateList*)webStateList {
@@ -140,7 +153,16 @@
     willShowOverlayForRequest:(OverlayRequest*)request
           initialPresentation:(BOOL)initialPresentation {
   [self cancelShowPromoTimer];
-  [self dismissPromo];
+  [self dismissPromoAnimated:YES];
+}
+
+#pragma mark - SceneStateObserver
+
+- (void)sceneState:(SceneState*)sceneState
+    transitionedToActivationLevel:(SceneActivationLevel)level {
+  if (level <= SceneActivationLevelBackground) {
+    [self.handler dismissDefaultBrowserNonModalPromoAnimated:NO];
+  }
 }
 
 #pragma mark - Timer Management
@@ -191,11 +213,7 @@
 
 - (void)dismissPromoTimerFinished {
   self.dismissPromoTimer = nil;
-  [self.handler dismissDefaultBrowserNonModalPromo];
-}
-
-- (void)logPromoWasDismissed {
-  self.promoIsShowing = NO;
+  [self.handler dismissDefaultBrowserNonModalPromoAnimated:YES];
 }
 
 @end
diff --git a/ios/chrome/browser/ui/default_promo/default_promo_non_modal_presentation_delegate.h b/ios/chrome/browser/ui/default_promo/default_promo_non_modal_presentation_delegate.h
new file mode 100644
index 0000000..2830d34
--- /dev/null
+++ b/ios/chrome/browser/ui/default_promo/default_promo_non_modal_presentation_delegate.h
@@ -0,0 +1,22 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef IOS_CHROME_BROWSER_UI_DEFAULT_PROMO_DEFAULT_PROMO_NON_MODAL_PRESENTATION_DELEGATE_H_
+#define IOS_CHROME_BROWSER_UI_DEFAULT_PROMO_DEFAULT_PROMO_NON_MODAL_PRESENTATION_DELEGATE_H_
+
+// Delegate used for presenting and dismissing the non-modal default browser
+// promo.
+@protocol DefaultPromoNonModalPresentationDelegate
+
+// Whether the default promo is currently showing.
+- (BOOL)defaultNonModalPromoIsShowing;
+
+// Asks the delegate to dismiss the promo, |animated|, and call |completion|
+// when the dismissal is done.
+- (void)dismissDefaultNonModalPromoAnimated:(BOOL)animated
+                                 completion:(void (^)())completion;
+
+@end
+
+#endif  // IOS_CHROME_BROWSER_UI_DEFAULT_PROMO_DEFAULT_PROMO_NON_MODAL_PRESENTATION_DELEGATE_H_
diff --git a/ios/chrome/browser/ui/main/default_browser_scene_agent.mm b/ios/chrome/browser/ui/main/default_browser_scene_agent.mm
index 2c01588d..de80bc39 100644
--- a/ios/chrome/browser/ui/main/default_browser_scene_agent.mm
+++ b/ios/chrome/browser/ui/main/default_browser_scene_agent.mm
@@ -41,6 +41,7 @@
   if (!base::ios::IsRunningOnOrLater(14, 0, 1)) {
     return;
   }
+
   AppState* appState = self.sceneState.appState;
   // Can only present UI when activation level is
   // SceneActivationLevelForegroundActive. Show the UI if user has met the
diff --git a/ios/chrome/browser/ui/main/scene_controller.mm b/ios/chrome/browser/ui/main/scene_controller.mm
index bb9eb64c..e2f7c295 100644
--- a/ios/chrome/browser/ui/main/scene_controller.mm
+++ b/ios/chrome/browser/ui/main/scene_controller.mm
@@ -790,6 +790,9 @@
       OverlayPresenter::FromBrowser(mainBrowser,
                                     OverlayModality::kInfobarBanner);
   [self.sceneState addAgent:defaultBrowserAgent];
+  if (defaultBrowserAgent.nonModalScheduler) {
+    [self.sceneState addObserver:defaultBrowserAgent.nonModalScheduler];
+  }
 
   [self.sceneState
       addAgent:[[PolicySignoutSceneAgent alloc]
diff --git a/ios/chrome/browser/ui/overlays/infobar_banner/autofill_address_profile/save_address_profile_infobar_banner_overlay_mediator_unittest.mm b/ios/chrome/browser/ui/overlays/infobar_banner/autofill_address_profile/save_address_profile_infobar_banner_overlay_mediator_unittest.mm
index b09fec6..090bd2d 100644
--- a/ios/chrome/browser/ui/overlays/infobar_banner/autofill_address_profile/save_address_profile_infobar_banner_overlay_mediator_unittest.mm
+++ b/ios/chrome/browser/ui/overlays/infobar_banner/autofill_address_profile/save_address_profile_infobar_banner_overlay_mediator_unittest.mm
@@ -9,7 +9,7 @@
 #include "base/guid.h"
 #include "base/strings/sys_string_conversions.h"
 #include "components/autofill/core/browser/autofill_client.h"
-#include "components/autofill/core/browser/autofill_save_address_profile_delegate_ios.h"
+#include "components/autofill/core/browser/autofill_save_update_address_profile_delegate_ios.h"
 #include "components/autofill/core/browser/autofill_test_utils.h"
 #include "components/autofill/core/browser/data_model/autofill_profile.h"
 #include "ios/chrome/browser/infobars/infobar_ios.h"
@@ -34,16 +34,16 @@
 TEST_F(SaveAddressProfileInfobarBannerOverlayMediatorTest, SetUpConsumer) {
   autofill::AutofillProfile profile(base::GenerateGUID(),
                                     "https://www.example.com/");
-  std::unique_ptr<autofill::AutofillSaveAddressProfileDelegateIOS>
-      passed_delegate =
-          std::make_unique<autofill::AutofillSaveAddressProfileDelegateIOS>(
-              profile,
-              base::BindOnce(^(
-                  autofill::AutofillClient::SaveAddressProfileOfferUserDecision
-                      user_decision,
-                  autofill::AutofillProfile profile){
+  std::unique_ptr<autofill::AutofillSaveUpdateAddressProfileDelegateIOS>
+      passed_delegate = std::make_unique<
+          autofill::AutofillSaveUpdateAddressProfileDelegateIOS>(
+          profile, /*original_profile=*/nullptr,
+          base::BindOnce(
+              ^(autofill::AutofillClient::SaveAddressProfileOfferUserDecision
+                    user_decision,
+                autofill::AutofillProfile profile){
               }));
-  autofill::AutofillSaveAddressProfileDelegateIOS* delegate =
+  autofill::AutofillSaveUpdateAddressProfileDelegateIOS* delegate =
       passed_delegate.get();
   InfoBarIOS infobar(InfobarType::kInfobarTypeSaveAutofillAddressProfile,
                      std::move(passed_delegate));
diff --git a/ios/chrome/browser/ui/recent_tabs/BUILD.gn b/ios/chrome/browser/ui/recent_tabs/BUILD.gn
index f8db4b26..7916701 100644
--- a/ios/chrome/browser/ui/recent_tabs/BUILD.gn
+++ b/ios/chrome/browser/ui/recent_tabs/BUILD.gn
@@ -56,10 +56,7 @@
 }
 
 source_set("recent_tabs_ui") {
-  configs += [
-    "//build/config/compiler:enable_arc",
-    "//build/config/ios:disable_implicit_retain_self_warning",
-  ]
+  configs += [ "//build/config/compiler:enable_arc" ]
   sources = [
     "recent_tabs_consumer.h",
     "recent_tabs_menu_provider.h",
diff --git a/ios/chrome/browser/ui/recent_tabs/recent_tabs_table_view_controller.mm b/ios/chrome/browser/ui/recent_tabs/recent_tabs_table_view_controller.mm
index 4543ca7..a01783c 100644
--- a/ios/chrome/browser/ui/recent_tabs/recent_tabs_table_view_controller.mm
+++ b/ios/chrome/browser/ui/recent_tabs/recent_tabs_table_view_controller.mm
@@ -689,31 +689,39 @@
 
 - (void)removeSessionAtTableSectionWithIdentifier:(NSInteger)sectionIdentifier {
   DCHECK([self isSessionSectionIdentifier:sectionIdentifier]);
+
+  // Save the sessionTag before removing it from the table. It will be needed to
+  // delete the session later.
   synced_sessions::DistantSession const* session =
       [self sessionForTableSectionWithIdentifier:sectionIdentifier];
-  std::string sessionTagCopy = session->tag;
+  std::string sessionTag = session->tag;
 
-  NSInteger section =
-      [self.tableViewModel sectionForSectionIdentifier:sectionIdentifier];
-
+  // Remove the section and, on completion, the delete the session.
   __weak __typeof(self) weakSelf = self;
-  void (^tableUpdates)(void) = ^{
-    [weakSelf.tableViewModel removeSectionWithIdentifier:sectionIdentifier];
-    _syncedSessions->EraseSession(section - kNumberOfSectionsBeforeSessions);
-    [weakSelf.tableView deleteSections:[NSIndexSet indexSetWithIndex:section]
-                      withRowAnimation:UITableViewRowAnimationLeft];
-  };
+  [self.tableView
+      performBatchUpdates:^{
+        [weakSelf removeSection:sectionIdentifier];
+      }
+      completion:^(BOOL) {
+        [weakSelf deleteSession:sessionTag];
+      }];
+}
 
-  [self.tableView performBatchUpdates:tableUpdates
-                           completion:^(BOOL) {
-                             if (!weakSelf)
-                               return;
-                             sync_sessions::OpenTabsUIDelegate* openTabs =
-                                 SessionSyncServiceFactory::GetForBrowserState(
-                                     weakSelf.browserState)
-                                     ->GetOpenTabsUIDelegate();
-                             openTabs->DeleteForeignSession(sessionTagCopy);
-                           }];
+// Helper for removeSessionAtTableSectionWithIdentifier
+- (void)removeSection:(NSInteger)sectionIdentifier {
+  NSInteger sectionIndex =
+      [self.tableViewModel sectionForSectionIdentifier:sectionIdentifier];
+  [self.tableViewModel removeSectionWithIdentifier:sectionIdentifier];
+  _syncedSessions->EraseSession(sectionIndex - kNumberOfSectionsBeforeSessions);
+  [self.tableView deleteSections:[NSIndexSet indexSetWithIndex:sectionIndex]
+                withRowAnimation:UITableViewRowAnimationLeft];
+}
+
+// Helper for removeSessionAtTableSectionWithIdentifier
+- (void)deleteSession:(std::string)sessionTag {
+  SessionSyncServiceFactory::GetForBrowserState(self.browserState)
+      ->GetOpenTabsUIDelegate()
+      ->DeleteForeignSession(sessionTag);
 }
 
 #pragma mark - Private
diff --git a/ios/chrome/browser/ui/start_surface/start_surface_recent_tab_browser_agent.h b/ios/chrome/browser/ui/start_surface/start_surface_recent_tab_browser_agent.h
index a8a80f62..9222c1f 100644
--- a/ios/chrome/browser/ui/start_surface/start_surface_recent_tab_browser_agent.h
+++ b/ios/chrome/browser/ui/start_surface/start_surface_recent_tab_browser_agent.h
@@ -11,6 +11,7 @@
 #include "ios/chrome/browser/main/browser_observer.h"
 #import "ios/chrome/browser/main/browser_user_data.h"
 #import "ios/chrome/browser/web_state_list/web_state_list_observer.h"
+#include "ios/web/public/web_state_observer.h"
 
 namespace web {
 class WebState;
@@ -45,6 +46,7 @@
     : public BrowserUserData<StartSurfaceRecentTabBrowserAgent>,
       BrowserObserver,
       public WebStateListObserver,
+      public web::WebStateObserver,
       public favicon::FaviconDriverObserver {
  public:
   // Notifies the Browser Agent to save the most recent WebState.
@@ -77,6 +79,9 @@
                           web::WebState* web_state,
                           int index) override;
 
+  // web::WebStateObserver
+  void WebStateDestroyed(web::WebState* web_state) override;
+
   // favicon::FaviconDriverObserver
   void OnFaviconUpdated(favicon::FaviconDriver* driver,
                         NotificationIconType notification_icon_type,
@@ -87,10 +92,13 @@
   // A list of observers notified when the most recent tab is removed. Weak
   // references.
   base::ObserverList<StartSurfaceRecentTabObserver, true>::Unchecked observers_;
-  // Manages observation relationship between |this| and WebFaviconDriver.
+  // Manages observation relationship between |this| and favicon::FaviconDriver.
   base::ScopedObservation<favicon::FaviconDriver,
                           favicon::FaviconDriverObserver>
       favicon_driver_observer_{this};
+  // Manages observation relationship between |this| and web::WebState.
+  base::ScopedObservation<web::WebState, web::WebStateObserver>
+      web_state_observation_{this};
   // The most recent tab managed by this Browser Agent.
   web::WebState* most_recent_tab_ = nullptr;
   // Browser.
diff --git a/ios/chrome/browser/ui/start_surface/start_surface_recent_tab_browser_agent.mm b/ios/chrome/browser/ui/start_surface/start_surface_recent_tab_browser_agent.mm
index d16adce..454daa5 100644
--- a/ios/chrome/browser/ui/start_surface/start_surface_recent_tab_browser_agent.mm
+++ b/ios/chrome/browser/ui/start_surface/start_surface_recent_tab_browser_agent.mm
@@ -29,13 +29,21 @@
 #pragma mark - Public
 
 void StartSurfaceRecentTabBrowserAgent::SaveMostRecentTab() {
-  most_recent_tab_ = browser_->GetWebStateList()->GetActiveWebState();
-  DCHECK(favicon::WebFaviconDriver::FromWebState(most_recent_tab_));
-  if (favicon_driver_observer_.IsObserving()) {
-    favicon_driver_observer_.Reset();
+  web::WebState* active_web_state =
+      browser_->GetWebStateList()->GetActiveWebState();
+  if (most_recent_tab_ != active_web_state) {
+    most_recent_tab_ = active_web_state;
+    DCHECK(favicon::WebFaviconDriver::FromWebState(most_recent_tab_));
+    if (favicon_driver_observer_.IsObserving()) {
+      favicon_driver_observer_.Reset();
+    }
+    favicon_driver_observer_.Observe(
+        favicon::WebFaviconDriver::FromWebState(most_recent_tab_));
+    if (web_state_observation_.IsObserving()) {
+      web_state_observation_.Reset();
+    }
+    web_state_observation_.Observe(most_recent_tab_);
   }
-  favicon_driver_observer_.Observe(
-      favicon::WebFaviconDriver::FromWebState(most_recent_tab_));
 }
 
 void StartSurfaceRecentTabBrowserAgent::AddObserver(
@@ -55,6 +63,7 @@
   browser_->GetWebStateList()->RemoveObserver(this);
   browser_->RemoveObserver(this);
   favicon_driver_observer_.Reset();
+  web_state_observation_.Reset();
 }
 
 #pragma mark - WebStateListObserver
@@ -72,11 +81,21 @@
       observer.MostRecentTabRemoved(most_recent_tab_);
     }
     favicon_driver_observer_.Reset();
+    web_state_observation_.Reset();
     most_recent_tab_ = nullptr;
     return;
   }
 }
 
+#pragma mark - WebStateObserver
+
+void StartSurfaceRecentTabBrowserAgent::WebStateDestroyed(
+    web::WebState* web_state) {
+  favicon_driver_observer_.Reset();
+  web_state_observation_.Reset();
+  most_recent_tab_ = nullptr;
+}
+
 void StartSurfaceRecentTabBrowserAgent::OnFaviconUpdated(
     favicon::FaviconDriver* driver,
     NotificationIconType notification_icon_type,
diff --git a/ios/chrome/browser/ui/tab_switcher/tab_grid/BUILD.gn b/ios/chrome/browser/ui/tab_switcher/tab_grid/BUILD.gn
index a59e3cf..a021a743 100644
--- a/ios/chrome/browser/ui/tab_switcher/tab_grid/BUILD.gn
+++ b/ios/chrome/browser/ui/tab_switcher/tab_grid/BUILD.gn
@@ -41,11 +41,13 @@
     "//ios/chrome/browser/ui/alert_coordinator",
     "//ios/chrome/browser/ui/commands",
     "//ios/chrome/browser/ui/coordinators:chrome_coordinators",
+    "//ios/chrome/browser/ui/default_promo",
     "//ios/chrome/browser/ui/gestures",
     "//ios/chrome/browser/ui/history",
     "//ios/chrome/browser/ui/history/public",
     "//ios/chrome/browser/ui/incognito_reauth:incognito_reauth_scene_agent",
     "//ios/chrome/browser/ui/main",
+    "//ios/chrome/browser/ui/main:default_browser_scene_agent",
     "//ios/chrome/browser/ui/menu:context_menu_delegate",
     "//ios/chrome/browser/ui/recent_tabs",
     "//ios/chrome/browser/ui/recent_tabs:recent_tabs_ui",
diff --git a/ios/chrome/browser/ui/tab_switcher/tab_grid/tab_grid_coordinator.mm b/ios/chrome/browser/ui/tab_switcher/tab_grid/tab_grid_coordinator.mm
index 6525796..85ea473 100644
--- a/ios/chrome/browser/ui/tab_switcher/tab_grid/tab_grid_coordinator.mm
+++ b/ios/chrome/browser/ui/tab_switcher/tab_grid/tab_grid_coordinator.mm
@@ -27,6 +27,7 @@
 #import "ios/chrome/browser/ui/commands/open_new_tab_command.h"
 #import "ios/chrome/browser/ui/commands/reading_list_add_command.h"
 #import "ios/chrome/browser/ui/commands/thumb_strip_commands.h"
+#import "ios/chrome/browser/ui/default_promo/default_browser_promo_non_modal_scheduler.h"
 #import "ios/chrome/browser/ui/gestures/view_controller_trait_collection_observer.h"
 #import "ios/chrome/browser/ui/gestures/view_revealing_vertical_pan_handler.h"
 #import "ios/chrome/browser/ui/history/history_coordinator.h"
@@ -34,6 +35,7 @@
 #import "ios/chrome/browser/ui/incognito_reauth/incognito_reauth_mediator.h"
 #import "ios/chrome/browser/ui/incognito_reauth/incognito_reauth_scene_agent.h"
 #import "ios/chrome/browser/ui/main/bvc_container_view_controller.h"
+#import "ios/chrome/browser/ui/main/default_browser_scene_agent.h"
 #import "ios/chrome/browser/ui/main/scene_state_browser_agent.h"
 #import "ios/chrome/browser/ui/menu/tab_context_menu_delegate.h"
 #import "ios/chrome/browser/ui/recent_tabs/recent_tabs_mediator.h"
@@ -272,6 +274,12 @@
     return;
   }
 
+  SceneState* sceneState =
+      SceneStateBrowserAgent::FromBrowser(self.regularBrowser)->GetSceneState();
+  DefaultBrowserSceneAgent* agent =
+      [DefaultBrowserSceneAgent agentFromScene:sceneState];
+  [agent.nonModalScheduler logTabGridEntered];
+
   // If a BVC is currently being presented, dismiss it.  This will trigger any
   // necessary animations.
   if (self.bvcContainer) {
diff --git a/ios/chrome/common/ui/colors/resources/BUILD.gn b/ios/chrome/common/ui/colors/resources/BUILD.gn
index 65612586..815f931 100644
--- a/ios/chrome/common/ui/colors/resources/BUILD.gn
+++ b/ios/chrome/common/ui/colors/resources/BUILD.gn
@@ -48,6 +48,7 @@
     ":text_primary_dark_color",
     ":text_secondary_color",
     ":text_secondary_dark_color",
+    ":text_tertiary_color",
     ":textfield_background_color",
     ":textfield_background_dark_color",
     ":textfield_placeholder_color",
@@ -190,6 +191,10 @@
   sources = [ "tertiary_background_dark_color.colorset/Contents.json" ]
 }
 
+colorset("text_tertiary_color") {
+  sources = [ "text_tertiary_color.colorset/Contents.json" ]
+}
+
 colorset("text_primary_color") {
   sources = [ "text_primary_color.colorset/Contents.json" ]
 }
diff --git a/ios/chrome/common/ui/colors/resources/text_tertiary_color.colorset/Contents.json b/ios/chrome/common/ui/colors/resources/text_tertiary_color.colorset/Contents.json
new file mode 100644
index 0000000..bbef72f
--- /dev/null
+++ b/ios/chrome/common/ui/colors/resources/text_tertiary_color.colorset/Contents.json
@@ -0,0 +1,38 @@
+{
+  "info": {
+    "version": 1,
+    "author": "xcode"
+  },
+  "colors": [
+    {
+      "idiom": "universal",
+      "color": {
+        "color-space": "display-p3",
+        "components": {
+          "red": "0x3C",
+          "alpha": "0.3",
+          "blue": "0x43",
+          "green": "0x3C"
+        }
+      }
+    },
+    {
+      "idiom": "universal",
+      "appearances": [
+        {
+          "appearance": "luminosity",
+          "value": "dark"
+        }
+      ],
+      "color": {
+        "color-space": "display-p3",
+        "components": {
+          "red": "0xEB",
+          "alpha": "0.3",
+          "blue": "0xF5",
+          "green": "0xEB"
+        }
+      }
+    }
+  ]
+}
diff --git a/ios/chrome/common/ui/colors/semantic_color_names.h b/ios/chrome/common/ui/colors/semantic_color_names.h
index a514e58..cf21fa5 100644
--- a/ios/chrome/common/ui/colors/semantic_color_names.h
+++ b/ios/chrome/common/ui/colors/semantic_color_names.h
@@ -36,6 +36,7 @@
 extern NSString* const kTertiaryBackgroundColor;
 extern NSString* const kTextPrimaryColor;
 extern NSString* const kTextSecondaryColor;
+extern NSString* const kTextTertiaryColor;
 extern NSString* const kTextfieldBackgroundColor;
 extern NSString* const kTextfieldPlaceholderColor;
 // Color used for buttons on a toolbar.
diff --git a/ios/chrome/common/ui/colors/semantic_color_names.mm b/ios/chrome/common/ui/colors/semantic_color_names.mm
index 7495c452..3c87515 100644
--- a/ios/chrome/common/ui/colors/semantic_color_names.mm
+++ b/ios/chrome/common/ui/colors/semantic_color_names.mm
@@ -29,6 +29,7 @@
 NSString* const kTertiaryBackgroundColor = @"tertiary_background_color";
 NSString* const kTextPrimaryColor = @"text_primary_color";
 NSString* const kTextSecondaryColor = @"text_secondary_color";
+NSString* const kTextTertiaryColor = @"text_tertiary_color";
 NSString* const kTextfieldBackgroundColor = @"textfield_background_color";
 NSString* const kTextfieldPlaceholderColor = @"textfield_placeholder_color";
 NSString* const kToolbarButtonColor = @"toolbar_button_color";
diff --git a/media/base/media_log.h b/media/base/media_log.h
index dc3ea25b..46b52ae 100644
--- a/media/base/media_log.h
+++ b/media/base/media_log.h
@@ -214,8 +214,9 @@
 
 // Provides a stringstream to collect a log entry to pass to the provided
 // MediaLog at the requested level.
-#define MEDIA_LOG(level, media_log) \
-  LogHelper((MediaLogMessageLevel::k##level), (media_log)).stream()
+#define MEDIA_LOG(level, media_log)                                      \
+  media::LogHelper((media::MediaLogMessageLevel::k##level), (media_log)) \
+      .stream()
 
 // Logs only while |count| < |max|, increments |count| for each log, and warns
 // in the log if |count| has just reached |max|.
diff --git a/mojo/public/cpp/bindings/sync_call_restrictions.h b/mojo/public/cpp/bindings/sync_call_restrictions.h
index a0ff4d1..73c3cf30 100644
--- a/mojo/public/cpp/bindings/sync_call_restrictions.h
+++ b/mojo/public/cpp/bindings/sync_call_restrictions.h
@@ -35,6 +35,7 @@
 namespace viz {
 class GpuHostImpl;
 class HostFrameSinkManager;
+class HostGpuMemoryBufferManager;
 }
 
 namespace mojo {
@@ -86,6 +87,7 @@
   // For destroying the GL context/surface that draw to a platform window before
   // the platform window is destroyed.
   friend class viz::HostFrameSinkManager;
+  friend class viz::HostGpuMemoryBufferManager;
   // For preventing frame swaps of wrong size during resize on Windows.
   // (https://crbug.com/811945)
   friend class ui::Compositor;
diff --git a/net/quic/platform/impl/quic_containers_impl.h b/net/quic/platform/impl/quic_containers_impl.h
index 3985388..69ab292 100644
--- a/net/quic/platform/impl/quic_containers_impl.h
+++ b/net/quic/platform/impl/quic_containers_impl.h
@@ -42,12 +42,6 @@
 template <typename Key, typename Value, typename Hash>
 using QuicLinkedHashMapImpl = quiche::SimpleLinkedHashMap<Key, Value, Hash>;
 
-// A map which is faster than (for example) hash_map for a certain number of
-// unique key-value-pair elements, and upgrades itself to unordered_map when
-// runs out of space.
-template <typename Key, typename Value, int Size>
-using QuicSmallMapImpl = base::small_map<std::unordered_map<Key, Value>, Size>;
-
 // Represents a simple queue which may be backed by a list or
 // a flat circular buffer.
 //
diff --git a/net/quic/quic_connectivity_monitor.h b/net/quic/quic_connectivity_monitor.h
index 5e26d8d..646d96d 100644
--- a/net/quic/quic_connectivity_monitor.h
+++ b/net/quic/quic_connectivity_monitor.h
@@ -5,6 +5,7 @@
 #ifndef NET_QUIC_QUIC_CONNECTIVITY_MONITOR_H_
 #define NET_QUIC_QUIC_CONNECTIVITY_MONITOR_H_
 
+#include "base/containers/flat_set.h"
 #include "base/numerics/clamped_math.h"
 #include "net/base/network_change_notifier.h"
 #include "net/quic/quic_chromium_client_session.h"
@@ -85,11 +86,11 @@
 
  private:
   // Size chosen per net.QuicSession.WriteError histogram.
-  using WriteErrorMap = quic::QuicSmallMap<int, size_t, 20>;
+  using WriteErrorMap = base::flat_map<int, size_t>;
   // The most common QuicErrorCode cared by this monitor is:
   // QUIC_PUBLIC_RESET by the peer, or
   // QUIC_PACKET_WRITE_ERROR/QUIC_TOO_MANY_RTOS by self.
-  using QuicErrorCodeMap = quic::QuicSmallMap<quic::QuicErrorCode, size_t, 5>;
+  using QuicErrorCodeMap = base::flat_map<quic::QuicErrorCode, size_t>;
 
   // If NetworkHandle is not supported, always set to
   // NetworkChangeNotifier::kInvalidNetworkHandle.
diff --git a/net/spdy/platform/impl/spdy_containers_impl.h b/net/spdy/platform/impl/spdy_containers_impl.h
index f28f8a2..c5dde362 100644
--- a/net/spdy/platform/impl/spdy_containers_impl.h
+++ b/net/spdy/platform/impl/spdy_containers_impl.h
@@ -20,12 +20,6 @@
 template <typename T, size_t N, typename A = std::allocator<T>>
 using SpdyInlinedVectorImpl = std::vector<T, A>;
 
-// A map which is faster than (for example) hash_map for a certain number of
-// unique key-value-pair elements, and upgrades itself to unordered_map when
-// runs out of space.
-template <typename Key, typename Value, size_t Size>
-using SpdySmallMapImpl = base::small_map<std::unordered_map<Key, Value>, Size>;
-
 }  // namespace spdy
 
 #endif  // NET_SPDY_PLATFORM_IMPL_SPDY_CONTAINERS_IMPL_H_
diff --git a/net/third_party/quiche/BUILD.gn b/net/third_party/quiche/BUILD.gn
index 635b8f4e2..82308775 100644
--- a/net/third_party/quiche/BUILD.gn
+++ b/net/third_party/quiche/BUILD.gn
@@ -427,6 +427,8 @@
       "src/quic/core/quic_blocked_writer_interface.h",
       "src/quic/core/quic_buffer_allocator.cc",
       "src/quic/core/quic_buffer_allocator.h",
+      "src/quic/core/quic_chaos_protector.cc",
+      "src/quic/core/quic_chaos_protector.h",
       "src/quic/core/quic_clock.cc",
       "src/quic/core/quic_clock.h",
       "src/quic/core/quic_coalesced_packet.cc",
@@ -1341,6 +1343,7 @@
     "src/quic/core/quic_arena_scoped_ptr_test.cc",
     "src/quic/core/quic_bandwidth_test.cc",
     "src/quic/core/quic_buffered_packet_store_test.cc",
+    "src/quic/core/quic_chaos_protector_test.cc",
     "src/quic/core/quic_coalesced_packet_test.cc",
     "src/quic/core/quic_config_test.cc",
     "src/quic/core/quic_connection_id_manager_test.cc",
diff --git a/services/device/hid/hid_preparsed_data.cc b/services/device/hid/hid_preparsed_data.cc
index 76df71dba..75dfb3a 100644
--- a/services/device/hid/hid_preparsed_data.cc
+++ b/services/device/hid/hid_preparsed_data.cc
@@ -42,7 +42,7 @@
 
   uint16_t unknown[3];
 
-  // Number of report items for input reports.
+  // Number of report items for input reports. Includes unused items.
   uint16_t input_item_count;
 
   uint16_t unknown2;
@@ -53,7 +53,7 @@
 
   uint16_t unknown3;
 
-  // Number of report items for output reports.
+  // Number of report items for output reports. Includes unused items.
   uint16_t output_item_count;
 
   uint16_t unknown4;
@@ -64,10 +64,11 @@
 
   uint16_t unknown5;
 
-  // Number of report items for feature reports.
+  // Number of report items for feature reports. Includes unused items.
   uint16_t feature_item_count;
 
-  // Total number of report items (input, output, and feature).
+  // Total number of report items (input, output, and feature). Unused items are
+  // excluded.
   uint16_t item_count;
 
   // Maximum feature report size, in bytes. Includes the report ID byte. Zero if
@@ -188,12 +189,19 @@
     return false;
   if (header.feature_report_byte_length == 0 && header.feature_item_count > 0)
     return false;
-  if (header.input_item_count + header.output_item_count +
-          header.feature_item_count !=
-      header.item_count) {
-    return false;
-  }
-  if (header.item_count * sizeof(PreparsedDataItem) != header.size_bytes)
+
+  // Calculate the expected total size of report items in the
+  // _HIDP_PREPARSED_DATA object. Use the individual item counts for each report
+  // type instead of the total |item_count|. In some cases additional items are
+  // allocated but are not used for any reports. Unused items are excluded from
+  // |item_count| but are included in the item counts for each report type and
+  // contribute to the total size of the object. See crbug.com/1199890 for more
+  // information.
+  uint16_t total_item_size =
+      (header.input_item_count + header.output_item_count +
+       header.feature_item_count) *
+      sizeof(PreparsedDataItem);
+  if (total_item_size != header.size_bytes)
     return false;
   return true;
 }
diff --git a/services/tracing/perfetto/privacy_filtered_fields-inl.h b/services/tracing/perfetto/privacy_filtered_fields-inl.h
index 55503a6d..52cc53ab 100644
--- a/services/tracing/perfetto/privacy_filtered_fields-inl.h
+++ b/services/tracing/perfetto/privacy_filtered_fields-inl.h
@@ -201,7 +201,7 @@
     kChromeMemoryPressureNotificationIndices, nullptr};
 
 // Proto Message: ChromeTaskAnnotator
-constexpr int kChromeTaskAnnotatorIndices[] = {1, -1};
+constexpr int kChromeTaskAnnotatorIndices[] = {1, 2, -1};
 constexpr MessageInfo kChromeTaskAnnotator = {kChromeTaskAnnotatorIndices,
                                               nullptr};
 
@@ -424,7 +424,7 @@
     kTracePacketDefaultsIndices, kTracePacketDefaultsComplexMessages};
 
 // Proto Message: ChromeProcessDescriptor
-constexpr int kChromeProcessDescriptorIndices[] = {1, 2, 3, -1};
+constexpr int kChromeProcessDescriptorIndices[] = {1, 2, 3, 5, -1};
 constexpr MessageInfo kChromeProcessDescriptor = {
     kChromeProcessDescriptorIndices, nullptr};
 
diff --git a/services/viz/privileged/mojom/gl/gpu_service.mojom b/services/viz/privileged/mojom/gl/gpu_service.mojom
index 56f42b0..d1ccdf1 100644
--- a/services/viz/privileged/mojom/gl/gpu_service.mojom
+++ b/services/viz/privileged/mojom/gl/gpu_service.mojom
@@ -85,6 +85,7 @@
       pending_receiver<media.mojom.VideoEncodeAcceleratorProvider>
           vea_provider);
 
+  [Sync]
   CreateGpuMemoryBuffer(gfx.mojom.GpuMemoryBufferId id,
                         gfx.mojom.Size size,
                         gfx.mojom.BufferFormat format,
diff --git a/skia/BUILD.gn b/skia/BUILD.gn
index 114a9d0..dcefed0 100644
--- a/skia/BUILD.gn
+++ b/skia/BUILD.gn
@@ -44,7 +44,6 @@
 
   if (!is_ios) {
     if (enable_skia_wuffs_gif) {
-      defines += [ "SK_FAVOR_WUFFS_V_0_3_OVER_V_0_2" ]
       include_dirs += [ "//third_party/wuffs/src/release/c" ]
     } else {
       include_dirs += [ "//third_party/libgifcodec" ]
diff --git a/testing/buildbot/chromium.memory.json b/testing/buildbot/chromium.memory.json
index 1b7e8f5..58bddb8 100644
--- a/testing/buildbot/chromium.memory.json
+++ b/testing/buildbot/chromium.memory.json
@@ -5507,7 +5507,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
@@ -5528,7 +5528,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
@@ -6277,7 +6277,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
@@ -6578,7 +6578,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
diff --git a/testing/buildbot/test_suite_exceptions.pyl b/testing/buildbot/test_suite_exceptions.pyl
index 14ac0ed1..a4e0f84 100644
--- a/testing/buildbot/test_suite_exceptions.pyl
+++ b/testing/buildbot/test_suite_exceptions.pyl
@@ -1169,6 +1169,11 @@
       },
       'Linux ASan LSan Tests (1)': {
         'swarming': {
+          'dimension_sets': [
+            {
+              'os': 'Ubuntu-18.04',
+            }
+          ],
           'shards': 12,
         },
       },
@@ -1389,6 +1394,11 @@
         # These are slow on the ASAN trybot for some reason.
         # crbug.com/794372
         'swarming': {
+          'dimension_sets': [
+            {
+              'os': 'Ubuntu-18.04',
+            }
+          ],
           'shards': 2,
         },
       },
@@ -2146,6 +2156,11 @@
         # These are very slow on the ASAN trybot for some reason.
         # crbug.com/794372
         'swarming': {
+          'dimension_sets': [
+            {
+              'os': 'Ubuntu-18.04',
+            }
+          ],
           'shards': 16,
         },
       },
@@ -2600,6 +2615,11 @@
       },
       'Linux ASan LSan Tests (1)': {
         'swarming': {
+          'dimension_sets': [
+            {
+              'os': 'Ubuntu-18.04',
+            }
+          ],
           'shards': 4,
         },
       },
diff --git a/testing/variations/fieldtrial_testing_config.json b/testing/variations/fieldtrial_testing_config.json
index 192ba43..727ab8c 100644
--- a/testing/variations/fieldtrial_testing_config.json
+++ b/testing/variations/fieldtrial_testing_config.json
@@ -1847,6 +1847,27 @@
             ]
         }
     ],
+    "ClientHintReliability": [
+        {
+            "platforms": [
+                "android",
+                "chromeos",
+                "chromeos_lacros",
+                "linux",
+                "mac",
+                "windows"
+            ],
+            "experiments": [
+                {
+                    "name": "ClientHintReliability",
+                    "enable_features": [
+                        "AcceptCHFrame",
+                        "CriticalClientHint"
+                    ]
+                }
+            ]
+        }
+    ],
     "ClientSideDetectionModelOnAndroid": [
         {
             "platforms": [
@@ -3695,6 +3716,10 @@
                 },
                 {
                     "name": "EnabledM2FullSet",
+                    "params": {
+                        "DiscoverFeedIsNativeUIEnabled": "true",
+                        "RefactoredNTPLoggingEnabled": "true"
+                    },
                     "enable_features": [
                         "DiscoverFeedInNtp",
                         "RefactoredNTP"
@@ -5198,18 +5223,19 @@
             ]
         }
     ],
-    "PageLoadMetricsTimerDelay": [
+    "PageLoadMetricsBufferTimer": [
         {
             "platforms": [
                 "android",
                 "chromeos",
+                "chromeos_lacros",
                 "linux",
                 "mac",
                 "windows"
             ],
             "experiments": [
                 {
-                    "name": "100MS",
+                    "name": "Enabled100ms",
                     "params": {
                         "BufferTimerDelayMillis": "100"
                     },
@@ -5643,6 +5669,27 @@
             ]
         }
     ],
+    "PrivacySandboxHats": [
+        {
+            "platforms": [
+                "linux",
+                "mac",
+                "windows"
+            ],
+            "experiments": [
+                {
+                    "name": "Enabled",
+                    "params": {
+                        "en_site_id": "K1QEL8XnT0ugnJ3q1cK0X9jod55x",
+                        "probability": "1.0"
+                    },
+                    "enable_features": [
+                        "HappinessTrackingSurveysForDesktopPrivacySandbox"
+                    ]
+                }
+            ]
+        }
+    ],
     "PrivacySandboxSettings": [
         {
             "platforms": [
diff --git a/third_party/android_deps/fetch_all.py b/third_party/android_deps/fetch_all.py
index d71b4e2c0..b3c4962f 100755
--- a/third_party/android_deps/fetch_all.py
+++ b/third_party/android_deps/fetch_all.py
@@ -12,7 +12,6 @@
   - Generate a README.chromium file
   - Generate a GN target in BUILD.gn
   - Generate .info files for AAR libraries
-  - Generate CIPD yaml files describing the packages
   - Generate a 'deps' entry in DEPS.
 """
 
@@ -417,36 +416,6 @@
     print('\n'.join('    - ' + p for p in packages))
 
 
-def _GenerateCipdUploadCommands(android_deps_dir, cipd_pkg_infos):
-    """Generates a shell command to upload missing packages."""
-
-    def cipd_describe(info):
-        pkg_name, pkg_tag = info[1:]
-        result = subprocess.call(
-            ['cipd', 'describe', pkg_name, '-version', pkg_tag],
-            stdout=subprocess.DEVNULL)
-        return info, result
-
-    # Re-run the describe step to prevent mistakes if run multiple times.
-    TEMPLATE = ('(cd "{0}"; '
-                'cipd describe "{1}" -version "{2}" || '
-                'cipd create --pkg-def cipd.yaml -tag "{2}")')
-    cmds = []
-    # max_workers chosen arbitrarily.
-    with concurrent.futures.ThreadPoolExecutor(max_workers=80) as executor:
-        for info, result in executor.map(cipd_describe, cipd_pkg_infos):
-            if result:
-                pkg_path, pkg_name, pkg_tag = info
-                # pkg_path is implicitly relative to _CHROMIUM_SRC, make it
-                # explicit.
-                pkg_path = os.path.join(_CHROMIUM_SRC, android_deps_dir,
-                                        pkg_path)
-                # Now make pkg_path relative to os.curdir.
-                pkg_path = os.path.relpath(pkg_path)
-                cmds.append(TEMPLATE.format(pkg_path, pkg_name, pkg_tag))
-    return cmds
-
-
 def _CreateAarInfos(aar_files):
     jobs = []
 
@@ -596,12 +565,6 @@
 
         new_packages = sorted(set(build_packages) - set(existing_packages))
 
-        # Generate CIPD package upload commands.
-        logging.info('Querying %d CIPD packages', len(build_packages))
-        cipd_commands = _GenerateCipdUploadCommands(
-            args.android_deps_dir,
-            (build_packages[pkg] for pkg in build_packages))
-
         # Copy updated DEPS and BUILD.gn to build directory.
         update_cmds = []
         Copy(build_android_deps_dir,
@@ -634,14 +597,6 @@
         if deleted_packages:
             PrintPackageList(deleted_packages, 'deleted')
 
-        if cipd_commands:
-            print('Run the following to upload CIPD packages:')
-            print('-------------------- cut here ------------------------')
-            print('\n'.join(cipd_commands))
-            print('-------------------- cut here ------------------------')
-        else:
-            print('Done. All packages were already up-to-date on CIPD')
-
 
 if __name__ == "__main__":
     main()
diff --git a/third_party/blink/common/features.cc b/third_party/blink/common/features.cc
index ec844c3..367e8ae 100644
--- a/third_party/blink/common/features.cc
+++ b/third_party/blink/common/features.cc
@@ -922,12 +922,12 @@
 // API exposure will be disabled regardless of the OT config.
 // (See https://github.com/WICG/turtledove/blob/main/FLEDGE.md.)
 // Enables FLEDGE implementation. See https://crbug.com/1186444.
-const base::Feature kFledgeInterestGroups{"kFledgeInterestGroups",
+const base::Feature kFledgeInterestGroups{"FledgeInterestGroups",
                                           base::FEATURE_DISABLED_BY_DEFAULT};
 
 // Enable the availability of the Fledge interest group API as part of the
 // origin trial.
-const base::Feature kFledgeInterestGroupAPI{"kFledgeInterestGroupAPI",
+const base::Feature kFledgeInterestGroupAPI{"FledgeInterestGroupAPI",
                                             base::FEATURE_DISABLED_BY_DEFAULT};
 
 }  // namespace features
diff --git a/third_party/blink/public/devtools_protocol/browser_protocol.pdl b/third_party/blink/public/devtools_protocol/browser_protocol.pdl
index 056d82b..beb9685 100644
--- a/third_party/blink/public/devtools_protocol/browser_protocol.pdl
+++ b/third_party/blink/public/devtools_protocol/browser_protocol.pdl
@@ -5144,8 +5144,8 @@
   # Request pattern for interception.
   experimental type RequestPattern extends object
     properties
-      # Wildcards ('*' -> zero or more, '?' -> exactly one) are allowed. Escape character is
-      # backslash. Omitting is equivalent to "*".
+      # Wildcards (`'*'` -> zero or more, `'?'` -> exactly one) are allowed. Escape character is
+      # backslash. Omitting is equivalent to `"*"`.
       optional string urlPattern
       # If set, only requests for matching resource types will be intercepted.
       optional ResourceType resourceType
@@ -8675,8 +8675,8 @@
 
   type RequestPattern extends object
     properties
-      # Wildcards ('*' -> zero or more, '?' -> exactly one) are allowed. Escape character is
-      # backslash. Omitting is equivalent to "*".
+      # Wildcards (`'*'` -> zero or more, `'?'` -> exactly one) are allowed. Escape character is
+      # backslash. Omitting is equivalent to `"*"`.
       optional string urlPattern
       # If set, only requests for matching resource types will be intercepted.
       optional Network.ResourceType resourceType
diff --git a/third_party/blink/renderer/bindings/modules/v8/v8_binding_for_modules.cc b/third_party/blink/renderer/bindings/modules/v8/v8_binding_for_modules.cc
index 1ec4526..ab4e9b1 100644
--- a/third_party/blink/renderer/bindings/modules/v8/v8_binding_for_modules.cc
+++ b/third_party/blink/renderer/bindings/modules/v8/v8_binding_for_modules.cc
@@ -267,6 +267,8 @@
 
   v8::Local<v8::Context> context = isolate->GetCurrentContext();
   v8::TryCatch try_block(isolate);
+  v8::MicrotasksScope microtasks_scope(
+      isolate, v8::MicrotasksScope::kDoNotRunMicrotasks);
 
   // Process stack - will return when complete.
   while (true) {
@@ -371,6 +373,8 @@
   v8::HandleScope handle_scope(isolate);
   v8::Local<v8::Context> context = isolate->GetCurrentContext();
   v8::TryCatch block(isolate);
+  v8::MicrotasksScope microtasks_scope(
+      isolate, v8::MicrotasksScope::kDoNotRunMicrotasks);
   for (wtf_size_t i = 0; i < key_path_elements.size(); ++i) {
     const String& element = key_path_elements[i];
 
diff --git a/third_party/blink/renderer/core/editing/frame_selection_test.cc b/third_party/blink/renderer/core/editing/frame_selection_test.cc
index fe46e5f..aad2171 100644
--- a/third_party/blink/renderer/core/editing/frame_selection_test.cc
+++ b/third_party/blink/renderer/core/editing/frame_selection_test.cc
@@ -1103,4 +1103,35 @@
   EXPECT_EQ("foo\nbar\nbaz", Selection().SelectedTextForClipboard());
 }
 
+// For https://crbug.com/1177295
+TEST_F(FrameSelectionTest, PositionDisconnectedInFlatTree) {
+  SetBodyContent("<div id=host>x</div>y");
+  SetShadowContent("", "host");
+  Element* host = GetElementById("host");
+  Node* text = host->firstChild();
+  Position positions[] = {
+      Position::BeforeNode(*host),         Position::FirstPositionInNode(*host),
+      Position::LastPositionInNode(*host), Position::AfterNode(*host),
+      Position::BeforeNode(*text),         Position::FirstPositionInNode(*text),
+      Position::LastPositionInNode(*text), Position::AfterNode(*text)};
+  for (const Position& base : positions) {
+    EXPECT_TRUE(base.IsConnected());
+    bool flat_base_is_connected = ToPositionInFlatTree(base).IsConnected();
+    EXPECT_EQ(base.AnchorNode() == host, flat_base_is_connected);
+    for (const Position& extent : positions) {
+      const SelectionInDOMTree& selection =
+          SelectionInDOMTree::Builder().SetBaseAndExtent(base, extent).Build();
+      Selection().SetSelection(selection, SetSelectionOptions());
+      EXPECT_TRUE(extent.IsConnected());
+      bool flat_extent_is_connected =
+          ToPositionInFlatTree(selection.Extent()).IsConnected();
+      EXPECT_EQ(flat_base_is_connected || flat_extent_is_connected
+                    ? "<div id=\"host\"></div>|y"
+                    : "<div id=\"host\"></div>y",
+                GetSelectionTextInFlatTreeFromBody(
+                    GetVisibleSelectionInFlatTree().AsSelection()));
+    }
+  }
+}
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/editing/selection_editor.cc b/third_party/blink/renderer/core/editing/selection_editor.cc
index 31f71d5..778cdb9 100644
--- a/third_party/blink/renderer/core/editing/selection_editor.cc
+++ b/third_party/blink/renderer/core/editing/selection_editor.cc
@@ -452,18 +452,8 @@
   style_version_for_flat_tree_ = GetDocument().StyleVersion();
 #endif
   cached_visible_selection_in_flat_tree_is_dirty_ = false;
-  SelectionInFlatTree::Builder builder;
-  const PositionInFlatTree& base = ToPositionInFlatTree(selection_.Base());
-  const PositionInFlatTree& extent = ToPositionInFlatTree(selection_.Extent());
-  if (base.IsNotNull() && extent.IsNotNull())
-    builder.SetBaseAndExtent(base, extent);
-  else if (base.IsNotNull())
-    builder.Collapse(base);
-  else if (extent.IsNotNull())
-    builder.Collapse(extent);
-  builder.SetAffinity(selection_.Affinity());
   cached_visible_selection_in_flat_tree_ =
-      CreateVisibleSelection(builder.Build());
+      CreateVisibleSelection(ConvertToSelectionInFlatTree(selection_));
   if (!cached_visible_selection_in_flat_tree_.IsNone())
     return;
 #if DCHECK_IS_ON()
diff --git a/third_party/blink/renderer/core/editing/selection_modifier_test.cc b/third_party/blink/renderer/core/editing/selection_modifier_test.cc
index 7f99f28..6d20038 100644
--- a/third_party/blink/renderer/core/editing/selection_modifier_test.cc
+++ b/third_party/blink/renderer/core/editing/selection_modifier_test.cc
@@ -363,9 +363,10 @@
       modifier.Modify(SelectionModifyAlteration::kExtend,
                       SelectionModifyDirection::kForward,
                       TextGranularity::kParagraph);
+      EXPECT_TRUE(extent.IsConnected());
       bool flat_extent_is_connected =
           ToPositionInFlatTree(selection.Extent()).IsConnected();
-      EXPECT_EQ(flat_base_is_connected && flat_extent_is_connected
+      EXPECT_EQ(flat_base_is_connected || flat_extent_is_connected
                     ? "<div id=\"host\">x</div>^y|"
                     : "<div id=\"host\">x</div>y",
                 GetSelectionTextFromBody(modifier.Selection().AsSelection()));
diff --git a/third_party/blink/renderer/core/editing/selection_template.cc b/third_party/blink/renderer/core/editing/selection_template.cc
index 37346799..e505d7bb 100644
--- a/third_party/blink/renderer/core/editing/selection_template.cc
+++ b/third_party/blink/renderer/core/editing/selection_template.cc
@@ -407,14 +407,17 @@
 
 SelectionInFlatTree ConvertToSelectionInFlatTree(
     const SelectionInDOMTree& selection) {
+  SelectionInFlatTree::Builder builder;
   const PositionInFlatTree& base = ToPositionInFlatTree(selection.Base());
   const PositionInFlatTree& extent = ToPositionInFlatTree(selection.Extent());
-  if (!base.IsConnected() || !extent.IsConnected())
-    return SelectionInFlatTree();
-  return SelectionInFlatTree::Builder()
-      .SetAffinity(selection.Affinity())
-      .SetBaseAndExtent(base, extent)
-      .Build();
+  if (base.IsConnected() && extent.IsConnected())
+    builder.SetBaseAndExtent(base, extent);
+  else if (base.IsConnected())
+    builder.Collapse(base);
+  else if (extent.IsConnected())
+    builder.Collapse(extent);
+  builder.SetAffinity(selection.Affinity());
+  return builder.Build();
 }
 
 template <typename Strategy>
diff --git a/third_party/blink/renderer/core/frame/csp/conversion_util_fuzzer.cc b/third_party/blink/renderer/core/frame/csp/conversion_util_fuzzer.cc
index eba92aee..596997b 100644
--- a/third_party/blink/renderer/core/frame/csp/conversion_util_fuzzer.cc
+++ b/third_party/blink/renderer/core/frame/csp/conversion_util_fuzzer.cc
@@ -33,6 +33,7 @@
     // Due to this quadratic behavior, we must limit the size of the origin to
     // prevent the fuzzer from triggering OOM crash. Note that real domain names
     // are limited to 253 characters.
+    return EXIT_SUCCESS;
   }
 
   String url = String(data, it - 1 - data);
diff --git a/third_party/blink/renderer/core/layout/depth_ordered_layout_object_list.cc b/third_party/blink/renderer/core/layout/depth_ordered_layout_object_list.cc
index 95f116cc..1c57758 100644
--- a/third_party/blink/renderer/core/layout/depth_ordered_layout_object_list.cc
+++ b/third_party/blink/renderer/core/layout/depth_ordered_layout_object_list.cc
@@ -5,8 +5,10 @@
 #include "third_party/blink/renderer/core/layout/depth_ordered_layout_object_list.h"
 
 #include <algorithm>
+#include "third_party/blink/renderer/core/css/style_engine.h"
 #include "third_party/blink/renderer/core/frame/local_frame_view.h"
 #include "third_party/blink/renderer/core/layout/layout_object.h"
+#include "third_party/blink/renderer/core/layout/ng/legacy_layout_tree_walking.h"
 
 namespace blink {
 
@@ -47,8 +49,22 @@
   return data_->objects().IsEmpty();
 }
 
+namespace {
+
+bool ListModificationAllowedFor(const LayoutObject& object) {
+  if (!object.GetFrameView()->IsInPerformLayout())
+    return true;
+  // We are allowed to insert/remove orthogonal writing mode roots during
+  // layout for interleaved style recalcs, but only when these roots are fully
+  // managed by LayoutNG.
+  return object.GetDocument().GetStyleEngine().InContainerQueryStyleRecalc() &&
+         IsManagedByLayoutNG(object);
+}
+
+}  // namespace
+
 void DepthOrderedLayoutObjectList::Add(LayoutObject& object) {
-  DCHECK(!object.GetFrameView()->IsInPerformLayout());
+  DCHECK(ListModificationAllowedFor(object));
   data_->objects().insert(&object);
   data_->ordered_objects().clear();
 }
@@ -57,7 +73,7 @@
   auto it = data_->objects().find(&object);
   if (it == data_->objects().end())
     return;
-  DCHECK(!object.GetFrameView()->IsInPerformLayout());
+  DCHECK(ListModificationAllowedFor(object));
   data_->objects().erase(it);
   data_->ordered_objects().clear();
 }
diff --git a/third_party/blink/renderer/core/layout/layout_box.cc b/third_party/blink/renderer/core/layout/layout_box.cc
index ff12d41..3b404ec 100644
--- a/third_party/blink/renderer/core/layout/layout_box.cc
+++ b/third_party/blink/renderer/core/layout/layout_box.cc
@@ -3568,6 +3568,12 @@
       cache_status == NGLayoutCacheStatus::kHit)
     cache_status = NGLayoutCacheStatus::kNeedsSimplifiedLayout;
 
+  // Only allow simplified layout for non-replaced boxes.
+  if (RuntimeEnabledFeatures::LayoutNGReplacedEnabled() &&
+      cache_status == NGLayoutCacheStatus::kNeedsSimplifiedLayout &&
+      IsLayoutReplaced())
+    return nullptr;
+
   LayoutUnit bfc_line_offset = new_space.BfcOffset().line_offset;
   base::Optional<LayoutUnit> bfc_block_offset =
       cached_layout_result->BfcBlockOffset();
diff --git a/third_party/blink/renderer/core/layout/ng/inline/layout_ng_text_test.cc b/third_party/blink/renderer/core/layout/ng/inline/layout_ng_text_test.cc
index 63d0de7..e7c2849 100644
--- a/third_party/blink/renderer/core/layout/ng/inline/layout_ng_text_test.cc
+++ b/third_party/blink/renderer/core/layout/ng/inline/layout_ng_text_test.cc
@@ -5,18 +5,14 @@
 #include "third_party/blink/renderer/core/layout/ng/inline/layout_ng_text.h"
 
 #include <sstream>
-#include "testing/gmock/include/gmock/gmock.h"
-#include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/blink/renderer/core/layout/layout_block_flow.h"
 #include "third_party/blink/renderer/core/layout/ng/inline/ng_inline_item.h"
 #include "third_party/blink/renderer/core/layout/ng/inline/ng_inline_node_data.h"
-#include "third_party/blink/renderer/core/testing/page_test_base.h"
-#include "third_party/blink/renderer/platform/runtime_enabled_features.h"
-#include "third_party/blink/renderer/platform/testing/runtime_enabled_features_test_helpers.h"
+#include "third_party/blink/renderer/core/layout/ng/ng_layout_test.h"
 
 namespace blink {
 
-class LayoutNGTextTest : public PageTestBase {
+class LayoutNGTextTest : public NGLayoutTest {
  protected:
   std::string GetItemsAsString(const LayoutText& layout_text) {
     if (layout_text.NeedsCollectInlines())
@@ -48,9 +44,6 @@
 };
 
 TEST_F(LayoutNGTextTest, SetTextWithOffsetAppendBidi) {
-  if (!RuntimeEnabledFeatures::LayoutNGEnabled())
-    return;
-
   SetBodyInnerHTML(u"<div dir=rtl id=target>\u05D0\u05D1\u05BC\u05D2</div>");
   Text& text = To<Text>(*GetElementById("target")->firstChild());
   text.appendData(u"\u05D0\u05D1\u05BC\u05D2");
@@ -62,9 +55,6 @@
 }
 
 TEST_F(LayoutNGTextTest, SetTextWithOffsetAppendControl) {
-  if (!RuntimeEnabledFeatures::LayoutNGEnabled())
-    return;
-
   SetBodyInnerHTML(u"<pre id=target>a</pre>");
   Text& text = To<Text>(*GetElementById("target")->firstChild());
   // Note: "\n" is control character instead of text character.
@@ -77,9 +67,6 @@
 }
 
 TEST_F(LayoutNGTextTest, SetTextWithOffsetAppendCollapseWhiteSpace) {
-  if (!RuntimeEnabledFeatures::LayoutNGEnabled())
-    return;
-
   SetBodyInnerHTML(u"<p id=target>abc </p>");
   Text& text = To<Text>(*GetElementById("target")->firstChild());
   text.appendData("XYZ");
@@ -89,9 +76,6 @@
 }
 
 TEST_F(LayoutNGTextTest, SetTextWithOffsetAppend) {
-  if (!RuntimeEnabledFeatures::LayoutNGEnabled())
-    return;
-
   SetBodyInnerHTML(u"<pre id=target><a>abc</a>XYZ<b>def</b></pre>");
   Text& text = To<Text>(*GetElementById("target")->firstChild()->nextSibling());
   text.appendData("xyz");
@@ -104,9 +88,6 @@
 }
 
 TEST_F(LayoutNGTextTest, SetTextWithOffsetDelete) {
-  if (!RuntimeEnabledFeatures::LayoutNGEnabled())
-    return;
-
   SetBodyInnerHTML(u"<pre id=target><a>abc</a>xXYZyz<b>def</b></pre>");
   Text& text = To<Text>(*GetElementById("target")->firstChild()->nextSibling());
   text.deleteData(1, 3, ASSERT_NO_EXCEPTION);
@@ -119,9 +100,6 @@
 }
 
 TEST_F(LayoutNGTextTest, SetTextWithOffsetDeleteCollapseWhiteSpace) {
-  if (!RuntimeEnabledFeatures::LayoutNGEnabled())
-    return;
-
   SetBodyInnerHTML(u"<p id=target>ab  XY  cd</p>");
   Text& text = To<Text>(*GetElementById("target")->firstChild());
   text.deleteData(4, 2, ASSERT_NO_EXCEPTION);  // remove "XY"
@@ -131,9 +109,6 @@
 }
 
 TEST_F(LayoutNGTextTest, SetTextWithOffsetDeleteCollapseWhiteSpaceEnd) {
-  if (!RuntimeEnabledFeatures::LayoutNGEnabled())
-    return;
-
   SetBodyInnerHTML(u"<p id=target>a bc</p>");
   Text& text = To<Text>(*GetElementById("target")->firstChild());
   text.deleteData(2, 2, ASSERT_NO_EXCEPTION);  // remove "bc"
@@ -145,9 +120,6 @@
 // web_tests/external/wpt/editing/run/delete.html?993-993
 // web_tests/external/wpt/editing/run/forwarddelete.html?1193-1193
 TEST_F(LayoutNGTextTest, SetTextWithOffsetDeleteNbspInPreWrap) {
-  if (!RuntimeEnabledFeatures::LayoutNGEnabled())
-    return;
-
   InsertStyleElement("#target { white-space:pre-wrap; }");
   SetBodyInnerHTML(u"<p id=target>&nbsp; abc</p>");
   Text& text = To<Text>(*GetElementById("target")->firstChild());
@@ -160,9 +132,6 @@
 }
 
 TEST_F(LayoutNGTextTest, SetTextWithOffsetDeleteRTL) {
-  if (!RuntimeEnabledFeatures::LayoutNGEnabled())
-    return;
-
   SetBodyInnerHTML(u"<p id=target dir=rtl>0 234</p>");
   Text& text = To<Text>(*GetElementById("target")->firstChild());
   text.deleteData(2, 2, ASSERT_NO_EXCEPTION);  // remove "23"
@@ -176,9 +145,6 @@
 
 // http://crbug.com/1000685
 TEST_F(LayoutNGTextTest, SetTextWithOffsetDeleteRTL2) {
-  if (!RuntimeEnabledFeatures::LayoutNGEnabled())
-    return;
-
   SetBodyInnerHTML(u"<p id=target dir=rtl>0(xy)5</p>");
   Text& text = To<Text>(*GetElementById("target")->firstChild());
   text.deleteData(0, 1, ASSERT_NO_EXCEPTION);  // remove "0"
@@ -193,9 +159,6 @@
 
 // editing/deleting/delete_ws_fixup.html
 TEST_F(LayoutNGTextTest, SetTextWithOffsetDeleteThenNonCollapse) {
-  if (!RuntimeEnabledFeatures::LayoutNGEnabled())
-    return;
-
   SetBodyInnerHTML(u"<div id=target>abc def<b> </b>ghi</div>");
   Text& text = To<Text>(*GetElementById("target")->firstChild());
   text.deleteData(4, 3, ASSERT_NO_EXCEPTION);  // remove "def"
@@ -209,9 +172,6 @@
 
 // editing/deleting/delete_ws_fixup.html
 TEST_F(LayoutNGTextTest, SetTextWithOffsetDeleteThenNonCollapse2) {
-  if (!RuntimeEnabledFeatures::LayoutNGEnabled())
-    return;
-
   SetBodyInnerHTML(u"<div id=target>abc def<b> X </b>ghi</div>");
   Text& text = To<Text>(*GetElementById("target")->firstChild());
   text.deleteData(4, 3, ASSERT_NO_EXCEPTION);  // remove "def"
@@ -225,9 +185,6 @@
 
 // http://crbug.com/1039143
 TEST_F(LayoutNGTextTest, SetTextWithOffsetDeleteWithBidiControl) {
-  if (!RuntimeEnabledFeatures::LayoutNGEnabled())
-    return;
-
   // In text content, we have bidi control codes:
   // U+2066 U+2069 \n U+2066 abc U+2066
   SetBodyInnerHTML(u"<pre><b id=target dir=ltr>\nabc</b></pre>");
@@ -240,9 +197,6 @@
 
 // http://crbug.com/1125262
 TEST_F(LayoutNGTextTest, SetTextWithOffsetDeleteWithGeneratedBreakOpportunity) {
-  if (!RuntimeEnabledFeatures::LayoutNGEnabled())
-    return;
-
   InsertStyleElement("#target { white-space:nowrap; }");
   SetBodyInnerHTML(u"<p><b><i id=target>ab\n</i>\n</b>\n</div>");
   // We have two ZWS for "</i>\n" and "</b>\n".
@@ -258,8 +212,6 @@
 
 // http://crbug.com/1123251
 TEST_F(LayoutNGTextTest, SetTextWithOffsetEditingTextCollapsedSpace) {
-  if (!RuntimeEnabledFeatures::LayoutNGEnabled())
-    return;
   SetBodyInnerHTML(u"<p id=target></p>");
   // Simulate: insertText("A") + InsertHTML("X ")
   Text& text = *GetDocument().CreateEditingTextNode("AX ");
@@ -272,9 +224,6 @@
 }
 
 TEST_F(LayoutNGTextTest, SetTextWithOffsetInsert) {
-  if (!RuntimeEnabledFeatures::LayoutNGEnabled())
-    return;
-
   SetBodyInnerHTML(u"<pre id=target><a>abc</a>XYZ<b>def</b></pre>");
   Text& text = To<Text>(*GetElementById("target")->firstChild()->nextSibling());
   text.insertData(1, "xyz", ASSERT_NO_EXCEPTION);
@@ -287,9 +236,6 @@
 }
 
 TEST_F(LayoutNGTextTest, SetTextWithOffsetInsertAfterSpace) {
-  if (!RuntimeEnabledFeatures::LayoutNGEnabled())
-    return;
-
   SetBodyInnerHTML(u"<p id=target>ab cd</p>");
   Text& text = To<Text>(*GetElementById("target")->firstChild());
   text.insertData(3, " XYZ ", ASSERT_NO_EXCEPTION);
@@ -299,9 +245,6 @@
 }
 
 TEST_F(LayoutNGTextTest, SetTextWithOffsetInserBeforetSpace) {
-  if (!RuntimeEnabledFeatures::LayoutNGEnabled())
-    return;
-
   SetBodyInnerHTML(u"<p id=target>ab cd</p>");
   Text& text = To<Text>(*GetElementById("target")->firstChild());
   text.insertData(2, " XYZ ", ASSERT_NO_EXCEPTION);
@@ -311,9 +254,6 @@
 }
 
 TEST_F(LayoutNGTextTest, SetTextWithOffsetNoRelocation) {
-  if (!RuntimeEnabledFeatures::LayoutNGEnabled())
-    return;
-
   SetBodyInnerHTML(u"<pre id=target><a>abc</a>XYZ<b>def</b></pre>");
   Text& text = To<Text>(*GetElementById("target")->firstChild()->nextSibling());
   // Note: |CharacterData::setData()| is implementation of Node::setNodeValue()
@@ -326,9 +266,6 @@
 }
 
 TEST_F(LayoutNGTextTest, SetTextWithOffsetPrepend) {
-  if (!RuntimeEnabledFeatures::LayoutNGEnabled())
-    return;
-
   SetBodyInnerHTML(u"<pre id=target><a>abc</a>XYZ<b>def</b></pre>");
   Text& text = To<Text>(*GetElementById("target")->firstChild()->nextSibling());
   text.insertData(1, "xyz", ASSERT_NO_EXCEPTION);
@@ -341,9 +278,6 @@
 }
 
 TEST_F(LayoutNGTextTest, SetTextWithOffsetReplace) {
-  if (!RuntimeEnabledFeatures::LayoutNGEnabled())
-    return;
-
   SetBodyInnerHTML(u"<pre id=target><a>abc</a>XYZW<b>def</b></pre>");
   Text& text = To<Text>(*GetElementById("target")->firstChild()->nextSibling());
   text.replaceData(1, 2, "yz", ASSERT_NO_EXCEPTION);
@@ -356,9 +290,6 @@
 }
 
 TEST_F(LayoutNGTextTest, SetTextWithOffsetReplaceCollapseWhiteSpace) {
-  if (!RuntimeEnabledFeatures::LayoutNGEnabled())
-    return;
-
   SetBodyInnerHTML(u"<p id=target>ab  XY  cd</p>");
   Text& text = To<Text>(*GetElementById("target")->firstChild());
   text.replaceData(4, 2, " ", ASSERT_NO_EXCEPTION);  // replace "XY" to " "
@@ -368,9 +299,6 @@
 }
 
 TEST_F(LayoutNGTextTest, SetTextWithOffsetReplaceToExtend) {
-  if (!RuntimeEnabledFeatures::LayoutNGEnabled())
-    return;
-
   SetBodyInnerHTML(u"<pre id=target><a>abc</a>XYZW<b>def</b></pre>");
   Text& text = To<Text>(*GetElementById("target")->firstChild()->nextSibling());
   text.replaceData(1, 2, "xyz", ASSERT_NO_EXCEPTION);
@@ -383,9 +311,6 @@
 }
 
 TEST_F(LayoutNGTextTest, SetTextWithOffsetReplaceToShrink) {
-  if (!RuntimeEnabledFeatures::LayoutNGEnabled())
-    return;
-
   SetBodyInnerHTML(u"<pre id=target><a>abc</a>XYZW<b>def</b></pre>");
   Text& text = To<Text>(*GetElementById("target")->firstChild()->nextSibling());
   text.replaceData(1, 2, "y", ASSERT_NO_EXCEPTION);
@@ -398,9 +323,6 @@
 }
 
 TEST_F(LayoutNGTextTest, SetTextWithOffsetToEmpty) {
-  if (!RuntimeEnabledFeatures::LayoutNGEnabled())
-    return;
-
   SetBodyInnerHTML(u"<pre id=target><a>abc</a>XYZ<b>def</b></pre>");
   Text& text = To<Text>(*GetElementById("target")->firstChild()->nextSibling());
   // Note: |CharacterData::setData()| is implementation of Node::setNodeValue()
diff --git a/third_party/blink/renderer/core/layout/ng/ng_length_utils.cc b/third_party/blink/renderer/core/layout/ng/ng_length_utils.cc
index 57c912c..442c5ba2 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_length_utils.cc
+++ b/third_party/blink/renderer/core/layout/ng/ng_length_utils.cc
@@ -792,6 +792,7 @@
 // Computes size for a replaced element.
 LogicalSize ComputeReplacedSize(const NGBlockNode& node,
                                 const NGConstraintSpace& space,
+                                const NGBoxStrut& border_padding,
                                 ReplacedSizeMode mode) {
   DCHECK(node.IsReplaced());
 
@@ -804,8 +805,6 @@
     return LogicalSize();
 
   const ComputedStyle& style = node.Style();
-  const NGBoxStrut border_padding =
-      ComputeBorders(space, node) + ComputePadding(space, style);
   const EBoxSizing box_sizing = style.BoxSizingForAspectRatio();
 
   // Replaced elements in quirks-mode resolve their min/max block-sizes against
@@ -1444,7 +1443,7 @@
 
   if (node.IsReplaced()) {
     const LogicalSize border_box_size =
-        ComputeReplacedSize(node, constraint_space);
+        ComputeReplacedSize(node, constraint_space, border_padding);
     return {border_box_size, border, scrollbar, padding};
   }
 
diff --git a/third_party/blink/renderer/core/layout/ng/ng_length_utils.h b/third_party/blink/renderer/core/layout/ng/ng_length_utils.h
index c117a949..45ab7c4 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_length_utils.h
+++ b/third_party/blink/renderer/core/layout/ng/ng_length_utils.h
@@ -484,6 +484,7 @@
 CORE_EXPORT LogicalSize
 ComputeReplacedSize(const NGBlockNode&,
                     const NGConstraintSpace&,
+                    const NGBoxStrut& border_padding,
                     ReplacedSizeMode = ReplacedSizeMode::kNormal);
 
 // Based on available inline size, CSS computed column-width, CSS computed
diff --git a/third_party/blink/renderer/core/layout/ng/ng_out_of_flow_layout_part.cc b/third_party/blink/renderer/core/layout/ng/ng_out_of_flow_layout_part.cc
index 6d9ac6c1..ace8497 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_out_of_flow_layout_part.cc
+++ b/third_party/blink/renderer/core/layout/ng/ng_out_of_flow_layout_part.cc
@@ -990,8 +990,8 @@
 
   base::Optional<LogicalSize> replaced_size;
   if (node_info.node.IsReplaced()) {
-    replaced_size =
-        ComputeReplacedSize(node_info.node, node_info.constraint_space);
+    replaced_size = ComputeReplacedSize(
+        node_info.node, node_info.constraint_space, border_padding);
   }
 
   offset_info.inline_size_depends_on_min_max_sizes =
diff --git a/third_party/blink/renderer/core/layout/ng/ng_replaced_layout_algorithm.cc b/third_party/blink/renderer/core/layout/ng/ng_replaced_layout_algorithm.cc
index dc04e66b..03474d3 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_replaced_layout_algorithm.cc
+++ b/third_party/blink/renderer/core/layout/ng/ng_replaced_layout_algorithm.cc
@@ -19,7 +19,7 @@
   container_builder_.SetIsLegacyLayoutRoot();
 
   const LayoutUnit intrinsic_block_size =
-      ComputeReplacedSize(Node(), ConstraintSpace(),
+      ComputeReplacedSize(Node(), ConstraintSpace(), BorderPadding(),
                           ReplacedSizeMode::kIgnoreBlockLengths)
           .block_size;
   container_builder_.SetIntrinsicBlockSize(intrinsic_block_size);
@@ -34,7 +34,7 @@
   // This is only used by flex, which expects inline-lengths to be ignored for
   // the min/max content size.
   MinMaxSizes sizes;
-  sizes = ComputeReplacedSize(Node(), ConstraintSpace(),
+  sizes = ComputeReplacedSize(Node(), ConstraintSpace(), BorderPadding(),
                               ReplacedSizeMode::kIgnoreInlineLengths)
               .inline_size;
   return {sizes, /* depends_on_block_constraints */ false};
diff --git a/third_party/blink/renderer/core/layout/ng/ng_simplified_layout_algorithm.cc b/third_party/blink/renderer/core/layout/ng/ng_simplified_layout_algorithm.cc
index 6127306..0da8823 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_simplified_layout_algorithm.cc
+++ b/third_party/blink/renderer/core/layout/ng/ng_simplified_layout_algorithm.cc
@@ -27,6 +27,8 @@
     : NGLayoutAlgorithm(params),
       previous_result_(result),
       writing_direction_(Style().GetWritingDirection()) {
+  DCHECK(!Node().IsReplaced());
+
   const bool is_block_flow = Node().IsBlockFlow();
   const NGPhysicalBoxFragment& physical_fragment =
       To<NGPhysicalBoxFragment>(result.PhysicalFragment());
diff --git a/third_party/blink/renderer/core/scroll/scrollbar_theme_mac.mm b/third_party/blink/renderer/core/scroll/scrollbar_theme_mac.mm
index 1148ab01..7d9527a 100644
--- a/third_party/blink/renderer/core/scroll/scrollbar_theme_mac.mm
+++ b/third_party/blink/renderer/core/scroll/scrollbar_theme_mac.mm
@@ -358,16 +358,6 @@
         base::scoped_policy::RETAIN);
     ScrollbarPainter scrollbar_painter = [observer painter];
     [scrollbar_painter setEnabled:scrollbar.Enabled()];
-    // drawKnob aligns the thumb to right side of the draw rect.
-    // If the vertical overlay scrollbar is on the left, use trackWidth instead
-    // of scrollbar width, to avoid the gap on the left side of the thumb.
-    IntRect draw_rect = IntRect(rect);
-    if (UsesOverlayScrollbars() && scrollbar.IsLeftSideVerticalScrollbar()) {
-      int thumb_width = [scrollbar_painter trackWidth];
-      draw_rect.SetWidth(thumb_width);
-    }
-    [scrollbar_painter
-        setBoundsSize:NSSizeFromCGSize(CGSize(draw_rect.Size()))];
 
     [scrollbar_painter setDoubleValue:0];
     [scrollbar_painter setKnobProportion:1];
diff --git a/third_party/blink/renderer/modules/accessibility/ax_object.cc b/third_party/blink/renderer/modules/accessibility/ax_object.cc
index 70cbd66..0f183e36 100644
--- a/third_party/blink/renderer/modules/accessibility/ax_object.cc
+++ b/third_party/blink/renderer/modules/accessibility/ax_object.cc
@@ -160,7 +160,15 @@
 
 #endif
 
-String GetElementString(Element* element) {
+String GetNodeString(Node* node) {
+  if (node->IsTextNode()) {
+    String string_builder = "\"";
+    string_builder = string_builder + node->nodeValue();
+    string_builder = string_builder + "\"";
+    return string_builder;
+  }
+
+  Element* element = DynamicTo<Element>(node);
   if (!element)
     return "<null>";
 
@@ -1995,7 +2003,8 @@
       DisplayLockUtilities::NearestLockedExclusiveAncestor(*GetNode())) {
     DCHECK(!cached_is_ignored_but_included_in_tree_)
         << "Display locked text should not be included in the tree (subject to "
-           "future rule change)";
+           "future rule change): "
+        << ToString(true, true);
   }
 #endif
   bool included_in_tree_changed = false;
@@ -5520,8 +5529,8 @@
   if (verbose) {
     string_builder = string_builder + " axid#" + String::Number(AXObjectID());
     // Add useful HTML element info, like <div.myClass#myId>.
-    if (GetElement())
-      string_builder = string_builder + " " + GetElementString(GetElement());
+    if (GetNode())
+      string_builder = string_builder + " " + GetNodeString(GetNode());
 
     // Add properties of interest that often contribute to errors:
     if (HasARIAOwns(GetElement())) {
@@ -5582,7 +5591,7 @@
         string_builder = string_builder + " ariaHiddenRoot";
         if (aria_hidden_root != this) {
           string_builder =
-              string_builder + GetElementString(aria_hidden_root->GetElement());
+              string_builder + GetNodeString(aria_hidden_root->GetNode());
         }
       }
     }
diff --git a/third_party/blink/renderer/modules/breakout_box/frame_queue_underlying_source.cc b/third_party/blink/renderer/modules/breakout_box/frame_queue_underlying_source.cc
index 2d059a95..37a5f92 100644
--- a/third_party/blink/renderer/modules/breakout_box/frame_queue_underlying_source.cc
+++ b/third_party/blink/renderer/modules/breakout_box/frame_queue_underlying_source.cc
@@ -5,8 +5,6 @@
 #include "third_party/blink/renderer/modules/breakout_box/frame_queue_underlying_source.h"
 
 #include "base/bind_post_task.h"
-#include "media/base/audio_buffer.h"
-#include "media/base/video_frame.h"
 #include "third_party/blink/renderer/bindings/core/v8/v8_throw_dom_exception.h"
 #include "third_party/blink/renderer/core/dom/dom_exception.h"
 #include "third_party/blink/renderer/core/execution_context/execution_context.h"
diff --git a/third_party/blink/renderer/modules/breakout_box/frame_queue_underlying_source.h b/third_party/blink/renderer/modules/breakout_box/frame_queue_underlying_source.h
index 40ee349..bf7502f 100644
--- a/third_party/blink/renderer/modules/breakout_box/frame_queue_underlying_source.h
+++ b/third_party/blink/renderer/modules/breakout_box/frame_queue_underlying_source.h
@@ -6,14 +6,12 @@
 #define THIRD_PARTY_BLINK_RENDERER_MODULES_BREAKOUT_BOX_FRAME_QUEUE_UNDERLYING_SOURCE_H_
 
 #include "base/threading/thread_checker.h"
+#include "media/base/audio_buffer.h"
+#include "media/base/video_frame.h"
 #include "third_party/blink/renderer/core/streams/underlying_source_base.h"
 #include "third_party/blink/renderer/modules/modules_export.h"
 #include "third_party/blink/renderer/platform/wtf/deque.h"
 
-namespace media {
-class AudioBuffer;
-}
-
 namespace blink {
 
 template <typename NativeFrameType>
diff --git a/third_party/blink/renderer/modules/webcodecs/audio_decoder.cc b/third_party/blink/renderer/modules/webcodecs/audio_decoder.cc
index 6633824..c228be5 100644
--- a/third_party/blink/renderer/modules/webcodecs/audio_decoder.cc
+++ b/third_party/blink/renderer/modules/webcodecs/audio_decoder.cc
@@ -89,6 +89,8 @@
       decoder.IsPlatformDecoder());
   media_log->SetProperty<media::MediaLogProperty::kAudioTracks>(
       std::vector<MediaConfigType>{media_config});
+  MEDIA_LOG(INFO, media_log)
+      << "Initialized AudioDecoder: " << media_config.AsHumanReadableString();
 }
 
 // static
@@ -124,6 +126,7 @@
 // static
 void AudioDecoderTraits::InitializeDecoder(
     MediaDecoderType& decoder,
+    bool /*low_delay*/,
     const MediaConfigType& media_config,
     MediaDecoderType::InitCB init_cb,
     MediaDecoderType::OutputCB output_cb) {
diff --git a/third_party/blink/renderer/modules/webcodecs/audio_decoder.h b/third_party/blink/renderer/modules/webcodecs/audio_decoder.h
index 8fdd64d..420e5cd1e 100644
--- a/third_party/blink/renderer/modules/webcodecs/audio_decoder.h
+++ b/third_party/blink/renderer/modules/webcodecs/audio_decoder.h
@@ -58,6 +58,7 @@
       media::GpuVideoAcceleratorFactories* gpu_factories,
       media::MediaLog* media_log);
   static void InitializeDecoder(MediaDecoderType& decoder,
+                                bool low_delay,
                                 const MediaConfigType& media_config,
                                 MediaDecoderType::InitCB init_cb,
                                 MediaDecoderType::OutputCB output_cb);
diff --git a/third_party/blink/renderer/modules/webcodecs/audio_decoder_broker.cc b/third_party/blink/renderer/modules/webcodecs/audio_decoder_broker.cc
index 200cbff..ddb5e4923 100644
--- a/third_party/blink/renderer/modules/webcodecs/audio_decoder_broker.cc
+++ b/third_party/blink/renderer/modules/webcodecs/audio_decoder_broker.cc
@@ -124,8 +124,9 @@
                            weak_factory_.GetWeakPtr()));
 
     selector_->SelectDecoder(
-        config, WTF::Bind(&MediaAudioTaskWrapper::OnDecoderSelected,
-                          weak_factory_.GetWeakPtr()));
+        config, /*low_delay=*/false,
+        WTF::Bind(&MediaAudioTaskWrapper::OnDecoderSelected,
+                  weak_factory_.GetWeakPtr()));
   }
 
   void Decode(scoped_refptr<media::DecoderBuffer> buffer, int cb_id) {
diff --git a/third_party/blink/renderer/modules/webcodecs/decoder_selector.cc b/third_party/blink/renderer/modules/webcodecs/decoder_selector.cc
index 87aaf932..da52a10 100644
--- a/third_party/blink/renderer/modules/webcodecs/decoder_selector.cc
+++ b/third_party/blink/renderer/modules/webcodecs/decoder_selector.cc
@@ -50,11 +50,18 @@
     return true;
   }
 
+  void set_low_delay(bool low_delay) { low_delay_ = low_delay; }
+  media::DemuxerStream::Liveness liveness() const override {
+    return low_delay_ ? media::DemuxerStream::LIVENESS_LIVE
+                      : media::DemuxerStream::LIVENESS_UNKNOWN;
+  }
+
  private:
   static const media::DemuxerStream::Type stream_type = StreamType;
 
   media::AudioDecoderConfig audio_decoder_config_;
   media::VideoDecoderConfig video_decoder_config_;
+  bool low_delay_ = false;
 };
 
 template <>
@@ -90,9 +97,11 @@
 template <media::DemuxerStream::Type StreamType>
 void DecoderSelector<StreamType>::SelectDecoder(
     const DecoderConfig& config,
+    bool low_delay,
     SelectDecoderCB select_decoder_cb) {
   // |impl_| will internally use this the |config| from our NullDemuxerStream.
   demuxer_stream_->Configure(config);
+  demuxer_stream_->set_low_delay(low_delay);
 
   // media::DecoderSelector will call back with a null decoder if selection is
   // in progress when it is destructed.
diff --git a/third_party/blink/renderer/modules/webcodecs/decoder_selector.h b/third_party/blink/renderer/modules/webcodecs/decoder_selector.h
index e907229..86aae0d6 100644
--- a/third_party/blink/renderer/modules/webcodecs/decoder_selector.h
+++ b/third_party/blink/renderer/modules/webcodecs/decoder_selector.h
@@ -54,6 +54,7 @@
   // be returned via |select_decoder_cb| posted to |task_runner_|. Subsequent
   // calls will again select from the full list of decoders.
   void SelectDecoder(const DecoderConfig& config,
+                     bool low_delay,
                      SelectDecoderCB select_decoder_cb);
 
  private:
diff --git a/third_party/blink/renderer/modules/webcodecs/decoder_selector_test.cc b/third_party/blink/renderer/modules/webcodecs/decoder_selector_test.cc
index a0221cd..4b0e73d 100644
--- a/third_party/blink/renderer/modules/webcodecs/decoder_selector_test.cc
+++ b/third_party/blink/renderer/modules/webcodecs/decoder_selector_test.cc
@@ -58,7 +58,8 @@
   // Decoder::Initialize() takes different parameters depending on the type.
   static void ExpectInitialize(MockDecoder* decoder,
                                DecoderCapability capability,
-                               media::AudioDecoderConfig expected_config) {
+                               media::AudioDecoderConfig expected_config,
+                               bool /*low_delay */) {
     EXPECT_CALL(*decoder, Initialize_(_, _, _, _, _))
         .WillRepeatedly([capability, expected_config](
                             const media::AudioDecoderConfig& config,
@@ -96,8 +97,9 @@
 
   static void ExpectInitialize(MockDecoder* decoder,
                                DecoderCapability capability,
-                               media::VideoDecoderConfig expected_config) {
-    EXPECT_CALL(*decoder, Initialize_(_, _, _, _, _, _))
+                               media::VideoDecoderConfig expected_config,
+                               bool low_delay) {
+    EXPECT_CALL(*decoder, Initialize_(_, low_delay, _, _, _, _))
         .WillRepeatedly([capability, expected_config](
                             const media::VideoDecoderConfig& config,
                             bool low_delay, media::CdmContext*,
@@ -162,7 +164,7 @@
               /*is_platform_decoder=*/false, /*supports_decryption=*/true,
               info.first);
       TypeParam::ExpectInitialize(decoder.get(), info.second,
-                                  last_set_decoder_config_);
+                                  last_set_decoder_config_, low_delay_);
       decoders.push_back(std::move(decoder));
     }
 
@@ -180,7 +182,7 @@
   void SelectDecoder(DecoderConfig config = TypeParam::CreateConfig()) {
     last_set_decoder_config_ = config;
     decoder_selector_->SelectDecoder(
-        config,
+        config, low_delay_,
         base::BindOnce(&Self::OnDecoderSelectedThunk, base::Unretained(this)));
     RunUntilIdle();
   }
@@ -196,6 +198,8 @@
 
   std::vector<std::pair<int, DecoderCapability>> mock_decoders_to_create_;
 
+  bool low_delay_ = false;
+
  private:
   DISALLOW_COPY_AND_ASSIGN(WebCodecsDecoderSelectorTest);
 };
@@ -218,6 +222,14 @@
   this->SelectDecoder();
 }
 
+TYPED_TEST(WebCodecsDecoderSelectorTest, LowDelay) {
+  this->low_delay_ = true;
+  this->AddMockDecoder(kDecoder1, kSucceed);
+
+  EXPECT_CALL(*this, OnDecoderSelected(kDecoder1));
+  this->SelectDecoder();
+}
+
 TYPED_TEST(WebCodecsDecoderSelectorTest, TwoDecoders) {
   this->AddMockDecoder(kDecoder1, kFail);
   this->AddMockDecoder(kDecoder2, kSucceed);
diff --git a/third_party/blink/renderer/modules/webcodecs/decoder_template.cc b/third_party/blink/renderer/modules/webcodecs/decoder_template.cc
index b761b27..74515bd 100644
--- a/third_party/blink/renderer/modules/webcodecs/decoder_template.cc
+++ b/third_party/blink/renderer/modules/webcodecs/decoder_template.cc
@@ -129,6 +129,11 @@
 }
 
 template <typename Traits>
+bool DecoderTemplate<Traits>::GetLowDelayPreference(const ConfigType&) {
+  return false;
+}
+
+template <typename Traits>
 void DecoderTemplate<Traits>::SetHardwarePreference(HardwarePreference) {}
 
 template <typename Traits>
@@ -163,6 +168,7 @@
   request->media_config = std::move(media_config);
   request->reset_generation = reset_generation_;
   request->hw_pref = GetHardwarePreference(*config);
+  request->low_delay = GetLowDelayPreference(*config);
   requests_.push_back(request);
   ProcessRequests();
 }
@@ -300,7 +306,7 @@
     SetHardwarePreference(pending_request_->hw_pref);
 
     Traits::InitializeDecoder(
-        *decoder_, *pending_request_->media_config,
+        *decoder_, pending_request_->low_delay, *pending_request_->media_config,
         WTF::Bind(&DecoderTemplate::OnInitializeDone, WrapWeakPersistent(this)),
         WTF::BindRepeating(&DecoderTemplate::OnOutput, WrapWeakPersistent(this),
                            reset_generation_));
@@ -509,7 +515,8 @@
 
   // Processing continues in OnInitializeDone().
   Traits::InitializeDecoder(
-      *decoder_, is_flush ? *active_config_ : *pending_request_->media_config,
+      *decoder_, is_flush ? low_delay_ : pending_request_->low_delay,
+      is_flush ? *active_config_ : *pending_request_->media_config,
       WTF::Bind(&DecoderTemplate::OnInitializeDone, WrapWeakPersistent(this)),
       WTF::BindRepeating(&DecoderTemplate::OnOutput, WrapWeakPersistent(this),
                          reset_generation_));
@@ -547,6 +554,7 @@
     Traits::UpdateDecoderLog(*decoder_, *pending_request_->media_config,
                              logger_->log());
 
+    low_delay_ = pending_request_->low_delay;
     active_config_ = std::move(pending_request_->media_config);
     pending_request_.Release();
   }
diff --git a/third_party/blink/renderer/modules/webcodecs/decoder_template.h b/third_party/blink/renderer/modules/webcodecs/decoder_template.h
index 92f1922..b8fbd08 100644
--- a/third_party/blink/renderer/modules/webcodecs/decoder_template.h
+++ b/third_party/blink/renderer/modules/webcodecs/decoder_template.h
@@ -78,6 +78,10 @@
   // If derived classes do not override this, this will default to kAllow.
   virtual HardwarePreference GetHardwarePreference(const ConfigType& config);
 
+  // Get the low delay preference from a config.
+  // If derived classes do not override this, this will default to false.
+  virtual bool GetLowDelayPreference(const ConfigType& config);
+
   // Sets the HardwarePreference on the |decoder_|.
   // The default implementation does nothing and must be overridden by derived
   // classes if needed.
@@ -110,6 +114,7 @@
     // For kConfigure Requests.
     std::unique_ptr<MediaConfigType> media_config;
     HardwarePreference hw_pref = HardwarePreference::kAllow;
+    bool low_delay = false;
 
     // For kDecode Requests.
     scoped_refptr<media::DecoderBuffer> decoder_buffer;
@@ -166,6 +171,7 @@
 
   // Cached config from the last kConfigure request which successfully completed
   // initialization.
+  bool low_delay_ = false;
   std::unique_ptr<MediaConfigType> active_config_;
 
   // TODO(sandersd): Store the last config, flush, and reset so that
diff --git a/third_party/blink/renderer/modules/webcodecs/video_decoder.cc b/third_party/blink/renderer/modules/webcodecs/video_decoder.cc
index 3d4d429..baaec07 100644
--- a/third_party/blink/renderer/modules/webcodecs/video_decoder.cc
+++ b/third_party/blink/renderer/modules/webcodecs/video_decoder.cc
@@ -299,6 +299,9 @@
   if (config.hasHardwareAcceleration())
     copy->setHardwareAcceleration(config.hardwareAcceleration());
 
+  if (config.hasOptimizeForLatency())
+    copy->setOptimizeForLatency(config.optimizeForLatency());
+
   return copy;
 }
 
@@ -326,12 +329,12 @@
 // static
 void VideoDecoderTraits::InitializeDecoder(
     MediaDecoderType& decoder,
+    bool low_delay,
     const MediaConfigType& media_config,
     MediaDecoderType::InitCB init_cb,
     MediaDecoderType::OutputCB output_cb) {
-  decoder.Initialize(media_config, false /* low_delay */,
-                     nullptr /* cdm_context */, std::move(init_cb), output_cb,
-                     media::WaitingCB());
+  decoder.Initialize(media_config, low_delay, nullptr /* cdm_context */,
+                     std::move(init_cb), output_cb, media::WaitingCB());
 }
 
 // static
@@ -346,6 +349,8 @@
       decoder.IsPlatformDecoder());
   media_log->SetProperty<media::MediaLogProperty::kVideoTracks>(
       std::vector<MediaConfigType>{media_config});
+  MEDIA_LOG(INFO, media_log)
+      << "Initialized VideoDecoder: " << media_config.AsHumanReadableString();
 }
 
 // static
@@ -452,6 +457,10 @@
   return GetHardwareAccelerationPreference(config);
 }
 
+bool VideoDecoder::GetLowDelayPreference(const ConfigType& config) {
+  return config.hasOptimizeForLatency() && config.optimizeForLatency();
+}
+
 void VideoDecoder::SetHardwarePreference(HardwarePreference preference) {
   static_cast<VideoDecoderBroker*>(decoder())->SetHardwarePreference(
       preference);
diff --git a/third_party/blink/renderer/modules/webcodecs/video_decoder.h b/third_party/blink/renderer/modules/webcodecs/video_decoder.h
index 52ab18e..cee1406 100644
--- a/third_party/blink/renderer/modules/webcodecs/video_decoder.h
+++ b/third_party/blink/renderer/modules/webcodecs/video_decoder.h
@@ -68,6 +68,7 @@
       media::GpuVideoAcceleratorFactories* gpu_factories,
       media::MediaLog* media_log);
   static void InitializeDecoder(MediaDecoderType& decoder,
+                                bool low_delay,
                                 const MediaConfigType& media_config,
                                 MediaDecoderType::InitCB init_cb,
                                 MediaDecoderType::OutputCB output_cb);
@@ -128,6 +129,7 @@
  private:
   // DecoderTemplate implementation.
   HardwarePreference GetHardwarePreference(const ConfigType& config) override;
+  bool GetLowDelayPreference(const ConfigType& config) override;
   void SetHardwarePreference(HardwarePreference preference) override;
 };
 
diff --git a/third_party/blink/renderer/modules/webcodecs/video_decoder_broker.cc b/third_party/blink/renderer/modules/webcodecs/video_decoder_broker.cc
index ad1a51f..1d90256 100644
--- a/third_party/blink/renderer/modules/webcodecs/video_decoder_broker.cc
+++ b/third_party/blink/renderer/modules/webcodecs/video_decoder_broker.cc
@@ -121,7 +121,7 @@
   MediaVideoTaskWrapper(const MediaVideoTaskWrapper&) = delete;
   MediaVideoTaskWrapper& operator=(const MediaVideoTaskWrapper&) = delete;
 
-  void Initialize(const media::VideoDecoderConfig& config) {
+  void Initialize(const media::VideoDecoderConfig& config, bool low_delay) {
     DVLOG(2) << __func__;
     DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
 
@@ -138,8 +138,9 @@
                            weak_factory_.GetWeakPtr()));
 
     selector_->SelectDecoder(
-        config, WTF::Bind(&MediaVideoTaskWrapper::OnDecoderSelected,
-                          weak_factory_.GetWeakPtr()));
+        config, low_delay,
+        WTF::Bind(&MediaVideoTaskWrapper::OnDecoderSelected,
+                  weak_factory_.GetWeakPtr()));
   }
 
   void Decode(scoped_refptr<media::DecoderBuffer> buffer, int cb_id) {
@@ -376,9 +377,6 @@
   DCHECK(!init_cb_) << "Initialize already pending";
 
   // The following are not currently supported in WebCodecs.
-  // TODO(chcunningham): Should |low_delay| be supported? Should it be
-  // hard-coded to true?
-  DCHECK(!low_delay);
   DCHECK(!cdm_context);
   DCHECK(!waiting_cb);
 
@@ -393,7 +391,7 @@
       *media_task_runner_, FROM_HERE,
       WTF::CrossThreadBindOnce(&MediaVideoTaskWrapper::Initialize,
                                WTF::CrossThreadUnretained(media_tasks_.get()),
-                               config));
+                               config, low_delay));
 }
 
 int VideoDecoderBroker::CreateCallbackId() {
diff --git a/third_party/blink/renderer/modules/webcodecs/video_decoder_config.idl b/third_party/blink/renderer/modules/webcodecs/video_decoder_config.idl
index f29ea79..8bbf71e 100644
--- a/third_party/blink/renderer/modules/webcodecs/video_decoder_config.idl
+++ b/third_party/blink/renderer/modules/webcodecs/video_decoder_config.idl
@@ -35,6 +35,9 @@
 
   HardwarePreference hardwareAcceleration = "allow";
 
+  // Hint that decoders should be configured for latency versus throughput.
+  boolean optimizeForLatency;
+
   // DEPRECATED: Use visibleRegion.
   [EnforceRange] unsigned long cropLeft;
   [EnforceRange] unsigned long cropTop;
diff --git a/third_party/blink/web_tests/FlagExpectations/disable-layout-ng b/third_party/blink/web_tests/FlagExpectations/disable-layout-ng
index 29737b9..75bbe15 100644
--- a/third_party/blink/web_tests/FlagExpectations/disable-layout-ng
+++ b/third_party/blink/web_tests/FlagExpectations/disable-layout-ng
@@ -931,6 +931,7 @@
 crbug.com/958381 external/wpt/css/css-tables/tentative/table-height-redistribution.html [ Failure ]
 crbug.com/958381 external/wpt/css/css-tables/tentative/table-minmax.html [ Failure ]
 crbug.com/958381 external/wpt/css/css-tables/tentative/tbody-height-redistribution.html [ Failure ]
+crbug.com/958381 external/wpt/css/css-tables/tentative/table-width-redistribution.html [ Failure ]
 
 ### external/wpt/css/css-tables/tentative/paint/
 crbug.com/958381 external/wpt/css/css-tables/tentative/paint/background-image-column-collapsed.html [ Failure ]
diff --git a/third_party/blink/web_tests/TestExpectations b/third_party/blink/web_tests/TestExpectations
index bf97029..ce6a926 100644
--- a/third_party/blink/web_tests/TestExpectations
+++ b/third_party/blink/web_tests/TestExpectations
@@ -1093,7 +1093,6 @@
 crbug.com/829028 virtual/layout_ng_block_frag/external/wpt/css/css-multicol/multicol-width-004.html [ Failure ]
 crbug.com/1079031 virtual/layout_ng_block_frag/fast/multicol/abspos-new-width-rebalance.html [ Crash Failure ]
 crbug.com/1058792 virtual/layout_ng_block_frag/fast/multicol/composited-opacity-2nd-and-3rd-column.html [ Failure ]
-crbug.com/1058792 virtual/layout_ng_block_frag/fast/multicol/composited-with-overflow-in-next-column.html [ Failure ]
 crbug.com/1151880 virtual/layout_ng_block_frag/fast/multicol/dynamic/relpos-becomes-static-has-abspos.html [ Failure ]
 crbug.com/1151880 virtual/layout_ng_block_frag/fast/multicol/dynamic/remove-column-content-next-to-abspos-between-spanners.html [ Failure ]
 crbug.com/1079031 virtual/layout_ng_block_frag/fast/multicol/dynamic/static-becomes-relpos-has-abspos.html [ Failure ]
@@ -7044,3 +7043,6 @@
 crbug.com/1198443 [ Mac10.14 ] virtual/plz-service-worker/external/wpt/service-workers/idlharness.https.any.serviceworker.html [ Pass Failure Timeout ]
 crbug.com/1204086 external/wpt/html/cross-origin-opener-policy/reporting/navigation-reporting/report-only-same-origin-report-to.https.html [ Pass Failure ]
 crbug.com/1204086 external/wpt/requestidlecallback/callback-invoked.html [ Pass Failure ]
+
+# Sheriff 2021-04-30
+crbug.com/1204498 [ Linux ] virtual/scroll-unification/fast/events/hit-test-cache-iframes.html [ Pass Failure ]
diff --git a/third_party/blink/web_tests/external/wpt/css/css-tables/tentative/table-width-redistribution.html b/third_party/blink/web_tests/external/wpt/css/css-tables/tentative/table-width-redistribution.html
new file mode 100644
index 0000000..90a89e81
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-tables/tentative/table-width-redistribution.html
@@ -0,0 +1,351 @@
+<!doctype html>
+<title>Auto table final assignable  distribution</title>
+<script src='/resources/testharness.js'></script>
+<script src='/resources/testharnessreport.js'></script>
+<script src="/resources/check-layout-th.js"></script>
+<link rel="stylesheet" type="text/css" href="./support/table-tentative.css">
+<link rel="author" title="Aleks Totic" href="atotic@chromium.org" />
+<link rel="help" href="https://www.w3.org/TR/css-tables-3/#distributing-width-to-columns" />
+<style>
+  main table {
+    background: gray;
+    border-spacing: 8px 8px;
+  }
+  main td {
+    background: #BFB;
+    font-size: 10px;
+  }
+  main td > div {
+    display: inline-block;
+    background: rgba(56,162,56,0.3);
+  }
+</style>
+ <main>
+<h1>Compute column computed widths from assignable table width</h1>
+<h2>Test design</h2>
+<p>All examples have border-spacing:8, td.padding:0</p>
+
+<h2>Table css sizing</h2>
+
+<p class="testdesc">Table: 50px; C0:100/50/100 C1:100/50/100
+When table.css_width is < columns.css_width, column.min_width is lower limit.
+<p class="error">
+<table style="width:50px" data-expected-width=124>
+  <tr>
+    <td style="width:100px" data-expected-width=50>
+      <div style="width:50px">50</div><div style="width:50px">50</div></td>
+    <td style="width:100px" data-expected-width=50>
+      <div style="width:50px">50</div><div style="width:25px">25</div></td>
+  </tr>
+</table>
+
+
+<p class="testdesc">Table: 300px; C0:100/100/200 C1:100/90/115
+When table.css_width is > columns.css_width , how is the conflict resolved?
+table.css_width wins</p>
+<table style="width:300px" data-expected-width=300>
+  <tr>
+    <td style="width:100px" data-expected-width=138>
+      <div style="width:100px">100</div><div style="width:100px">100</div></td>
+    <td style="width:100px" data-expected-width=138>
+      <div style="width:90px">90</div><div style="width:25px">25</div></td>
+  </tr>
+</table>
+
+<h2>Content sizings: min|max|fit|fill-available</h2>
+
+<p class="testdesc">table width:min-content; C0:Auto/50/100 C1:100/50/75 C2:20%/50/75
+</p>
+<p class="error">Edge treats as max-content.</p>
+<table style="width:min-content" data-expected-width=182>
+  <tr>
+    <td data-expected-width=50>
+      <div style="width:50px" >au</div><div style="width:50px">to</div></td>
+    <td style="width:100px" data-expected-width=50>
+      <div style="width:50px" >fix</div><div style="width:25px">ed</div></td>
+    <td style="width:20%" data-expected-width=50>
+      <div style="width:50px">per</div><div style="width:25px">cent</div></td>
+  </tr>
+</table>
+
+<p class="testdesc">table width:max-content; C0:Auto/50/100 C1:100/50/75 C2:20%/50/75
+Each column gets maximum width.
+C0: 100 C1:100 C2: 75
+max-content does not allow for assignable size to be influenced by inverse percent.
+Table size is 275 + 32 = 307
+If percent influenced table size, table size would have been 407
+</p>
+<table style="width:max-content" data-expected-width=307>
+  <tr>
+    <td data-expected-width=120>
+      <div style="width:50px">50</div><div style="width:50px">50</div></td>
+    <td style="width:100px" data-expected-width=100>
+      <div style="width:50px">50</div><div style="width:25px">25</div></td>
+    <td style="width:20%" data-expected-width=55>
+      <div style="width:50px">50</div><div style="width:25px">25</div></td>
+  </tr>
+</table>
+
+<p class="testdesc">table width:fit-content; C0:Auto/50/100 C1:100/50/75 C2:20%/50/75
+Percent column determines assignable table width, which gets distributed to all columns.
+Assignable width from %: 20%=75, 100%=375</p>
+</p>
+<table style="width:fit-content" data-expected-width=407>
+  <tr>
+    <td data-expected-width=200>
+      <div style="width:50px" >au</div><div style="width:50px">to</div></td>
+    <td style="width:100px" data-expected-width=100>
+      <div style="width:50px" >fix</div><div style="width:25px">ed</div></td>
+    <td style="width:20%" data-expected-width=75>
+      <div style="width:50px">per</div><div style="width:25px">cent</div></td>
+  </tr>
+</table>
+
+<p class="testdesc">table width:-webkit-fill-available; C0:Auto/50/100 C1:100/50/75 C2:20%/50/75
+</p>
+<p class="error">Edge treats as fit-content</p>
+<div style="width:632px">
+<table style="width:-webkit-fill-available;width:-moz-available;" data-expected-width=632>
+  <tr>
+    <td data-expected-width=380>
+      <div style="width:50px" >au</div><div style="width:50px">to</div></td>
+    <td style="width:100px" data-expected-width=100>
+      <div style="width:50px" >fix</div><div style="width:25px">ed</div></td>
+    <td style="width:20%" data-expected-width=120>
+      <div style="width:50px">per</div><div style="width:25px">cent</div></td>
+  </tr>
+</table>
+</div>
+
+<h2>Auto columns distribution</h2>
+
+<p class="testdesc">Assi:300px C0: Auto/75/75 C1:Auto/25/25
+Non-empty auto cells get surplus width proportionally to their max width.
+Guess3: 100. Guess4: 300, diff 200, fixed priority.
+C0: 75 + 75/100*200 = 225  C1: 25 + 25/100*200 = 75</p>
+<table style="width:calc(300px + 24px)" data-expected-width=324>
+  <tr>
+    <td data-expected-width=225><div style="width:75px">75</div></td>
+    <td data-expected-width=75><div style="width:25px">25</div></td>
+  </tr>
+</table>
+<p class="testdesc">Assignable:300px C0: Auto/75/75 C1:Auto/13/25 C2:Auto/0/0
+Empty cells get nothing if there are non-empty auto cells.
+Guess3: 100, Guess4: 300; diff 200, fixed priority.
+C0: 75 + 75/100*200 = 225  C1: 25 + 25/100*200 = 75; C2: 0
+</p>
+<table style="width:calc(300px + 32px)" data-expected-width=332>
+  <tr>
+    <td data-expected-width=225>
+      <div style="width:75px">75</div></td>
+    <td data-expected-width=75>
+      <div style="width:13px">13</div><div style="width:12px">12</div></td>
+    <td data-expected-width=0></td>
+  </tr>
+</table>
+
+<h2>Guess 0: Auto(min), Fixed(min), Percentage(min) to Guess 1.</h2>
+
+<p class="testdesc">Assi: 1px; C0:Auto/50/100 C1:100/50/100 C2:50%/50/100
+All columns get minimum width.
+Guess0: 150
+C0: 50, C1:50, C2: 50</p>
+<table style="width:1px" data-expected-width=182>
+  <tr>
+    <td data-expected-width=50>
+      <div style="width:50px">50</div><div style="width:50px">50</div></td>
+    <td data-expected-width=50 style="width:100px">
+      <div style="width:50px">50</div><div style="width:50px">50</div></td>
+    <td data-expected-width=50 style="width:50%">
+      <div style="width:50px">50</div><div style="width:50px">50</div></td>
+</table>
+
+<p class="testdesc">Assi: 160px; C0:Auto/50/100 C1:100/50/100 C2:50%/50/100
+%ge column grows.
+Guess0: 150, Guess1: 180; diff 10.
+C0: 50, C1:50, C2: 50 + 10 * 10/10 = 60</p>
+<table style="width:calc(160px + 32px)" data-expected-width=192>
+  <tr>
+    <td data-expected-width=50>
+      <div style="width:50px">50</div><div style="width:50px">50</div></td>
+    <td data-expected-width=50 style="width:100px">
+      <div style="width:50px">50</div><div style="width:50px">50</div></td>
+    <td data-expected-width=60 style="width:50%">
+      <div style="width:50px">50</div><div style="width:50px">50</div></td>
+</table>
+
+<p class="testdesc">Assi: 210px; C0:Auto/50/100 C1:100/50/100 C2:30%/50/100 C3:30%/50/100
+2 %ge columns grow evenly.
+Guess 0: 200, Guess 1: 240, diff 10
+C2: 50 + 10 * 70/140 C3: 50 + 10 * 70/140
+<table style="width:calc(40px + 210px)" data-expected-width=250>
+  <tr>
+    <td data-expected-width=50>
+      <div style="width:50px">50</div><div style="width:50px">50</div></td>
+    <td data-expected-width=50 style="width:100px">
+      <div style="width:50px">50</div><div style="width:50px">50</div></td>
+    <td data-expected-width=55 style="width:30%">
+      <div style="width:50px">50</div><div style="width:50px">50</div></td>
+    <td data-expected-width=55 style="width:30%">
+      <div style="width:50px">50</div><div style="width:50px">50</div></td>
+</table>
+
+<p class="testdesc">Assi: 220px; C0:Auto/50/100 C1:100/50/100 C2:25%/50/100 C3:40%50/100
+%ge columns grow in proportion to increase from previous guess.
+Guess 0: 200. C2[G1] = .25*220 = 55 C3[G1] = .4 * 220 = 88 Guess 1: 100 + 55 + 88 = 243
+diff = 220 - 200 = 20. C2 grew 5, C3 grew 38, total grew 43.
+C2: 50 + 20 *5/43 = 52.32; C3: 50 + 20 * 38 / 43 = 67.67
+<table style="width:calc(40px + 220px)" data-expected-width=260>
+  <tr>
+    <td data-expected-width=50>
+      <div style="width:50px">50</div> <div style="width:50px">50</div></td>
+    <td data-expected-width=50 style="width:100px">
+      <div style="width:50px">50</div><div style="width:50px">50</div></td>
+    <td data-expected-width=52 style="width:25%">
+      <div style="width:50px">50</div><div style="width:50px">50</div></td>
+    <td  data-expected-width=68 style="width:40%">
+      <div style="width:50px">50</div><div style="width:50px">50</div></td>
+</table>
+
+<h2>Guess 1 to Guess 2: Auto(min), Percentage(%max) to Fixed(min => max)</h2>
+<p>These tests are non-intuitive to evaluate. When table size increases betwee Guess 1 and 2,
+  although the standard says that fixed columns are growing, %ge columns grow too because their max width depends on table width.</p>
+
+<p class="testdesc">Assi:166, C0:Auto/50/100 C1:100/50/100 C2:40%/50/100
+Edge example, Guess 1 %ge cell has grown to the max.
+C2: grows to .4*165 = 66.4, table is 166.4+32 = 198.4</p>
+<table style="width:calc(166px + 32px)" data-expected-width=198>
+  <tr>
+    <td data-expected-width="50" >
+      <div style="width:50px">50</div><div style="width:50px">50</div></td>
+    <td data-expected-width="50" style="width:100px">
+       <div style="width:50px">50</div><div style="width:50px">50</div></td>
+    <td data-expected-width="66" style="width:40%">
+      <div style="width:50px">50</div><div style="width:50px">50</div></td>
+  </tr>
+</table>
+
+<p class="testdesc">Assi:216, C0:Auto/50/100 C1:100/50/100 C2:40%/50
+  %ge cell grows to the max, the rest goes to fixed.
+Guess 1 size is 50 + 50 + (.4*216=>86.4) = 186.4
+Guess 2 size is 50 + 100 + 86.4 = 236
+Assi - Guess1 = 29.6; C2 = 50 + 29.6 * 50/50 = 79.6
+</p>
+<table style="width:calc(216px + 32px)" data-expected-width=248>
+  <tr>
+    <td data-expected-width="50" >
+      <div style="width:50px">50</div><div style="width:50px">50</div></td>
+    <td data-expected-width="80" style="width:100px">
+       <div style="width:50px">50</div><div style="width:50px">50</div></td>
+    <td data-expected-width="86" style="width:40%">
+      <div style="width:50px">50</div><div style="width:50px">50</div></td>
+  </tr>
+</table>
+
+<h2>Guess 2 to Guess 3: Percentage(%max), Fixed(max), Auto(min=>max)</h2>
+
+<p class="testdesc">Assi:300
+Guess 2 size is 50 + 100 + .4*300 = 270
+Guess 3 size is 100 + 100 + 120 = 320
+Assi - Guess2 = 30, C0 = 50 + 30 = 80
+<table style="width:calc(300px + 32px)" data-expected-width=332>
+  <tr>
+    <td data-expected-width="80" >
+      <div style="width:50px">50</div><div style="width:50px">50</div></td>
+    <td data-expected-width="100" style="width:100px">
+       <div style="width:50px">50</div><div style="width:50px">50</div></td>
+    <td data-expected-width="120" style="width:40%">
+      <div style="width:50px">50</div><div style="width:50px">50</div></td>
+  </tr>
+</table>
+
+<h2>Guess3 to Guess4, Auto(max), Percentage(%max), Fixed(max) grow first available Auto|Fixed|Percent</h2>
+
+<p class="testdesc">Assi: 500, C0:Auto, C1: Fixed, C2: Percent
+Guess 3 is 100 + 100 + .4 * 500 = 400
+C0 gets the 100
+<table style="width:calc(500px + 32px)" data-expected-width=532>
+  <tr>
+    <td data-expected-width="200" >
+      <div style="width:50px">50</div><div style="width:50px">50</div></td>
+    <td data-expected-width="100" style="width:100px">
+       <div style="width:50px">50</div><div style="width:50px">50</div></td>
+    <td data-expected-width="200" style="width:40%">
+      <div style="width:50px">50</div><div style="width:50px">50</div></td>
+  </tr>
+</table>
+
+<p class="testdesc">Assi: 500, C0:Fixed, C1: Fixed, C2: Percent
+Guess 3 is 100 + 100 + .4 * 500 = 400, 100 to be redistributed
+Fixed cells, C0 and C1 get 50 each.
+<table style="width:calc(500px + 32px)" data-expected-width=532>
+  <tr>
+    <td data-expected-width="150" style="width:100px">
+      <div style="width:50px">50</div><div style="width:50px">50</div></td>
+    <td data-expected-width="150" style="width:100px">
+       <div style="width:50px">50</div><div style="width:50px">50</div></td>
+    <td data-expected-width="200" style="width:40%">
+      <div style="width:50px">50</div><div style="width:50px">50</div></td>
+  </tr>
+</table>
+
+<p class="testdesc">Assi: 700, C0:10%/40, C1: 20%/50, C2: 40%/50
+Percentage cells only.
+Compute columns as %ge of total width:
+C0: 700*10/70, C1: 700*20/70 C2: 700*40/70
+<table style="width:calc(700px + 32px)" data-expected-width=732>
+  <tr>
+    <td data-expected-width=100 style="width:10%">
+      <div style="width:40px">40</div></td>
+    <td data-expected-width=200 style="width:20%">
+       <div style="width:50px">50</div></td>
+    <td data-expected-width=400 style="width:40%">
+      <div style="width:50px">50</div></td>
+  </tr>
+</table>
+<p class="testdesc">Assi: 600, C0:10%/40, C1: 20%/50, C2: 40%/50, C3: 100%/50
+Percentage cells only. Over 100% columns get their percentage truncated.
+<table style="width:calc(600px + 40px)" data-expected-width=640>
+  <tr>
+    <td data-expected-width=60 style="width:10%">
+      <div style="width:40px">40</div></td>
+    <td data-expected-width=120 style="width:20%">
+       <div style="width:50px">50</div></td>
+    <td data-expected-width=240 style="width:40%">
+      <div style="width:50px">50</div></td>
+    <td data-expected-width=180 style="width:100%">
+      <div style="width:50px">50</div></td>
+  </tr>
+</table>
+<p class="testdesc">C0:20%/60, C1:Auto/50.
+Tests table max width from single cell.
+<table data-expected-width="324">
+  <tr>
+    <td style="width:20%"  data-expected-width="60"><div style="width:60px">60</div></td>
+    <td><div style="width:50px">50</div></td>
+  </tr>
+</table>
+
+<p class="testdesc">C0:10%/70, C1:Auto/50.
+Table limited to 1px. Tests that single cell specifies max width, not min width.
+<table style="width:1px" data-expected-width="134">
+  <tr>
+    <td style="width:20%"  data-expected-width="60"><div style="width:60px">60</div></td>
+    <td><div style="width:50px" data-expected-width="50">50</div></td>
+  </tr>
+</table>
+
+<p class="testdesc">C0:10%/70 border 10px, C1:Auto/50.
+Cell border padding do not affect max width.
+<table data-expected-width="524">
+  <tr>
+    <td style="width:20%;border:10px solid yellow;padding:10px"  data-expected-width="100"><div style="width:60px">60</div></td>
+    <td><div style="width:50px">50</div></td>
+  </tr>
+</table>
+</main>
+
+<script>
+  checkLayout("table");
+</script>
+
diff --git a/third_party/blink/web_tests/external/wpt/resource-timing/resources/resource-loaders.js b/third_party/blink/web_tests/external/wpt/resource-timing/resources/resource-loaders.js
index c5754d1d..a02de4f 100644
--- a/third_party/blink/web_tests/external/wpt/resource-timing/resources/resource-loaders.js
+++ b/third_party/blink/web_tests/external/wpt/resource-timing/resources/resource-loaders.js
@@ -1,11 +1,18 @@
 const load = {
+  _cache_bust_value: Math.random().toString().substr(2),
+  cache_bust: path => {
+    let url = new URL(path, location.origin);
+    url.hash += `cache_bust=${load._cache_bust_value++}`;
+    return url.href;
+  },
+
   // Returns a promise that settles once the given path has been fetched as an
   // image resource.
   image: path => {
     return new Promise(resolve => {
       const img = new Image();
       img.onload = img.onerror = resolve;
-      img.src = path;
+      img.src = load.cache_bust(path);
     });
   },
 
@@ -17,7 +24,7 @@
       <style>
       @font-face {
           font-family: ahem;
-          src: url('${path}');
+          src: url('${load.cache_bust(path)}');
       }
       </style>
       <div style="font-family: ahem;">This fetches ahem font.</div>
@@ -34,7 +41,7 @@
     const link = document.createElement("link");
     link.rel = "stylesheet";
     link.type = "text/css";
-    link.href = path;
+    link.href = load.cache_bust(path);
 
     const loaded = new Promise(resolve => {
       link.onload = link.onerror = resolve;
@@ -52,7 +59,7 @@
     const loaded = new Promise(resolve => {
       frame.onload = frame.onerror = resolve;
     });
-    frame.src = path;
+    frame.src = load.cache_bust(path);
     document.body.appendChild(frame);
     await loaded;
     document.body.removeChild(frame);
@@ -65,7 +72,7 @@
     const loaded = new Promise(resolve => {
       script.onload = script.onerror = resolve;
     });
-    script.src = path;
+    script.src = load.cache_bust(path);
     document.body.appendChild(script);
     await loaded;
     document.body.removeChild(script);
diff --git a/third_party/blink/web_tests/external/wpt/webcodecs/README.md b/third_party/blink/web_tests/external/wpt/webcodecs/README.md
index dd401e4d..e562744 100644
--- a/third_party/blink/web_tests/external/wpt/webcodecs/README.md
+++ b/third_party/blink/web_tests/external/wpt/webcodecs/README.md
@@ -9,6 +9,12 @@
 Please provide full reference and steps to generate the test file so that
 any people can regenerate or update the file in the future.
 
+## Notes
+* When updating the sample offsets and descriptions for tests using mp4 files, it's easiest to use [mp4box.js](https://gpac.github.io/mp4box.js/test/filereader.html).
+  * Sample offsets can be copied from the "Sample View" tab after unchecking all but offset and size. Use a multi-line edit mode and clang-format to quickly format entries.
+  * Description entries can be found under moov.trak.mdia.minf.stbl.stsd in box view.
+    * avc1.avcC has an offset, size in the same view. Add 8 to offset and subtract 8 from the size to get the values the tests want.
+
 ## List of Test Files
 
 ### four-colors.png
@@ -77,6 +83,11 @@
 ### four-colors.mp4
 Used a [custom tool](https://storage.googleapis.com/dalecurtis/avif2mp4.html) to convert four-colors.avif into a .mp4 file.
 
+### h264.mp4
+```
+ffmpeg -f lavfi -i testsrc=rate=10:n=1 -t 1 -pix_fmt yuv420p -vcodec h264 -tune zerolatency h264.mp4
+```
+
 ### sfx-opus.ogg
 ```
 sox -n -r 48000 sfx.wav synth 1 sine 480
diff --git a/third_party/blink/web_tests/external/wpt/webcodecs/h264.mp4 b/third_party/blink/web_tests/external/wpt/webcodecs/h264.mp4
index e21fcba..e0d6a6b 100644
--- a/third_party/blink/web_tests/external/wpt/webcodecs/h264.mp4
+++ b/third_party/blink/web_tests/external/wpt/webcodecs/h264.mp4
Binary files differ
diff --git a/third_party/blink/web_tests/external/wpt/webcodecs/video-decoder.any.js b/third_party/blink/web_tests/external/wpt/webcodecs/video-decoder.any.js
index 0f562e36..ca9b752 100644
--- a/third_party/blink/web_tests/external/wpt/webcodecs/video-decoder.any.js
+++ b/third_party/blink/web_tests/external/wpt/webcodecs/video-decoder.any.js
@@ -4,45 +4,45 @@
 // TODO(sandersd): Move metadata into a helper library.
 // TODO(sandersd): Add H.264 decode test once there is an API to query for
 // supported codecs.
+
 const h264 = {
-  async buffer() { return (await fetch('h264.mp4')).arrayBuffer(); },
-  codec: "avc1.64000c",
-  description: {offset: 7229, size: 46},
-  frames: [{offset: 48, size: 4007},
-           {offset: 4055, size: 926},
-           {offset: 4981, size: 241},
-           {offset: 5222, size: 97},
-           {offset: 5319, size: 98},
-           {offset: 5417, size: 624},
-           {offset: 6041, size: 185},
-           {offset: 6226, size: 94},
-           {offset: 6320, size: 109},
-           {offset: 6429, size: 281}]
+  async buffer() {
+    return (await fetch('h264.mp4')).arrayBuffer();
+  },
+  codec: 'avc1.64000c',
+  description: {offset: 9490, size: 45},
+  frames: [
+    {offset: 48, size: 4140}, {offset: 4188, size: 604},
+    {offset: 4792, size: 475}, {offset: 5267, size: 561},
+    {offset: 5828, size: 587}, {offset: 6415, size: 519},
+    {offset: 6934, size: 532}, {offset: 7466, size: 523},
+    {offset: 7989, size: 454}, {offset: 8443, size: 528}
+  ]
 };
 
+
 const vp9 = {
-  async buffer() { return (await fetch('vp9.mp4')).arrayBuffer(); },
+  async buffer() {
+    return (await fetch('vp9.mp4')).arrayBuffer();
+  },
   // TODO(sandersd): Verify that the file is actually level 1.
-  codec: "vp09.00.10.08",
-  frames: [{offset: 44, size: 3315},
-           {offset: 3359, size: 203},
-           {offset: 3562, size: 245},
-           {offset: 3807, size: 172},
-           {offset: 3979, size: 312},
-           {offset: 4291, size: 170},
-           {offset: 4461, size: 195},
-           {offset: 4656, size: 181},
-           {offset: 4837, size: 356},
-           {offset: 5193, size: 159}]
+  codec: 'vp09.00.10.08',
+  frames: [
+    {offset: 44, size: 3315}, {offset: 3359, size: 203},
+    {offset: 3562, size: 245}, {offset: 3807, size: 172},
+    {offset: 3979, size: 312}, {offset: 4291, size: 170},
+    {offset: 4461, size: 195}, {offset: 4656, size: 181},
+    {offset: 4837, size: 356}, {offset: 5193, size: 159}
+  ]
 };
 
 const badCodecsList = [
-    '',                         // Empty codec
-    'bogus',                    // Non exsitent codec
-    'vorbis',                   // Audio codec
-    'vp9',                      // Ambiguous codec
-    'video/webm; codecs="vp9"'  // Codec with mime type
-  ]
+  '',                         // Empty codec
+  'bogus',                    // Non exsitent codec
+  'vorbis',                   // Audio codec
+  'vp9',                      // Ambiguous codec
+  'video/webm; codecs="vp9"'  // Codec with mime type
+];
 
 const invalidConfigs = [
   {
@@ -99,32 +99,38 @@
       displayHeight: 0,
     },
   },
-] //  invalidConfigs
+];  //  invalidConfigs
 
 function view(buffer, {offset, size}) {
   return new Uint8Array(buffer, offset, size);
 }
 
 function getFakeChunk() {
-  return new EncodedVideoChunk({
-    type:'key',
-    timestamp:0,
-    data:Uint8Array.of(0)
-  });
+  return new EncodedVideoChunk(
+      {type: 'key', timestamp: 0, data: Uint8Array.of(0)});
 }
 
 invalidConfigs.forEach(entry => {
-  promise_test(t => {
-    return promise_rejects_js(t, TypeError, VideoDecoder.isConfigSupported(entry.config));
-  }, 'Test that VideoDecoder.isConfigSupported() rejects invalid config:' + entry.comment);
+  promise_test(
+      t => {
+        return promise_rejects_js(
+            t, TypeError, VideoDecoder.isConfigSupported(entry.config));
+      },
+      'Test that VideoDecoder.isConfigSupported() rejects invalid config:' +
+          entry.comment);
 });
 
 invalidConfigs.forEach(entry => {
-  async_test(t => {
-    let codec = new VideoDecoder(getDefaultCodecInit(t));
-    assert_throws_js(TypeError, () => { codec.configure(entry.config); });
-    t.done();
-  }, 'Test that VideoDecoder.configure() rejects invalid config:' + entry.comment);
+  async_test(
+      t => {
+        let codec = new VideoDecoder(getDefaultCodecInit(t));
+        assert_throws_js(TypeError, () => {
+          codec.configure(entry.config);
+        });
+        t.done();
+      },
+      'Test that VideoDecoder.configure() rejects invalid config:' +
+          entry.comment);
 });
 
 promise_test(t => {
@@ -177,10 +183,12 @@
     assert_equals(decoderSupport.config.visibleRegion.width, 1920);
     assert_equals(decoderSupport.config.visibleRegion.height, 1080);
     assert_equals(decoderSupport.config.displayWidth, validConfig.displayWidth);
-    assert_equals(decoderSupport.config.displayHeight, validConfig.displayHeight);
+    assert_equals(
+        decoderSupport.config.displayHeight, validConfig.displayHeight);
 
     // The description BufferSource must copy the input config description.
-    assert_not_equals(decoderSupport.config.description, validConfig.description);
+    assert_not_equals(
+        decoderSupport.config.description, validConfig.description);
     let parsedDescription = new Uint8Array(decoderSupport.config.description);
     assert_equals(parsedDescription.length, validConfig.description.length);
     for (let i = 0; i < parsedDescription.length; ++i) {
@@ -192,12 +200,14 @@
 
 promise_test(t => {
   // VideoDecoderInit lacks required fields.
-  assert_throws_js(TypeError, () => { new VideoDecoder({}); });
+  assert_throws_js(TypeError, () => {
+    new VideoDecoder({});
+  });
 
   // VideoDecoderInit has required fields.
   let decoder = new VideoDecoder(getDefaultCodecInit(t));
 
-  assert_equals(decoder.state, "unconfigured");
+  assert_equals(decoder.state, 'unconfigured');
 
   decoder.close();
 
@@ -209,7 +219,7 @@
 
   // TODO(chcunningham): Remove badCodecsList testing. It's now covered more
   // extensively by other tests.
-  testConfigurations(decoder, { codec: vp9.codec }, badCodecsList);
+  testConfigurations(decoder, {codec: vp9.codec}, badCodecsList);
 
   return endAfterEventLoopTurn();
 }, 'Test VideoDecoder.configure() with various codec strings');
@@ -221,29 +231,28 @@
   let decoder = new VideoDecoder({
     output(frame) {
       t.step(() => {
-        assert_equals(++numOutputs, 1, "outputs");
-        assert_equals(frame.visibleRegion.width, 320, "visibleRegion.width");
-        assert_equals(frame.visibleRegion.height, 240, "visibleRegion.height");
-        assert_equals(frame.timestamp, 0, "timestamp");
+        assert_equals(++numOutputs, 1, 'outputs');
+        assert_equals(frame.visibleRegion.width, 320, 'visibleRegion.width');
+        assert_equals(frame.visibleRegion.height, 240, 'visibleRegion.height');
+        assert_equals(frame.timestamp, 0, 'timestamp');
         frame.close();
       });
     },
     error(e) {
-      t.step(() => { throw e; });
+      t.step(() => {
+        throw e;
+      });
     }
   });
 
   decoder.configure({codec: vp9.codec});
 
-  decoder.decode(new EncodedVideoChunk({
-    type:'key',
-    timestamp:0,
-    data: view(buffer, vp9.frames[0])
-  }));
+  decoder.decode(new EncodedVideoChunk(
+      {type: 'key', timestamp: 0, data: view(buffer, vp9.frames[0])}));
 
   await decoder.flush();
 
-  assert_equals(numOutputs, 1, "outputs");
+  assert_equals(numOutputs, 1, 'outputs');
 }, 'Decode VP9');
 
 promise_test(async t => {
@@ -264,25 +273,25 @@
       });
     },
     error(e) {
-      t.step(() => { throw e; });
+      t.step(() => {
+        throw e;
+      });
     }
   });
 
   decoder.configure({codec: vp9.codec});
 
   for (let i = 0; i < 100; i++) {
-    decoder.decode(new EncodedVideoChunk({
-      type:'key',
-      timestamp:0,
-      data: view(buffer, vp9.frames[0])
-    }));
+    decoder.decode(new EncodedVideoChunk(
+        {type: 'key', timestamp: 0, data: view(buffer, vp9.frames[0])}));
   }
 
   assert_greater_than(decoder.decodeQueueSize, 0);
 
   // Wait for the first frame to be decoded.
-  await t.step_wait(() => outputs_before_reset > 0,
-      "Decoded outputs started coming", 10000, 1);
+  await t.step_wait(
+      () => outputs_before_reset > 0, 'Decoded outputs started coming', 10000,
+      1);
 
   let saved_outputs_before_reset = outputs_before_reset;
   assert_greater_than(saved_outputs_before_reset, 0);
@@ -294,11 +303,8 @@
   decoder.configure({codec: vp9.codec});
 
   for (let i = 0; i < 5; i++) {
-    decoder.decode(new EncodedVideoChunk({
-      type:'key',
-      timestamp:1,
-      data: view(buffer, vp9.frames[0])
-    }));
+    decoder.decode(new EncodedVideoChunk(
+        {type: 'key', timestamp: 1, data: view(buffer, vp9.frames[0])}));
   }
   await decoder.flush();
   assert_equals(outputs_after_reset, 5);
@@ -311,7 +317,7 @@
 promise_test(t => {
   let decoder = new VideoDecoder(getDefaultCodecInit(t));
 
-  return testClosedCodec(t, decoder, { codec: vp9.codec }, getFakeChunk());
+  return testClosedCodec(t, decoder, {codec: vp9.codec}, getFakeChunk());
 }, 'Verify closed VideoDecoder operations');
 
 promise_test(t => {
@@ -332,11 +338,10 @@
   let fakeChunk = getFakeChunk();
   decoder.decode(fakeChunk);
 
-  return promise_rejects_exactly(t, undefined, decoder.flush()).then(
-      () => {
-        assert_equals(numErrors, 1, "errors");
-        assert_equals(decoder.state, "closed");
-      });
+  return promise_rejects_exactly(t, undefined, decoder.flush()).then(() => {
+    assert_equals(numErrors, 1, 'errors');
+    assert_equals(decoder.state, 'closed');
+  });
 }, 'Decode corrupt VP9 frame');
 
 promise_test(t => {
@@ -351,11 +356,10 @@
   let fakeChunk = getFakeChunk();
   decoder.decode(fakeChunk);
 
-  return promise_rejects_exactly(t, undefined, decoder.flush()).then(
-      () => {
-        assert_equals(numErrors, 1, "errors");
-        assert_equals(decoder.state, "closed");
-      });
+  return promise_rejects_exactly(t, undefined, decoder.flush()).then(() => {
+    assert_equals(numErrors, 1, 'errors');
+    assert_equals(decoder.state, 'closed');
+  });
 }, 'Decode empty VP9 frame');
 
 promise_test(t => {
@@ -446,3 +450,31 @@
   decoder.reset();
   return p;
 }, 'Test reset during flush.');
+
+promise_test(async t => {
+  let result = await VideoDecoder.isConfigSupported({codec: h264.codec});
+  assert_implements_optional(
+      result.supported, 'Optional codec ' + h264.codec + ' not supported.');
+  let buffer = await h264.buffer();
+
+  let numOutputs = 0;
+  let decoder = new VideoDecoder({
+    output: t.step_func(frame => {
+      frame.close();
+      ++numOutputs;
+    }),
+    error: t.unreached_func()
+  });
+
+  decoder.configure({
+    codec: h264.codec,
+    description: view(buffer, h264.description),
+    optimizeForLatency: true
+  });
+  decoder.decode(new EncodedVideoChunk(
+      {type: 'key', timestamp: 0, data: view(buffer, h264.frames[0])}));
+
+  // Wait for the first frame to be decoded.
+  await t.step_wait(() => numOutputs > 0, 'Decoded first frame', 10000, 1);
+  assert_equals(numOutputs, 1, 'outputs');
+}, 'Test low latency decoding.');
diff --git a/third_party/blink/web_tests/platform/linux/virtual/layout_ng_block_frag/fast/multicol/composited-with-overflow-in-next-column-expected.png b/third_party/blink/web_tests/platform/linux/virtual/layout_ng_block_frag/fast/multicol/composited-with-overflow-in-next-column-expected.png
new file mode 100644
index 0000000..98de5cc
--- /dev/null
+++ b/third_party/blink/web_tests/platform/linux/virtual/layout_ng_block_frag/fast/multicol/composited-with-overflow-in-next-column-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/virtual/layout_ng_block_frag/fast/multicol/composited-with-overflow-in-next-column-expected.png b/third_party/blink/web_tests/platform/mac/virtual/layout_ng_block_frag/fast/multicol/composited-with-overflow-in-next-column-expected.png
new file mode 100644
index 0000000..6f86227
--- /dev/null
+++ b/third_party/blink/web_tests/platform/mac/virtual/layout_ng_block_frag/fast/multicol/composited-with-overflow-in-next-column-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/virtual/layout_ng_block_frag/fast/multicol/composited-with-overflow-in-next-column-expected.png b/third_party/blink/web_tests/platform/win/virtual/layout_ng_block_frag/fast/multicol/composited-with-overflow-in-next-column-expected.png
new file mode 100644
index 0000000..474a960
--- /dev/null
+++ b/third_party/blink/web_tests/platform/win/virtual/layout_ng_block_frag/fast/multicol/composited-with-overflow-in-next-column-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/wpt_internal/css/css-conditional/container-queries/orthogonal-wm-container-query.html b/third_party/blink/web_tests/wpt_internal/css/css-conditional/container-queries/orthogonal-wm-container-query.html
new file mode 100644
index 0000000..82890cf1
--- /dev/null
+++ b/third_party/blink/web_tests/wpt_internal/css/css-conditional/container-queries/orthogonal-wm-container-query.html
@@ -0,0 +1,34 @@
+<!doctype html>
+<title>Orthogonal writing-mode change in @container</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<link rel="stylesheet" href="/fonts/ahem.css">
+<style>
+  #container {
+    contain: size layout;
+    width: 50vw;
+    height: 50vh;
+  }
+  #orthogonal {
+    font: 50px/1 Ahem;
+  }
+  @container (max-width: 100px) {
+    #orthogonal {
+      writing-mode: vertical-lr;
+    }
+  }
+</style>
+<div id="container">
+  <div id="orthogonal">XX</div>
+</div>
+<script>
+  test(() => {
+    assert_equals(orthogonal.offsetWidth, container.offsetWidth);
+  }, "Initial non-orthogonal width");
+
+  test(() => {
+    container.style.width = "100px";
+    assert_equals(orthogonal.offsetWidth, 50);
+    assert_not_equals(orthogonal.offsetWidth, container.offsetWidth);
+  }, "Orthogonal width");
+</script>
diff --git a/third_party/freetype/README.chromium b/third_party/freetype/README.chromium
index 1ca742b9..b82c5ba 100644
--- a/third_party/freetype/README.chromium
+++ b/third_party/freetype/README.chromium
@@ -1,7 +1,7 @@
 Name: FreeType
 URL: http://www.freetype.org/
-Version: VER-2-10-4-226-gf631542da
-Revision: f631542dae1aaf9101135ed660cd3ff1d08ed93c
+Version: VER-2-10-4-227-g4e1c6a12e
+Revision: 4e1c6a12e5f285d57e66818177013cce7efbd7b0
 CPEPrefix: cpe:/a:freetype:freetype:2.10.4
 License: Custom license "inspired by the BSD, Artistic, and IJG (Independent
          JPEG Group) licenses"
diff --git a/third_party/libjxl/README.chromium b/third_party/libjxl/README.chromium
index f5bb62c..f2dcfd3 100644
--- a/third_party/libjxl/README.chromium
+++ b/third_party/libjxl/README.chromium
@@ -2,8 +2,8 @@
 Short Name: libjxl
 URL: https://gitlab.com/wg1/jpeg-xl
 Version: 0
-Date: 2021-04-08
-Revision: a124844519310785445d0b6efcd536c5398e6a20
+Date: 2021-04-29
+Revision: e5ce94456581d43f8a52c8100c726a0d079f65e7
 License: Apache 2.0
 Security Critical: yes
 CPEPrefix: unknown
diff --git a/third_party/markdown/treeprocessors.py b/third_party/markdown/treeprocessors.py
index 109358b..152daab1 100644
--- a/third_party/markdown/treeprocessors.py
+++ b/third_party/markdown/treeprocessors.py
@@ -163,7 +163,7 @@
         childResult = self.__processPlaceholders(text, subnode)
 
         if not isText and node is not subnode:
-            pos = node.getchildren().index(subnode)
+            pos = list(node).index(subnode)
             node.remove(subnode)
         else:
             pos = 0
@@ -211,7 +211,7 @@
                         linkText(text)
 
                     if not isString(node): # it's Element
-                        for child in [node] + node.getchildren():
+                        for child in [node] + list(node):
                             if child.tail:
                                 if child.tail.strip():
                                     self.__processElementText(node, child,False)
@@ -269,9 +269,9 @@
         if not isString(node):
             if not isinstance(node.text, util.AtomicString):
                 # We need to process current node too
-                for child in [node] + node.getchildren():
+                for child in [node] + list(node):
                     if not isString(node):
-                        if child.text: 
+                        if child.text:
                             child.text = self.__handleInline(child.text,
                                                             patternIndex + 1)
                         if child.tail:
@@ -308,7 +308,7 @@
         while stack:
             currElement = stack.pop()
             insertQueue = []
-            for child in currElement.getchildren():
+            for child in currElement:
                 if child.text and not isinstance(child.text, util.AtomicString):
                     text = child.text
                     child.text = None
@@ -324,11 +324,11 @@
                         child.tail = dumby.text
                     else:
                         child.tail = None
-                    pos = currElement.getchildren().index(child) + 1
+                    pos = list(currElement).index(child) + 1
                     tailResult.reverse()
                     for newChild in tailResult:
                         currElement.insert(pos, newChild)
-                if child.getchildren():
+                if list(child):
                     stack.append(child)
 
             for element, lst in insertQueue:
@@ -379,14 +379,14 @@
         self._prettifyETree(root)
         # Do <br />'s seperately as they are often in the middle of
         # inline content and missed by _prettifyETree.
-        brs = root.getiterator('br')
+        brs = root.iter('br')
         for br in brs:
             if not br.tail or not br.tail.strip():
                 br.tail = '\n'
             else:
                 br.tail = '\n%s' % br.tail
         # Clean up extra empty lines at end of code blocks.
-        pres = root.getiterator('pre')
+        pres = root.iter('pre')
         for pre in pres:
             if len(pre) and pre[0].tag == 'code':
                 pre[0].text = pre[0].text.rstrip() + '\n'
diff --git a/tools/android/build_speed/benchmark.py b/tools/android/build_speed/benchmark.py
index 2891685..63e4f51c 100755
--- a/tools/android/build_speed/benchmark.py
+++ b/tools/android/build_speed/benchmark.py
@@ -83,6 +83,7 @@
     'extra_incremental': [
         'turbine_headers',
         'compile_java',
+        'write_build_config',
     ],
 }
 
@@ -152,6 +153,12 @@
         'to_string': '#temporary_edit_for_benchmark.py',
         'change_file': 'build/android/gyp/compile_java.py',
     }),
+    Benchmark('write_build_config', {
+        'kind': 'incremental',
+        'from_string': '# found in the LICENSE file.',
+        'to_string': '#temporary_edit_for_benchmark.py',
+        'change_file': 'build/android/gyp/write_build_config.py',
+    }),
 ]
 
 
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml
index 6f32891..6dbe186 100644
--- a/tools/metrics/histograms/enums.xml
+++ b/tools/metrics/histograms/enums.xml
@@ -6706,6 +6706,7 @@
       label="kEmbedder-kChromePasswordManagerClient_BindCredentialManager"/>
   <int value="196618" label="kEmbedder-kPermissionRequestManager"/>
   <int value="196619" label="kEmbedder-kModalDialog"/>
+  <int value="196620" label="kEmbedder-kExtensions"/>
 </enum>
 
 <enum name="BackForwardCacheDisabledForRenderFrameHostReasonShort">
@@ -9674,6 +9675,9 @@
   <int value="6" label="Move failed">
     Migration did not complete since move stage did not complete.
   </int>
+  <int value="7" label="Data wipe failed">
+    Failed to delete lacros data directory with old data.
+  </int>
 </enum>
 
 <enum name="BrowserGestureActionType">
@@ -24322,7 +24326,7 @@
   <int value="840" label="BrowserThemeColor"/>
   <int value="841" label="CECPQ2Enabled"/>
   <int value="842" label="HeadlessMode"/>
-  <int value="843" label="WebRTCIPHandlingPolicy"/>
+  <int value="843" label="WebRtcIPHandling"/>
   <int value="844" label="PdfAnnotationsEnabled"/>
   <int value="845" label="DefaultFileHandlingGuardSetting"/>
   <int value="846" label="FileHandlingAllowedForUrls"/>
@@ -24336,6 +24340,7 @@
   <int value="854" label="RelaunchWindow"/>
   <int value="855" label="LacrosAvailability"/>
   <int value="856" label="DataLeakPreventionReportingEnabled"/>
+  <int value="857" label="AdditionalDnsQueryTypesEnabled"/>
 </enum>
 
 <enum name="EnterprisePolicyDeviceIdValidity">
diff --git a/tools/metrics/histograms/histograms_xml/METRIC_REVIEWER_OWNERS b/tools/metrics/histograms/histograms_xml/METRIC_REVIEWER_OWNERS
index 6d73a3f..15e57cf 100644
--- a/tools/metrics/histograms/histograms_xml/METRIC_REVIEWER_OWNERS
+++ b/tools/metrics/histograms/histograms_xml/METRIC_REVIEWER_OWNERS
@@ -14,6 +14,7 @@
 mlippautz@chromium.org
 nancylingwang@chromium.org
 nohle@chromium.org
+olivierrobin@chromium.org
 sebmarchand@chromium.org
 mthiesse@chromium.org
 rayankans@chromium.org
diff --git a/tools/metrics/histograms/histograms_xml/apps/histograms.xml b/tools/metrics/histograms/histograms_xml/apps/histograms.xml
index 26f0820..ccf4e801 100644
--- a/tools/metrics/histograms/histograms_xml/apps/histograms.xml
+++ b/tools/metrics/histograms/histograms_xml/apps/histograms.xml
@@ -1852,8 +1852,20 @@
   <owner>mmourgos@chromium.org</owner>
   <owner>newcomer@chromium.org</owner>
   <summary>
-    The number of folders that users have in their Launcher. Includes the OEM
-    folder. This metric is recorded every time the launcher is shown.
+    The number of folders that users have in their Launcher. Includes
+    system-created folders like &quot;Linux apps&quot; or the OEM folder.
+    Recorded every time the launcher is shown.
+  </summary>
+</histogram>
+
+<histogram name="Apps.NumberOfNonSystemFolders" units="folder(s)"
+    expires_after="2021-12-31">
+  <owner>jamescook@chromium.org</owner>
+  <owner>newcomer@chromium.org</owner>
+  <summary>
+    The number of folders that users have in their launcher. Does not include
+    system-created folders like &quot;Linux apps&quot; or the OEM folder.
+    Recorded every time the launcher is shown.
   </summary>
 </histogram>
 
diff --git a/tools/metrics/histograms/histograms_xml/compositing/histograms.xml b/tools/metrics/histograms/histograms_xml/compositing/histograms.xml
index acf03bb..61a39b6 100644
--- a/tools/metrics/histograms/histograms_xml/compositing/histograms.xml
+++ b/tools/metrics/histograms/histograms_xml/compositing/histograms.xml
@@ -642,11 +642,32 @@
     time recorded for Compositing.SurfaceAggregator.AggregateUs and is logged
     once per frame.
 
-    Warning: This metric may include reports from clients with low-resolution
-    clocks (i.e. on Windows, ref. |TimeTicks::IsHighResolution()|). Such reports
-    will cause this metric to have an abnormal distribution. When considering
-    revising this histogram, see UMA_HISTOGRAM_CUSTOM_MICROSECONDS_TIMES for the
-    solution.
+    Warning: This metric does not include reports from clients with
+    low-resolution clocks.
+  </summary>
+</histogram>
+
+<histogram name="Compositing.SurfaceAggregator.DeclareResourceCount"
+    units="resources" expires_after="2021-09-05">
+  <owner>kylechar@chromium.org</owner>
+  <owner>jonross@chromium.org</owner>
+  <summary>
+    The number of TransferableResources processed while declaring resources as
+    used during surface aggregation. This is logged once per frame.
+  </summary>
+</histogram>
+
+<histogram name="Compositing.SurfaceAggregator.DeclareResourcesUs"
+    units="microseconds" expires_after="2021-09-05">
+  <owner>kylechar@chromium.org</owner>
+  <owner>jonross@chromium.org</owner>
+  <summary>
+    Time spent declaring resources as used during surface aggregation. This is a
+    subset of the time recorded for Compositing.SurfaceAggregator.AggregateUs
+    and is logged once per frame.
+
+    Warning: This metric does not include reports from clients with
+    low-resolution clocks.
   </summary>
 </histogram>
 
@@ -680,11 +701,8 @@
     time recorded for Compositing.SurfaceAggregator.AggregateUs and is logged
     once per frame.
 
-    Warning: This metric may include reports from clients with low-resolution
-    clocks (i.e. on Windows, ref. |TimeTicks::IsHighResolution()|). Such reports
-    will cause this metric to have an abnormal distribution. When considering
-    revising this histogram, see UMA_HISTOGRAM_CUSTOM_MICROSECONDS_TIMES for the
-    solution.
+    Warning: This metric does not include reports from clients with
+    low-resolution clocks.
   </summary>
 </histogram>
 
diff --git a/tools/metrics/histograms/histograms_xml/ios/OWNERS b/tools/metrics/histograms/histograms_xml/ios/OWNERS
index a42ac77..1afb3f81b 100644
--- a/tools/metrics/histograms/histograms_xml/ios/OWNERS
+++ b/tools/metrics/histograms/histograms_xml/ios/OWNERS
@@ -4,3 +4,4 @@
 # Use chromium-metrics-reviews@google.com as a backup.
 ajuma@chromium.org
 javierrobles@chromium.org
+olivierrobin@chromium.org
diff --git a/tools/metrics/histograms/histograms_xml/ios/histograms.xml b/tools/metrics/histograms/histograms_xml/ios/histograms.xml
index e0be51d2..8faa959a 100644
--- a/tools/metrics/histograms/histograms_xml/ios/histograms.xml
+++ b/tools/metrics/histograms/histograms_xml/ios/histograms.xml
@@ -605,9 +605,12 @@
 </histogram>
 
 <histogram name="IOS.MetricKit.ForegroundExitData" enum="MetricKitExitData"
-    expires_after="2021-09-19">
+    expires_after="never">
+<!-- expires-never: guiding metric (internal: go/chrome-browser-guiding-metrics) -->
+
   <owner>justincohen@chromium.org</owner>
   <owner>olivierrobin@chromium.org</owner>
+  <owner>chrome-analysis-team@google.com</owner>
   <summary>
     The reason for the application termination in foreground. Recorded when a
     MXMetricPayload is received from the OS (at most once per day) at
@@ -623,6 +626,9 @@
 
     Recorded only if user opted in for sharing diagnostic data on iOS device.
     Note: The date the data is reported is later than the day it account for.
+
+    This histogram is of special interest to the chrome-analysis-team@. Do not
+    change its semantics or retire it without talking to them first.
   </summary>
 </histogram>
 
diff --git a/tools/metrics/histograms/histograms_xml/others/histograms.xml b/tools/metrics/histograms/histograms_xml/others/histograms.xml
index 7b7a4b11..b80f477 100644
--- a/tools/metrics/histograms/histograms_xml/others/histograms.xml
+++ b/tools/metrics/histograms/histograms_xml/others/histograms.xml
@@ -17535,21 +17535,15 @@
   </summary>
 </histogram>
 
-<histogram name="VoiceInteraction.AudioPermissionEvent{Timing}"
+<histogram name="VoiceInteraction.AudioPermissionEvent"
     enum="AudioPermissionState" expires_after="2021-08-22">
   <owner>basiaz@google.com</owner>
   <owner>chrome-language@google.com</owner>
   <summary>
     Android: The events related to granting or denying audio permission for
-    system level transcription (as opposite to Assistant). Will be logged
-    {Timing}.
+    system level transcription (as opposite to Assistant). Will be logged each
+    time non-Assistant voice recognition is triggered with a mic button click.
   </summary>
-  <token key="Timing">
-    <variant name=""
-        summary="each time non-Assistant voice recognition is triggered with
-                 a mic button press"/>
-    <variant name=".SessionStart" summary="on browser session start"/>
-  </token>
 </histogram>
 
 <histogram name="VoiceInteraction.DismissedEventSource"
diff --git a/tools/metrics/histograms/histograms_xml/service/histograms.xml b/tools/metrics/histograms/histograms_xml/service/histograms.xml
index 9427972..15a9895 100644
--- a/tools/metrics/histograms/histograms_xml/service/histograms.xml
+++ b/tools/metrics/histograms/histograms_xml/service/histograms.xml
@@ -736,6 +736,9 @@
 
 <histogram name="ServiceWorker.RegisteredOriginCount" units="origins"
     expires_after="2021-01-31">
+  <obsolete>
+    Removed 2021-04-29.
+  </obsolete>
   <owner>falken@chromium.org</owner>
   <owner>chrome-worker@google.com</owner>
   <summary>
@@ -744,6 +747,16 @@
   </summary>
 </histogram>
 
+<histogram name="ServiceWorker.RegisteredStorageKeyCount" units="origins"
+    expires_after="2022-04-29">
+  <owner>falken@chromium.org</owner>
+  <owner>chrome-worker@google.com</owner>
+  <summary>
+    The number of StorageKeys that have a service worker registration. Recorded
+    near browser startup, when the service worker storage system is initialized.
+  </summary>
+</histogram>
+
 <histogram name="ServiceWorker.Runtime" units="ms" expires_after="2020-07-30">
   <owner>falken@chromium.org</owner>
   <owner>chrome-worker@google.com</owner>
diff --git a/tools/metrics/histograms/histograms_xml/stability/histograms.xml b/tools/metrics/histograms/histograms_xml/stability/histograms.xml
index c75ada28..1843c7a 100644
--- a/tools/metrics/histograms/histograms_xml/stability/histograms.xml
+++ b/tools/metrics/histograms/histograms_xml/stability/histograms.xml
@@ -464,9 +464,9 @@
 </histogram>
 
 <histogram name="Stability.IOS.UTE.AppWillTerminateWasCalledInForeground"
-    enum="Boolean" expires_after="2021-06-06">
+    enum="Boolean" expires_after="2022-06-06">
   <owner>olivierrobin@chromium.org</owner>
-  <owner>eugenebut@google.com</owner>
+  <owner>ajuma@chromium.org</owner>
   <summary>
     Recorded on iOS when applicationWillTerminate callback is called when app is
     in the foreground. When this happens the app will not write clean exit
diff --git a/tools/perf/core/perfetto_binary_roller/binary_deps.json b/tools/perf/core/perfetto_binary_roller/binary_deps.json
index 2cdf40af..62f0ce1 100644
--- a/tools/perf/core/perfetto_binary_roller/binary_deps.json
+++ b/tools/perf/core/perfetto_binary_roller/binary_deps.json
@@ -1,16 +1,16 @@
 {
     "trace_processor_shell": {
         "win": {
-            "hash": "d0c78cf24062870d20522ddcc2a6064dc712eec7",
-            "remote_path": "perfetto_binaries/trace_processor_shell/win/52fefceb29bb88140996879a9a8970d7ea6dce83/trace_processor_shell.exe"
+            "hash": "2145b63282a8b7deabea927d60b4599ff621d3df",
+            "remote_path": "perfetto_binaries/trace_processor_shell/win/12f724860dbb6f6d820b13da4c3d715f4e8ec9aa/trace_processor_shell.exe"
         },
         "mac": {
-            "hash": "64cbd45c017f64469906075f8898be28b9be9fbf",
-            "remote_path": "perfetto_binaries/trace_processor_shell/mac/21d255cb2580c7d9b2a9a554b35a7faed6b2605a/trace_processor_shell"
+            "hash": "4468cc3fa3b7eec01941ccada8c915af9a3c7709",
+            "remote_path": "perfetto_binaries/trace_processor_shell/mac/12f724860dbb6f6d820b13da4c3d715f4e8ec9aa/trace_processor_shell"
         },
         "linux": {
-            "hash": "69e538301b42ba89cbe14e061ce227422140a206",
-            "remote_path": "perfetto_binaries/trace_processor_shell/linux/21d255cb2580c7d9b2a9a554b35a7faed6b2605a/trace_processor_shell"
+            "hash": "c54d1391c6738bf328449524f23c460ee9be434d",
+            "remote_path": "perfetto_binaries/trace_processor_shell/linux/12f724860dbb6f6d820b13da4c3d715f4e8ec9aa/trace_processor_shell"
         }
     },
     "power_profile.sql": {
diff --git a/ui/accessibility/platform/ax_platform_node_auralinux_unittest.cc b/ui/accessibility/platform/ax_platform_node_auralinux_unittest.cc
index 65848947..405db872 100644
--- a/ui/accessibility/platform/ax_platform_node_auralinux_unittest.cc
+++ b/ui/accessibility/platform/ax_platform_node_auralinux_unittest.cc
@@ -148,6 +148,7 @@
     base::Optional<ax::mojom::Role> role = base::nullopt) {
   AXNodeData new_data = AXNodeData();
   new_data.role = role.value_or(ax::mojom::Role::kApplication);
+  new_data.id = ax_node->data().id;
   ax_node->SetData(new_data);
   EnsureAtkObjectDoesNotHaveAttribute(atk_object, attribute_name);
 
@@ -176,6 +177,7 @@
     base::Optional<ax::mojom::Role> role = base::nullopt) {
   AXNodeData new_data = AXNodeData();
   new_data.role = role.value_or(ax::mojom::Role::kApplication);
+  new_data.id = ax_node->data().id;
   ax_node->SetData(new_data);
   EnsureAtkObjectDoesNotHaveAttribute(atk_object, attribute_name);
 
@@ -198,6 +200,7 @@
     base::Optional<ax::mojom::Role> role = base::nullopt) {
   AXNodeData new_data = AXNodeData();
   new_data.role = role.value_or(ax::mojom::Role::kApplication);
+  new_data.id = ax_node->data().id;
   ax_node->SetData(new_data);
   EnsureAtkObjectDoesNotHaveAttribute(atk_object, attribute_name);
 
@@ -416,6 +419,7 @@
   g_object_unref(state_set);
 
   root = AXNodeData();
+  root.id = 1;
   root.AddState(ax::mojom::State::kDefault);
   root.AddState(ax::mojom::State::kEditable);
   root.AddState(ax::mojom::State::kExpanded);
@@ -472,6 +476,7 @@
   g_object_unref(state_set);
 
   root = AXNodeData();
+  root.id = 1;
   root.AddState(ax::mojom::State::kInvisible);
   root.AddBoolAttribute(ax::mojom::BoolAttribute::kModal, true);
   GetRootAsAXNode()->SetData(root);
diff --git a/ui/base/BUILD.gn b/ui/base/BUILD.gn
index 07b180b..2bcb3fd 100644
--- a/ui/base/BUILD.gn
+++ b/ui/base/BUILD.gn
@@ -274,8 +274,6 @@
       "cocoa/flipped_view.mm",
       "cocoa/focus_tracker.h",
       "cocoa/focus_tracker.mm",
-      "cocoa/focus_window_set.h",
-      "cocoa/focus_window_set.mm",
       "cocoa/menu_controller.h",
       "cocoa/menu_controller.mm",
       "cocoa/nsmenuitem_additions.h",
diff --git a/ui/base/cocoa/focus_window_set.h b/ui/base/cocoa/focus_window_set.h
deleted file mode 100644
index 820195f69..0000000
--- a/ui/base/cocoa/focus_window_set.h
+++ /dev/null
@@ -1,33 +0,0 @@
-// Copyright 2013 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef UI_BASE_COCOA_FOCUS_WINDOW_SET_H_
-#define UI_BASE_COCOA_FOCUS_WINDOW_SET_H_
-
-#include <set>
-
-#include "base/component_export.h"
-#include "ui/gfx/native_widget_types.h"
-
-namespace ui {
-
-// Brings a group of windows to the front without changing their order, and
-// makes the frontmost one key and main. If none are visible, the frontmost
-// miniaturized window is deminiaturized.
-COMPONENT_EXPORT(UI_BASE)
-void FocusWindowSet(const std::set<gfx::NativeWindow>& windows);
-
-// Brings a group of windows to the front without changing their
-// order, and makes the frontmost one key and main. If none are
-// visible, the frontmost miniaturized window is deminiaturized. This
-// variant is meant to clean up after the system-default Dock icon
-// behavior. Unlike FocusWindowSet, only windows on the current space
-// are considered. It also ignores the hidden state of windows; the
-// window system may be in the middle of unhiding the application.
-COMPONENT_EXPORT(UI_BASE)
-void FocusWindowSetOnCurrentSpace(const std::set<gfx::NativeWindow>& windows);
-
-}  // namespace ui
-
-#endif  // UI_BASE_COCOA_FOCUS_WINDOW_SET_H_
diff --git a/ui/base/cocoa/focus_window_set.mm b/ui/base/cocoa/focus_window_set.mm
deleted file mode 100644
index 06e7223..0000000
--- a/ui/base/cocoa/focus_window_set.mm
+++ /dev/null
@@ -1,92 +0,0 @@
-// Copyright 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.
-
-#import <Cocoa/Cocoa.h>
-
-#include "base/check.h"
-#include "ui/base/cocoa/focus_window_set.h"
-
-namespace ui {
-
-namespace {
-
-// This attempts to match OS X's native behavior, namely that a window
-// is only ever deminiaturized if ALL windows on ALL workspaces are
-// miniaturized.
-void FocusWindowSetHelper(const std::set<gfx::NativeWindow>& windows,
-                          bool allow_workspace_switch,
-                          bool visible_windows_only) {
-  NSArray* ordered_windows = [NSApp orderedWindows];
-  NSWindow* frontmost_window = nil;
-  NSWindow* frontmost_window_all_spaces = nil;
-  NSWindow* frontmost_miniaturized_window = nil;
-  bool all_miniaturized = true;
-  for (int i = [ordered_windows count] - 1; i >= 0; i--) {
-    NSWindow* win = ordered_windows[i];
-    if (windows.find(win) == windows.end())
-      continue;
-    if ([win isMiniaturized]) {
-      frontmost_miniaturized_window = win;
-    } else if (!visible_windows_only || [win isVisible]) {
-      all_miniaturized = false;
-      frontmost_window_all_spaces = win;
-      if ([win isOnActiveSpace]) {
-        // Raise the old |frontmost_window| (if any). The topmost |win| will be
-        // raised with makeKeyAndOrderFront: below.
-        [frontmost_window orderFront:nil];
-        frontmost_window = win;
-      }
-    }
-  }
-  if (all_miniaturized && frontmost_miniaturized_window) {
-    DCHECK(!frontmost_window);
-    // Note the call to makeKeyAndOrderFront: will deminiaturize the window.
-    frontmost_window = frontmost_miniaturized_window;
-  }
-  // If we couldn't find one on the active space, consider all spaces.
-  if (allow_workspace_switch && !frontmost_window)
-    frontmost_window = frontmost_window_all_spaces;
-
-  if (frontmost_window) {
-    [frontmost_window makeKeyAndOrderFront:nil];
-    [NSApp activateIgnoringOtherApps:YES];
-  }
-}
-
-}  // namespace
-
-void FocusWindowSet(const std::set<gfx::NativeWindow>& windows) {
-  FocusWindowSetHelper(windows, true, true);
-}
-
-void FocusWindowSetOnCurrentSpace(const std::set<gfx::NativeWindow>& windows) {
-  // This callback runs before AppKit picks its own window to
-  // deminiaturize, so we get to pick one from the right set. Limit to
-  // the windows on the current workspace. Otherwise we jump spaces
-  // haphazardly.
-  //
-  // Also consider both visible and hidden windows; this call races
-  // with the system unhiding the application. http://crbug.com/368238
-  //
-  // NOTE: If this is called in the
-  // applicationShouldHandleReopen:hasVisibleWindows: hook when
-  // clicking the dock icon, and that caused OS X to begin switch
-  // spaces, isOnActiveSpace gives the answer for the PREVIOUS
-  // space. This means that we actually raise and focus the wrong
-  // space's windows, leaving the new key window off-screen. To detect
-  // this, check if the key window is on the active space prior to
-  // calling.
-  //
-  // Also, if we decide to deminiaturize a window during a space switch,
-  // that can switch spaces and then switch back. Fortunately, this only
-  // happens if, say, space 1 contains an app, space 2 contains a
-  // miniaturized browser. We click the icon, OS X switches to space 1,
-  // we deminiaturize the browser, and that triggers switching back.
-  //
-  // TODO(davidben): To limit those cases, consider preferentially
-  // deminiaturizing a window on the current space.
-  FocusWindowSetHelper(windows, false, false);
-}
-
-}  // namespace ui
diff --git a/ui/ozone/platform/wayland/BUILD.gn b/ui/ozone/platform/wayland/BUILD.gn
index cf68b2d5..4f3ecb62 100644
--- a/ui/ozone/platform/wayland/BUILD.gn
+++ b/ui/ozone/platform/wayland/BUILD.gn
@@ -140,6 +140,8 @@
     "host/wayland_zcr_cursor_shapes.h",
     "host/wayland_zwp_linux_dmabuf.cc",
     "host/wayland_zwp_linux_dmabuf.h",
+    "host/wayland_zwp_pointer_gestures.cc",
+    "host/wayland_zwp_pointer_gestures.h",
     "host/xdg_foreign_wrapper.cc",
     "host/xdg_foreign_wrapper.h",
     "host/xdg_popup_wrapper_impl.cc",
@@ -187,6 +189,7 @@
     "//third_party/wayland-protocols:keyboard_extension_protocol",
     "//third_party/wayland-protocols:linux_dmabuf_protocol",
     "//third_party/wayland-protocols:linux_explicit_synchronization_protocol",
+    "//third_party/wayland-protocols:pointer_gestures_protocol",
     "//third_party/wayland-protocols:presentation_time_protocol",
     "//third_party/wayland-protocols:primary_selection_protocol",
     "//third_party/wayland-protocols:text_input_protocol",
diff --git a/ui/ozone/platform/wayland/common/wayland_object.cc b/ui/ozone/platform/wayland/common/wayland_object.cc
index 13643e0..5f63eb03 100644
--- a/ui/ozone/platform/wayland/common/wayland_object.cc
+++ b/ui/ozone/platform/wayland/common/wayland_object.cc
@@ -12,6 +12,7 @@
 #include <keyboard-extension-unstable-v1-client-protocol.h>
 #include <linux-dmabuf-unstable-v1-client-protocol.h>
 #include <linux-explicit-synchronization-unstable-v1-client-protocol.h>
+#include <pointer-gestures-unstable-v1-client-protocol.h>
 #include <presentation-time-client-protocol.h>
 #include <primary-selection-unstable-v1-client-protocol.h>
 #include <text-input-unstable-v1-client-protocol.h>
@@ -231,6 +232,16 @@
 const wl_interface* ObjectTraits<struct wl_proxy>::interface = nullptr;
 void (*ObjectTraits<wl_proxy>::deleter)(void*) = &wl_proxy_wrapper_destroy;
 
+const wl_interface* ObjectTraits<zwp_pointer_gesture_pinch_v1>::interface =
+    &zwp_pointer_gesture_pinch_v1_interface;
+void (*ObjectTraits<zwp_pointer_gesture_pinch_v1>::deleter)(
+    zwp_pointer_gesture_pinch_v1*) = &zwp_pointer_gesture_pinch_v1_destroy;
+
+const wl_interface* ObjectTraits<zwp_pointer_gestures_v1>::interface =
+    &zwp_pointer_gestures_v1_interface;
+void (*ObjectTraits<zwp_pointer_gestures_v1>::deleter)(
+    zwp_pointer_gestures_v1*) = &zwp_pointer_gestures_v1_destroy;
+
 const wl_interface* ObjectTraits<wp_viewport>::interface =
     &wp_viewport_interface;
 void (*ObjectTraits<wp_viewport>::deleter)(wp_viewport*) = &wp_viewport_destroy;
diff --git a/ui/ozone/platform/wayland/common/wayland_object.h b/ui/ozone/platform/wayland/common/wayland_object.h
index 6f0208a6..ef805db 100644
--- a/ui/ozone/platform/wayland/common/wayland_object.h
+++ b/ui/ozone/platform/wayland/common/wayland_object.h
@@ -63,6 +63,8 @@
 struct zwp_linux_buffer_release_v1;
 struct zwp_linux_explicit_synchronization_v1;
 struct zwp_linux_surface_synchronization_v1;
+struct zwp_pointer_gesture_pinch_v1;
+struct zwp_pointer_gestures_v1;
 struct zxdg_shell_v6;
 struct zxdg_surface_v6;
 struct zxdg_toplevel_v6;
@@ -309,6 +311,18 @@
 };
 
 template <>
+struct ObjectTraits<zwp_pointer_gesture_pinch_v1> {
+  static const wl_interface* interface;
+  static void (*deleter)(zwp_pointer_gesture_pinch_v1*);
+};
+
+template <>
+struct ObjectTraits<zwp_pointer_gestures_v1> {
+  static const wl_interface* interface;
+  static void (*deleter)(zwp_pointer_gestures_v1*);
+};
+
+template <>
 struct ObjectTraits<wp_viewport> {
   static const wl_interface* interface;
   static void (*deleter)(wp_viewport*);
diff --git a/ui/ozone/platform/wayland/host/gtk_primary_selection_device.cc b/ui/ozone/platform/wayland/host/gtk_primary_selection_device.cc
index ed4dc91..29f02107 100644
--- a/ui/ozone/platform/wayland/host/gtk_primary_selection_device.cc
+++ b/ui/ozone/platform/wayland/host/gtk_primary_selection_device.cc
@@ -28,9 +28,9 @@
 
 void GtkPrimarySelectionDevice::SetSelectionSource(
     GtkPrimarySelectionSource* source) {
-  DCHECK(source);
-  gtk_primary_selection_device_set_selection(
-      data_device_.get(), source->data_source(), connection()->serial());
+  auto* data_source = source ? source->data_source() : nullptr;
+  gtk_primary_selection_device_set_selection(data_device_.get(), data_source,
+                                             connection()->serial());
   connection()->ScheduleFlush();
 }
 
diff --git a/ui/ozone/platform/wayland/host/wayland_connection.cc b/ui/ozone/platform/wayland/host/wayland_connection.cc
index 5df8ae4..64256c1c 100644
--- a/ui/ozone/platform/wayland/host/wayland_connection.cc
+++ b/ui/ozone/platform/wayland/host/wayland_connection.cc
@@ -45,6 +45,7 @@
 #include "ui/ozone/platform/wayland/host/wayland_zaura_shell.h"
 #include "ui/ozone/platform/wayland/host/wayland_zcr_cursor_shapes.h"
 #include "ui/ozone/platform/wayland/host/wayland_zwp_linux_dmabuf.h"
+#include "ui/ozone/platform/wayland/host/wayland_zwp_pointer_gestures.h"
 #include "ui/ozone/platform/wayland/host/xdg_foreign_wrapper.h"
 #include "ui/ozone/platform/wayland/host/zwp_primary_selection_device_manager.h"
 #include "ui/platform_window/common/platform_window_defaults.h"
@@ -274,6 +275,11 @@
       // Wayland doesn't expose InputDeviceType.
       devices.emplace_back(InputDevice(
           pointer_->id(), InputDeviceType::INPUT_DEVICE_UNKNOWN, "pointer"));
+
+      // Pointer is required for PointerGestures to be functional.
+      if (wayland_zwp_pointer_gestures_)
+        wayland_zwp_pointer_gestures_->Init();
+
     } else {
       LOG(ERROR) << "Failed to get wl_pointer from seat";
     }
@@ -571,6 +577,17 @@
     }
     connection->zaura_shell_ =
         std::make_unique<WaylandZAuraShell>(zaura_shell.release(), connection);
+  } else if (!connection->wayland_zwp_pointer_gestures_ &&
+             (strcmp(interface, "zwp_pointer_gestures_v1") == 0)) {
+    auto zwp_pointer_gestures_v1 =
+        wl::Bind<struct zwp_pointer_gestures_v1>(registry, name, version);
+    if (!zwp_pointer_gestures_v1) {
+      LOG(ERROR) << "Failed to bind wp_pointer_gestures_v1";
+      return;
+    }
+    connection->wayland_zwp_pointer_gestures_ =
+        std::make_unique<WaylandZwpPointerGestures>(
+            zwp_pointer_gestures_v1.release(), connection);
   } else if (!connection->xdg_decoration_manager_ &&
              strcmp(interface, "zxdg_decoration_manager_v1") == 0) {
     connection->xdg_decoration_manager_ =
diff --git a/ui/ozone/platform/wayland/host/wayland_connection.h b/ui/ozone/platform/wayland/host/wayland_connection.h
index 9f98934..f29959f9 100644
--- a/ui/ozone/platform/wayland/host/wayland_connection.h
+++ b/ui/ozone/platform/wayland/host/wayland_connection.h
@@ -42,6 +42,7 @@
 class WaylandTouch;
 class WaylandZAuraShell;
 class WaylandZcrCursorShapes;
+class WaylandZwpPointerGestures;
 class WaylandZwpLinuxDmabuf;
 class WaylandDataDeviceManager;
 class WaylandCursorPosition;
@@ -274,6 +275,7 @@
   std::unique_ptr<WaylandCursorPosition> wayland_cursor_position_;
   std::unique_ptr<WaylandZAuraShell> zaura_shell_;
   std::unique_ptr<WaylandZcrCursorShapes> zcr_cursor_shapes_;
+  std::unique_ptr<WaylandZwpPointerGestures> wayland_zwp_pointer_gestures_;
   std::unique_ptr<WaylandZwpLinuxDmabuf> zwp_dmabuf_;
   std::unique_ptr<WaylandDrm> drm_;
   std::unique_ptr<WaylandShm> shm_;
diff --git a/ui/ozone/platform/wayland/host/wayland_zwp_pointer_gestures.cc b/ui/ozone/platform/wayland/host/wayland_zwp_pointer_gestures.cc
new file mode 100644
index 0000000..a5351e52
--- /dev/null
+++ b/ui/ozone/platform/wayland/host/wayland_zwp_pointer_gestures.cc
@@ -0,0 +1,72 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ui/ozone/platform/wayland/host/wayland_zwp_pointer_gestures.h"
+
+#include <pointer-gestures-unstable-v1-client-protocol.h>
+
+#include "ui/ozone/platform/wayland/host/wayland_connection.h"
+#include "ui/ozone/platform/wayland/host/wayland_pointer.h"
+
+namespace ui {
+
+WaylandZwpPointerGestures::WaylandZwpPointerGestures(
+    zwp_pointer_gestures_v1* pointer_gestures,
+    WaylandConnection* connection)
+    : obj_(pointer_gestures), connection_(connection) {
+  DCHECK(obj_);
+  DCHECK(connection_);
+}
+
+WaylandZwpPointerGestures::~WaylandZwpPointerGestures() = default;
+
+void WaylandZwpPointerGestures::Init() {
+  DCHECK(connection_->pointer());
+
+  pinch_.reset(zwp_pointer_gestures_v1_get_pinch_gesture(
+      obj_.get(), connection_->pointer()->wl_object()));
+
+  static const zwp_pointer_gesture_pinch_v1_listener
+      zwp_pointer_gesture_pinch_v1_listener = {
+          &WaylandZwpPointerGestures::OnPinchBegin,
+          &WaylandZwpPointerGestures::OnPinchUpdate,
+          &WaylandZwpPointerGestures::OnPinchEnd,
+      };
+  zwp_pointer_gesture_pinch_v1_add_listener(
+      pinch_.get(), &zwp_pointer_gesture_pinch_v1_listener, this);
+}
+
+// static
+void WaylandZwpPointerGestures::OnPinchBegin(
+    void* data,
+    struct zwp_pointer_gesture_pinch_v1* zwp_pointer_gesture_pinch_v1,
+    uint32_t serial,
+    uint32_t time,
+    struct wl_surface* surface,
+    uint32_t fingers) {
+  NOTIMPLEMENTED_LOG_ONCE();
+}
+
+// static
+void WaylandZwpPointerGestures::OnPinchUpdate(
+    void* data,
+    struct zwp_pointer_gesture_pinch_v1* zwp_pointer_gesture_pinch_v1,
+    uint32_t time,
+    wl_fixed_t dx,
+    wl_fixed_t dy,
+    wl_fixed_t scale,
+    wl_fixed_t rotation) {
+  NOTIMPLEMENTED_LOG_ONCE();
+}
+
+void WaylandZwpPointerGestures::OnPinchEnd(
+    void* data,
+    struct zwp_pointer_gesture_pinch_v1* zwp_pointer_gesture_pinch_v1,
+    uint32_t serial,
+    uint32_t time,
+    int32_t cancelled) {
+  NOTIMPLEMENTED_LOG_ONCE();
+}
+
+}  // namespace ui
diff --git a/ui/ozone/platform/wayland/host/wayland_zwp_pointer_gestures.h b/ui/ozone/platform/wayland/host/wayland_zwp_pointer_gestures.h
new file mode 100644
index 0000000..5a174f1
--- /dev/null
+++ b/ui/ozone/platform/wayland/host/wayland_zwp_pointer_gestures.h
@@ -0,0 +1,59 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_OZONE_PLATFORM_WAYLAND_HOST_WAYLAND_ZWP_POINTER_GESTURES_H_
+#define UI_OZONE_PLATFORM_WAYLAND_HOST_WAYLAND_ZWP_POINTER_GESTURES_H_
+
+#include "ui/ozone/platform/wayland/common/wayland_object.h"
+
+namespace ui {
+
+class WaylandConnection;
+
+// Wraps the zwp_pointer_gestures and zwp_pointer_gesture_pinch_v1 objects.
+class WaylandZwpPointerGestures {
+ public:
+  WaylandZwpPointerGestures(zwp_pointer_gestures_v1* pointer_gestures,
+                            WaylandConnection* connection);
+  WaylandZwpPointerGestures(const WaylandZwpPointerGestures&) = delete;
+  WaylandZwpPointerGestures& operator=(const WaylandZwpPointerGestures&) =
+      delete;
+  ~WaylandZwpPointerGestures();
+
+  // Init is called by WaylandConnection when its wl_pointer object is
+  // instantiated.
+  void Init();
+
+ private:
+  // zwp_pointer_gesture_pinch_v1_listener
+  static void OnPinchBegin(
+      void* data,
+      struct zwp_pointer_gesture_pinch_v1* zwp_pointer_gesture_pinch_v1,
+      uint32_t serial,
+      uint32_t time,
+      struct wl_surface* surface,
+      uint32_t fingers);
+  static void OnPinchUpdate(
+      void* data,
+      struct zwp_pointer_gesture_pinch_v1* zwp_pointer_gesture_pinch_v1,
+      uint32_t time,
+      wl_fixed_t dx,
+      wl_fixed_t dy,
+      wl_fixed_t scale,
+      wl_fixed_t rotation);
+  static void OnPinchEnd(
+      void* data,
+      struct zwp_pointer_gesture_pinch_v1* zwp_pointer_gesture_pinch_v1,
+      uint32_t serial,
+      uint32_t time,
+      int32_t cancelled);
+
+  wl::Object<zwp_pointer_gestures_v1> obj_;
+  wl::Object<zwp_pointer_gesture_pinch_v1> pinch_;
+  WaylandConnection* const connection_;
+};
+
+}  // namespace ui
+
+#endif  // UI_OZONE_PLATFORM_WAYLAND_HOST_WAYLAND_ZWP_POINTER_GESTURES_H_
diff --git a/ui/ozone/platform/wayland/host/xdg_toplevel_wrapper_impl.h b/ui/ozone/platform/wayland/host/xdg_toplevel_wrapper_impl.h
index 15d16f7..920f89a7 100644
--- a/ui/ozone/platform/wayland/host/xdg_toplevel_wrapper_impl.h
+++ b/ui/ozone/platform/wayland/host/xdg_toplevel_wrapper_impl.h
@@ -26,7 +26,7 @@
   XDGToplevelWrapperImpl& operator=(const XDGToplevelWrapperImpl&) = delete;
   ~XDGToplevelWrapperImpl() override;
 
-  // ShellSurfaceWrapper overrides:
+  // ShellToplevelWrapper overrides:
   bool Initialize() override;
   void SetMaximized() override;
   void UnSetMaximized() override;
diff --git a/ui/ozone/platform/wayland/host/zwp_primary_selection_device.cc b/ui/ozone/platform/wayland/host/zwp_primary_selection_device.cc
index a9e657d3..33c26d79 100644
--- a/ui/ozone/platform/wayland/host/zwp_primary_selection_device.cc
+++ b/ui/ozone/platform/wayland/host/zwp_primary_selection_device.cc
@@ -28,9 +28,9 @@
 
 void ZwpPrimarySelectionDevice::SetSelectionSource(
     ZwpPrimarySelectionSource* source) {
-  DCHECK(source);
-  zwp_primary_selection_device_v1_set_selection(
-      data_device_.get(), source->data_source(), connection()->serial());
+  auto* data_source = source ? source->data_source() : nullptr;
+  zwp_primary_selection_device_v1_set_selection(data_device_.get(), data_source,
+                                                connection()->serial());
   connection()->ScheduleFlush();
 }
 
diff --git a/ui/views/BUILD.gn b/ui/views/BUILD.gn
index a32377d..3769ddb2 100644
--- a/ui/views/BUILD.gn
+++ b/ui/views/BUILD.gn
@@ -793,7 +793,6 @@
         "widget/desktop_aura/desktop_focus_rules.cc",
         "widget/desktop_aura/desktop_native_cursor_manager.cc",
         "widget/desktop_aura/desktop_native_widget_aura.cc",
-        "widget/desktop_aura/desktop_screen.cc",
         "widget/desktop_aura/desktop_screen_position_client.cc",
         "widget/desktop_aura/desktop_window_tree_host.cc",
       ]
diff --git a/ui/views/controls/editable_combobox/editable_combobox_unittest.cc b/ui/views/controls/editable_combobox/editable_combobox_unittest.cc
index d5178997..716c3c6 100644
--- a/ui/views/controls/editable_combobox/editable_combobox_unittest.cc
+++ b/ui/views/controls/editable_combobox/editable_combobox_unittest.cc
@@ -587,8 +587,8 @@
   EXPECT_EQ(u"a  b", combobox_->GetText());
 }
 
-#if defined(OS_WIN)
-// Flaky on Windows. https://crbug.com/965601
+#if defined(OS_WIN) || defined(OS_LINUX)
+// Flaky on Windows and Linux. https://crbug.com/965601
 #define MAYBE_MenuCanAdaptToContentChange DISABLED_MenuCanAdaptToContentChange
 #else
 #define MAYBE_MenuCanAdaptToContentChange MenuCanAdaptToContentChange
@@ -623,7 +623,15 @@
   EXPECT_EQ(menu_runner1, menu_runner2);
 }
 
-TEST_F(EditableComboboxTest, RefocusingReopensMenuBasedOnLatestContent) {
+#if defined(OS_LINUX)
+// Flaky on Linux. https://crbug.com/1204584
+#define MAYBE_RefocusingReopensMenuBasedOnLatestContent \
+    DISABLED_RefocusingReopensMenuBasedOnLatestContent
+#else
+#define MAYBE_RefocusingReopensMenuBasedOnLatestContent \
+    RefocusingReopensMenuBasedOnLatestContent
+#endif
+TEST_F(EditableComboboxTest, MAYBE_RefocusingReopensMenuBasedOnLatestContent) {
   std::vector<std::u16string> items = {u"abc", u"abd", u"bac", u"bad", u"bac2"};
   InitEditableCombobox(items, /*filter_on_edit=*/true);
   combobox_->GetTextfieldForTest()->RequestFocus();
diff --git a/ui/views/examples/examples_main_proc.cc b/ui/views/examples/examples_main_proc.cc
index 883b448..0f0ff43 100644
--- a/ui/views/examples/examples_main_proc.cc
+++ b/ui/views/examples/examples_main_proc.cc
@@ -172,7 +172,7 @@
 #endif
 #if BUILDFLAG(ENABLE_DESKTOP_AURA)
     std::unique_ptr<display::Screen> desktop_screen =
-        base::WrapUnique(views::CreateDesktopScreen());
+        views::CreateDesktopScreen();
 #endif
 
     base::RunLoop run_loop(base::RunLoop::Type::kNestableTasksAllowed);
diff --git a/ui/views/widget/desktop_aura/desktop_screen.cc b/ui/views/widget/desktop_aura/desktop_screen.cc
deleted file mode 100644
index 5ced5db..0000000
--- a/ui/views/widget/desktop_aura/desktop_screen.cc
+++ /dev/null
@@ -1,24 +0,0 @@
-// Copyright 2018 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "ui/views/widget/desktop_aura/desktop_screen.h"
-
-#include "build/chromeos_buildflags.h"
-#include "ui/display/screen.h"
-
-namespace views {
-
-void InstallDesktopScreenIfNecessary() {
-#if BUILDFLAG(IS_CHROMEOS_ASH)
-  // ChromeOS ozone builds use another path instead, where display::Screen is
-  // properly set. Thus, do early return here.
-  return;
-#endif
-
-  // The screen may have already been set in test initialization.
-  if (!display::Screen::GetScreen())
-    CreateDesktopScreen();
-}
-
-}  // namespace views
diff --git a/ui/views/widget/desktop_aura/desktop_screen.h b/ui/views/widget/desktop_aura/desktop_screen.h
index 1f1ff49..32505c2 100644
--- a/ui/views/widget/desktop_aura/desktop_screen.h
+++ b/ui/views/widget/desktop_aura/desktop_screen.h
@@ -5,6 +5,8 @@
 #ifndef UI_VIEWS_WIDGET_DESKTOP_AURA_DESKTOP_SCREEN_H_
 #define UI_VIEWS_WIDGET_DESKTOP_AURA_DESKTOP_SCREEN_H_
 
+#include <memory>
+
 #include "ui/views/views_export.h"
 
 namespace display {
@@ -14,10 +16,8 @@
 namespace views {
 
 // Creates a Screen that represents the screen of the environment that hosts
-// a WindowTreeHost. Caller owns the result.
-VIEWS_EXPORT display::Screen* CreateDesktopScreen();
-
-VIEWS_EXPORT void InstallDesktopScreenIfNecessary();
+// a WindowTreeHost.
+VIEWS_EXPORT std::unique_ptr<display::Screen> CreateDesktopScreen();
 
 }  // namespace views
 
diff --git a/ui/views/widget/desktop_aura/desktop_screen_linux.cc b/ui/views/widget/desktop_aura/desktop_screen_linux.cc
index 7033e867..0eba478b 100644
--- a/ui/views/widget/desktop_aura/desktop_screen_linux.cc
+++ b/ui/views/widget/desktop_aura/desktop_screen_linux.cc
@@ -58,13 +58,13 @@
 
 #endif
 
-display::Screen* CreateDesktopScreen() {
+std::unique_ptr<display::Screen> CreateDesktopScreen() {
 #if defined(USE_OZONE)
   if (features::IsUsingOzonePlatform())
-    return new DesktopScreenOzoneLinux();
+    return std::make_unique<DesktopScreenOzoneLinux>();
 #endif
 #if defined(USE_X11)
-  auto* screen = new DesktopScreenX11();
+  auto screen = std::make_unique<DesktopScreenX11>();
   screen->Init();
   return screen;
 #else
diff --git a/ui/views/widget/desktop_aura/desktop_screen_ozone.cc b/ui/views/widget/desktop_aura/desktop_screen_ozone.cc
index 1f00883..d1195d5 100644
--- a/ui/views/widget/desktop_aura/desktop_screen_ozone.cc
+++ b/ui/views/widget/desktop_aura/desktop_screen_ozone.cc
@@ -4,6 +4,8 @@
 
 #include "ui/views/widget/desktop_aura/desktop_screen_ozone.h"
 
+#include <memory>
+
 #include "build/build_config.h"
 #include "ui/aura/screen_ozone.h"
 #include "ui/views/widget/desktop_aura/desktop_screen.h"
@@ -27,8 +29,8 @@
 // factory method for OS_LINUX as Linux has a factory method that decides what
 // screen to use based on IsUsingOzonePlatform feature flag.
 #if !defined(OS_LINUX) && !defined(OS_CHROMEOS)
-display::Screen* CreateDesktopScreen() {
-  return new DesktopScreenOzone();
+std::unique_ptr<display::Screen> CreateDesktopScreen() {
+  return std::make_unique<aura::ScreenOzone>();
 }
 #endif
 
diff --git a/ui/views/widget/desktop_aura/desktop_screen_win.cc b/ui/views/widget/desktop_aura/desktop_screen_win.cc
index 91c5164..7c352dd 100644
--- a/ui/views/widget/desktop_aura/desktop_screen_win.cc
+++ b/ui/views/widget/desktop_aura/desktop_screen_win.cc
@@ -4,6 +4,8 @@
 
 #include "ui/views/widget/desktop_aura/desktop_screen_win.h"
 
+#include <memory>
+
 #include "ui/aura/window.h"
 #include "ui/aura/window_tree_host.h"
 #include "ui/views/widget/desktop_aura/desktop_screen.h"
@@ -35,8 +37,8 @@
 
 ////////////////////////////////////////////////////////////////////////////////
 
-display::Screen* CreateDesktopScreen() {
-  return new DesktopScreenWin;
+std::unique_ptr<display::Screen> CreateDesktopScreen() {
+  return std::make_unique<DesktopScreenWin>();
 }
 
 }  // namespace views
diff --git a/ui/views_content_client/views_content_client_main_parts_desktop_aura.cc b/ui/views_content_client/views_content_client_main_parts_desktop_aura.cc
index c5eb0a5..27a5253b 100644
--- a/ui/views_content_client/views_content_client_main_parts_desktop_aura.cc
+++ b/ui/views_content_client/views_content_client_main_parts_desktop_aura.cc
@@ -27,6 +27,10 @@
 
   // ViewsContentClientMainPartsAura:
   int PreMainMessageLoopRun() override;
+  void PostMainMessageLoopRun() override;
+
+ private:
+  std::unique_ptr<display::Screen> screen_;
 };
 
 ViewsContentClientMainPartsDesktopAura::ViewsContentClientMainPartsDesktopAura(
@@ -38,13 +42,19 @@
 int ViewsContentClientMainPartsDesktopAura::PreMainMessageLoopRun() {
   ViewsContentClientMainPartsAura::PreMainMessageLoopRun();
 
-  views::CreateDesktopScreen();
+  screen_ = views::CreateDesktopScreen();
 
   views_content_client()->OnPreMainMessageLoopRun(browser_context(), nullptr);
 
   return content::RESULT_CODE_NORMAL_EXIT;
 }
 
+void ViewsContentClientMainPartsDesktopAura::PostMainMessageLoopRun() {
+  screen_.reset();
+
+  ViewsContentClientMainPartsAura::PostMainMessageLoopRun();
+}
+
 }  // namespace
 
 // static
diff --git a/ui/webui/resources/cr_components/chromeos/network/network_siminfo.html b/ui/webui/resources/cr_components/chromeos/network/network_siminfo.html
index 4b50dfa..5d83e31c 100644
--- a/ui/webui/resources/cr_components/chromeos/network/network_siminfo.html
+++ b/ui/webui/resources/cr_components/chromeos/network/network_siminfo.html
@@ -26,6 +26,10 @@
         margin-inline-end: 10px;
       }
 
+      cr-toggle {
+        margin-inline-start: var(--cr-button-edge-spacing);
+      }
+
       .error {
         color: red;
         font-weight: 500;
@@ -39,8 +43,8 @@
         margin-inline-start: var(--cr-section-padding);
       }
 
-      cr-toggle {
-        margin-inline-start: var(--cr-button-edge-spacing);
+      .pin-required-subtext {
+        color: var(--google-grey-700);
       }
     </style>
 
@@ -74,8 +78,13 @@
     <!-- SIM unlocked -->
     <template is="dom-if" if="[[eq_(State.SIM_UNLOCKED, state_)]]" restamp>
       <div class="property-box two-line">
-        <div id="simLockToggleLabel" class="start">
-          [[i18n('networkSimLockEnable')]]
+        <div class="flex layout vertical"  aria-hidden="true">
+          <div id="pinRequiredLabel">
+            [[i18n('networkSimLockEnable')]]
+          </div>
+          <div id="pinRequiredSublabel" class="pin-required-subtext">
+            [[i18n('networkSimLockEnableSublabel')]]
+          </div>
         </div>
         <cr-button id="changePinButton" on-click="onChangePinTap_"
             hidden$="[[!showChangePinButton_(deviceState, isActiveSim_)]]"
@@ -84,7 +93,7 @@
         </cr-button>
         <template is="dom-if" if="[[!isActiveSim_]]" restamp>
           <iron-icon id="help-icon" tabindex="0" icon="cr:help-outline"
-              aria-labelledby="simLockToggleLabel inActiveSimLockTooltip">
+              aria-labelledby="pinRequiredLabel pinRequiredSublabel inActiveSimLockTooltip">
           </iron-icon>
           <paper-tooltip id="inActiveSimLockTooltip" for="help-icon" position="bottom"
               aria-hidden="true" fit-to-visible-bounds>
@@ -95,7 +104,7 @@
         <cr-toggle id="simLockButton"
             disabled="[[isSimLockButtonDisabled_(disabled, isActiveSim_)]]"
             on-change="onSimLockEnabledChange_" checked="{{lockEnabled_}}"
-            aria-labelledby="simLockToggleLabel">
+            aria-labelledby="pinRequiredLabel pinRequiredSublabel">
         </cr-toggle>
       </div>
     </template>
diff --git a/weblayer/browser/navigation_browsertest.cc b/weblayer/browser/navigation_browsertest.cc
index 1e8f898..624ee6a 100644
--- a/weblayer/browser/navigation_browsertest.cc
+++ b/weblayer/browser/navigation_browsertest.cc
@@ -245,7 +245,10 @@
       base::BindLambdaForTesting([&](Navigation* navigation) {
         observer.reset();
         shell()->browser()->DestroyTab(new_tab);
-        run_loop.Quit();
+        // Destroying the tab posts a task to delete the WebContents, which must
+        // be run before the test shuts down lest it access deleted state.
+        base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
+                                                      run_loop.QuitClosure());
       }));
   new_tab->GetNavigationController()->Navigate(
       embedded_test_server()->GetURL("/simple_pageX.html"));
diff --git a/weblayer/shell/DEPS b/weblayer/shell/DEPS
index e0b45c8d..4166cef 100644
--- a/weblayer/shell/DEPS
+++ b/weblayer/shell/DEPS
@@ -2,6 +2,7 @@
   "+net",
   "+ui/aura",
   "+ui/base",
+  "+ui/display",
   "+ui/events",
   "+ui/gfx",
   "+ui/native_theme",
diff --git a/weblayer/shell/browser/shell.h b/weblayer/shell/browser/shell.h
index 47600ee..0748b22 100644
--- a/weblayer/shell/browser/shell.h
+++ b/weblayer/shell/browser/shell.h
@@ -27,9 +27,14 @@
 class Widget;
 class ViewsDelegate;
 }  // namespace views
+#if !defined(OS_CHROMEOS)
+namespace display {
+class Screen;
+}
 namespace wm {
 class WMState;
 }
+#endif
 #endif  // defined(USE_AURA)
 
 class GURL;
@@ -161,6 +166,7 @@
   base::android::ScopedJavaGlobalRef<jobject> java_object_;
 #elif defined(USE_AURA)
   static wm::WMState* wm_state_;
+  static display::Screen* screen_;
 #if defined(TOOLKIT_VIEWS)
   static views::ViewsDelegate* views_delegate_;
 
diff --git a/weblayer/shell/browser/shell_views.cc b/weblayer/shell/browser/shell_views.cc
index d11fea6..aedc40d6 100644
--- a/weblayer/shell/browser/shell_views.cc
+++ b/weblayer/shell/browser/shell_views.cc
@@ -32,12 +32,13 @@
 #include "ui/views/metadata/metadata_impl_macros.h"
 #include "ui/views/test/desktop_test_views_delegate.h"
 #include "ui/views/view.h"
-#include "ui/views/widget/desktop_aura/desktop_screen.h"
 #include "ui/views/widget/widget.h"
 #include "ui/views/widget/widget_delegate.h"
 #include "weblayer/public/tab.h"
 
-#if defined(USE_AURA)
+#if defined(USE_AURA) && !defined(OS_CHROMEOS)
+#include "ui/display/screen.h"
+#include "ui/views/widget/desktop_aura/desktop_screen.h"
 #include "ui/wm/core/wm_state.h"
 #endif
 
@@ -296,9 +297,10 @@
 
 }  // namespace
 
-#if defined(USE_AURA)
+#if defined(USE_AURA) && !defined(OS_CHROMEOS)
 // static
 wm::WMState* Shell::wm_state_ = nullptr;
+display::Screen* Shell::screen_ = nullptr;
 #endif
 // static
 views::ViewsDelegate* Shell::views_delegate_ = nullptr;
@@ -309,8 +311,11 @@
   _setmode(_fileno(stdout), _O_BINARY);
   _setmode(_fileno(stderr), _O_BINARY);
 #endif
+#if defined(USE_AURA) && !defined(OS_CHROMEOS)
   wm_state_ = new wm::WMState;
-  views::InstallDesktopScreenIfNecessary();
+  CHECK(!display::Screen::GetScreen());
+  screen_ = views::CreateDesktopScreen().release();
+#endif
   views_delegate_ = new views::DesktopTestViewsDelegate();
 }
 
@@ -320,6 +325,8 @@
   // delete platform_;
   // platform_ = nullptr;
 #if defined(USE_AURA)
+  delete screen_;
+  screen_ = nullptr;
   delete wm_state_;
   wm_state_ = nullptr;
 #endif