diff --git a/AUTHORS b/AUTHORS
index d2518879..f373ec9e 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -188,6 +188,7 @@
 Chang Shu <c.shu@samsung.com>
 Changbin Shao <changbin.shao@intel.com>
 Changjun Yang <changjun.yang@intel.com>
+ChangSeok Lee <charlie.lee921@gmail.com>
 ChangSeok Oh <shivamidow@gmail.com>
 Changwan Hong <changwan.hong@navercorp.com>
 Changyeon Kim <cyzero.kim@samsung.com>
diff --git a/DEPS b/DEPS
index c0fece2..9b88f31f 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': '91216673f7266acc9ee57cdb6c2106452a6fe0f0',
+  'skia_revision': 'ffeef16664ea6788023526de0705fa451881c407',
   # 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': '90c77f74325dfa5fba11689d6092ae940b412553',
+  'v8_revision': 'ee9390c296d74c86241568f06d7eaaaada16d643',
   # 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': 'dcfd55262c986478eeb04cc3edf2effc65c69044',
+  'angle_revision': '7444466cf7d22ce1c174931159839d422c76b917',
   # 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': '270ff52f62ecf6b38744fa5f6b95808dd7dc49d9',
+  'freetype_revision': 'f631542dae1aaf9101135ed660cd3ff1d08ed93c',
   # 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': 'c5554ea0ab72873126d06f36d1bc19b23a0086a5',
+  'catapult_revision': '70ce64f4639423f995fb9172f25b68d546634bd4',
   # 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': '300f2b3d7b972245dd3e92e6ce9f75027863d63b',
+  'devtools_frontend_revision': '11eafd8825130a221102f537c53cb1ce6f44b3e5',
   # 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.
@@ -328,7 +328,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
-  'dawn_revision': '1de1371bc53b62c4d8a56e1533bcddff4f42ffc8',
+  'dawn_revision': '963991a1074ca7ee86485937545165efaf1563c5',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
@@ -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' + '@' + '0ebeb0bbba816727d9d1c104dfdaad1498e04596',
+      'url': Var('chromium_git') + '/chromiumos/chromite.git' + '@' + 'f54a40a99443e76a1557cd134554904b65cd664e',
       'condition': 'checkout_chromeos',
   },
 
@@ -966,7 +966,7 @@
   },
 
   'src/third_party/depot_tools':
-    Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + '5ef4697af266685400317d4af1c585a346cf23de',
+    Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + '6234dd231719d51d565ca0d846cfaac6c72a2499',
 
   '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' + '@' + '14c4d4957d5e2cb7c6fd4d6b8a3f51318c1b22da',
+    Var('android_git') + '/platform/external/perfetto.git' + '@' + 'ee1e7359d1d243b3f4059a659c2bb0684c2b3fd0',
 
   'src/third_party/perl': {
       'url': Var('chromium_git') + '/chromium/deps/perl.git' + '@' + '6f3e5028eb65d0b4c5fdd792106ac4c84eee1eb3',
@@ -1549,10 +1549,10 @@
     Var('chromium_git') + '/external/khronosgroup/webgl.git' + '@' + 'a0b8774ce8cec1dc8f4308810bf05eb8867c62de',
 
   'src/third_party/webgpu-cts/src':
-    Var('chromium_git') + '/external/github.com/gpuweb/cts.git' + '@' + '9dd7f1e157c609c8056f7f8b9c04e627611ef937',
+    Var('chromium_git') + '/external/github.com/gpuweb/cts.git' + '@' + '267e0b71649664a27d79f23773f0bde9e0e1164f',
 
   'src/third_party/webrtc':
-    Var('webrtc_git') + '/src.git' + '@' + '8d190d3ce8dadc8c35db74b4968ed2a788341bc3',
+    Var('webrtc_git') + '/src.git' + '@' + '2a605b13339db11a2e9052c35c41f899a53b098a',
 
   'src/third_party/libgifcodec':
      Var('skia_git') + '/libgifcodec' + '@'+  Var('libgifcodec_revision'),
@@ -1613,7 +1613,7 @@
     Var('chromium_git') + '/v8/v8.git' + '@' +  Var('v8_revision'),
 
   'src-internal': {
-    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@9172c17a540aeb670dc9f04a1111e8c24f805d8d',
+    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@c6bebbe6eaada749593f277d91df7281de7fe04a',
     'condition': 'checkout_src_internal',
   },
 
@@ -1632,7 +1632,7 @@
     'packages': [
       {
         'package': 'chromeos_internal/apps/help_app/app',
-        'version': 'guDtOxCcCWk0mOQG5WjKF6zBwJGPvT2ks3pw-06sGSMC',
+        'version': 'Andzyub-HHPGRPXyhIl0D4qKwBWkewnGH_R_JZYaI_sC',
       },
     ],
     'condition': 'checkout_chromeos and checkout_src_internal',
@@ -1643,7 +1643,7 @@
     'packages': [
       {
         'package': 'chromeos_internal/apps/media_app/app',
-        'version': '4jMTXapOwjspzJpdZ4oHRPsnAOhC2WukJWizXb-H-ZcC',
+        'version': 'BfEh4fh0X7pglGonHNEFqt0auxRO02-ZOVCIeu2vcn8C',
       },
     ],
     'condition': 'checkout_chromeos and checkout_src_internal',
diff --git a/ash/constants/ash_features.cc b/ash/constants/ash_features.cc
index 453465d6..532cd0b 100644
--- a/ash/constants/ash_features.cc
+++ b/ash/constants/ash_features.cc
@@ -235,6 +235,9 @@
 const base::Feature kCryptAuthV2Enrollment{"CryptAuthV2Enrollment",
                                            base::FEATURE_ENABLED_BY_DEFAULT};
 
+const base::Feature kDemoModeSWA{"DemoModeSWA",
+                                 base::FEATURE_DISABLED_BY_DEFAULT};
+
 // Enables or disables the Diagnostics app.
 const base::Feature kDiagnosticsApp{"DiagnosticsApp",
                                     base::FEATURE_ENABLED_BY_DEFAULT};
@@ -472,6 +475,10 @@
 const base::Feature kLanguageSettingsUpdate2{"LanguageSettingsUpdate2",
                                              base::FEATURE_DISABLED_BY_DEFAULT};
 
+// Enables or disables device management disclosure on login / lock screen.
+const base::Feature kLoginDeviceManagementDisclosure{
+    "LoginDeviceManagementDisclosure", base::FEATURE_ENABLED_BY_DEFAULT};
+
 // Controls whether to enable the requirement of a minimum chrome version on the
 // device through the policy DeviceMinimumVersion. If the requirement is
 // not met and the warning time in the policy has expired, the user is
@@ -779,6 +786,10 @@
   return base::FeatureList::IsEnabled(kUpdatedCellularActivationUi);
 }
 
+bool IsDemoModeSWAEnabled() {
+  return base::FeatureList::IsEnabled(kDemoModeSWA);
+}
+
 bool IsDeepLinkingEnabled() {
   return base::FeatureList::IsEnabled(kOsSettingsDeepLinking);
 }
@@ -820,6 +831,10 @@
   return base::FeatureList::IsEnabled(kKerberosSettingsSection);
 }
 
+bool IsLoginDeviceManagementDisclosureEnabled() {
+  return base::FeatureList::IsEnabled(kLoginDeviceManagementDisclosure);
+}
+
 bool IsMinimumChromeVersionEnabled() {
   return base::FeatureList::IsEnabled(kMinimumChromeVersion);
 }
diff --git a/ash/constants/ash_features.h b/ash/constants/ash_features.h
index c6733bf..37205c6c 100644
--- a/ash/constants/ash_features.h
+++ b/ash/constants/ash_features.h
@@ -119,6 +119,7 @@
 extern const base::Feature kCryptAuthV2DeviceSync;
 COMPONENT_EXPORT(ASH_CONSTANTS)
 extern const base::Feature kCryptAuthV2Enrollment;
+COMPONENT_EXPORT(ASH_CONSTANTS) extern const base::Feature kDemoModeSWA;
 COMPONENT_EXPORT(ASH_CONSTANTS)
 extern const base::Feature kDisableOfficeEditingComponentApp;
 COMPONENT_EXPORT(ASH_CONSTANTS) extern const base::Feature kDriveFs;
@@ -208,6 +209,8 @@
 COMPONENT_EXPORT(ASH_CONSTANTS)
 extern const base::Feature kLanguageSettingsUpdate2;
 COMPONENT_EXPORT(ASH_CONSTANTS)
+extern const base::Feature kLoginDeviceManagementDisclosure;
+COMPONENT_EXPORT(ASH_CONSTANTS)
 extern const base::Feature kMediaAppAnnotation;
 COMPONENT_EXPORT(ASH_CONSTANTS)
 extern const base::Feature kMediaAppDisplayExif;
@@ -349,6 +352,7 @@
 COMPONENT_EXPORT(ASH_CONSTANTS) bool IsClipboardHistoryEnabled();
 COMPONENT_EXPORT(ASH_CONSTANTS)
 bool IsClipboardHistoryNudgeSessionResetEnabled();
+COMPONENT_EXPORT(ASH_CONSTANTS) bool IsDemoModeSWAEnabled();
 COMPONENT_EXPORT(ASH_CONSTANTS) bool IsDeepLinkingEnabled();
 COMPONENT_EXPORT(ASH_CONSTANTS) bool IsDiagnosticsAppEnabled();
 COMPONENT_EXPORT(ASH_CONSTANTS) bool IsEcheSWAEnabled();
@@ -360,6 +364,7 @@
 COMPONENT_EXPORT(ASH_CONSTANTS)
 bool IsInstantTetheringBackgroundAdvertisingSupported();
 COMPONENT_EXPORT(ASH_CONSTANTS) bool IsKerberosSettingsSectionEnabled();
+COMPONENT_EXPORT(ASH_CONSTANTS) bool IsLoginDeviceManagementDisclosureEnabled();
 COMPONENT_EXPORT(ASH_CONSTANTS) bool IsMinimumChromeVersionEnabled();
 COMPONENT_EXPORT(ASH_CONSTANTS) bool IsNetworkingInDiagnosticsAppEnabled();
 COMPONENT_EXPORT(ASH_CONSTANTS) bool IsNewOobeLayoutEnabled();
diff --git a/ash/fast_ink/fast_ink_host.cc b/ash/fast_ink/fast_ink_host.cc
index 02d7d34b..f16d438 100644
--- a/ash/fast_ink/fast_ink_host.cc
+++ b/ash/fast_ink/fast_ink_host.cc
@@ -404,14 +404,14 @@
 
   viz::SharedQuadState* quad_state =
       render_pass->CreateAndAppendSharedQuadState();
-  quad_state->SetAll(
-      buffer_to_target_transform,
-      /*quad_layer_rect=*/output_rect,
-      /*visible_quad_layer_rect=*/output_rect,
-      /*mask_filter_info=*/gfx::MaskFilterInfo(),
-      /*clip_rect=*/gfx::Rect(),
-      /*is_clipped=*/false, /*are_contents_opaque=*/false, /*opacity=*/1.f,
-      /*blend_mode=*/SkBlendMode::kSrcOver, /*sorting_context_id=*/0);
+  quad_state->SetAll(buffer_to_target_transform,
+                     /*quad_layer_rect=*/output_rect,
+                     /*visible_layer_rect=*/output_rect,
+                     /*mask_filter_info=*/gfx::MaskFilterInfo(),
+                     /*clip_rect=*/base::nullopt, /*are_contents_opaque=*/false,
+                     /*opacity=*/1.f,
+                     /*blend_mode=*/SkBlendMode::kSrcOver,
+                     /*sorting_context_id=*/0);
 
   viz::CompositorFrame frame;
   // TODO(eseckler): FastInkHost should use BeginFrames and set the ack
diff --git a/ash/fast_ink/view_tree_host_root_view.cc b/ash/fast_ink/view_tree_host_root_view.cc
index da5cfed..b4955adc 100644
--- a/ash/fast_ink/view_tree_host_root_view.cc
+++ b/ash/fast_ink/view_tree_host_root_view.cc
@@ -456,14 +456,14 @@
 
   viz::SharedQuadState* quad_state =
       render_pass->CreateAndAppendSharedQuadState();
-  quad_state->SetAll(
-      buffer_to_target_transform,
-      /*quad_layer_rect=*/output_rect,
-      /*visible_quad_layer_rect=*/output_rect,
-      /*mask_filter_info=*/gfx::MaskFilterInfo(),
-      /*clip_rect=*/gfx::Rect(),
-      /*is_clipped=*/false, /*are_contents_opaque=*/false, /*opacity=*/1.f,
-      /*blend_mode=*/SkBlendMode::kSrcOver, /*sorting_context_id=*/0);
+  quad_state->SetAll(buffer_to_target_transform,
+                     /*quad_layer_rect=*/output_rect,
+                     /*visible_layer_rect=*/output_rect,
+                     /*mask_filter_info=*/gfx::MaskFilterInfo(),
+                     /*clip_rect=*/base::nullopt, /*are_contents_opaque=*/false,
+                     /*opacity=*/1.f,
+                     /*blend_mode=*/SkBlendMode::kSrcOver,
+                     /*sorting_context_id=*/0);
 
   viz::CompositorFrame frame;
   // TODO(eseckler): ViewTreeHostRootView should use BeginFrames and set
diff --git a/ash/login/ui/lock_contents_view.cc b/ash/login/ui/lock_contents_view.cc
index b9f7b0b0..d0a7ff2 100644
--- a/ash/login/ui/lock_contents_view.cc
+++ b/ash/login/ui/lock_contents_view.cc
@@ -774,6 +774,8 @@
 
 void LockContentsView::ShowEnterpriseDomainManager(
     const std::string& entreprise_domain_manager) {
+  if (!chromeos::features::IsLoginDeviceManagementDisclosureEnabled())
+    return;
   bottom_status_indicator_->SetText(l10n_util::GetStringFUTF16(
       IDS_ASH_LOGIN_MANAGED_DEVICE_INDICATOR, ui::GetChromeOSDeviceName(),
       base::UTF8ToUTF16(entreprise_domain_manager)));
diff --git a/ash/login/ui/lock_contents_view_unittest.cc b/ash/login/ui/lock_contents_view_unittest.cc
index d1a4d14d..0a70f03 100644
--- a/ash/login/ui/lock_contents_view_unittest.cc
+++ b/ash/login/ui/lock_contents_view_unittest.cc
@@ -820,8 +820,22 @@
   EXPECT_TRUE(test_api.bottom_status_indicator()->GetVisible());
 }
 
+class LockContentsViewUnitTestWithDeviceDisclosureEnabled
+    : public LockContentsViewUnitTest {
+ public:
+  LockContentsViewUnitTestWithDeviceDisclosureEnabled()
+      : LockContentsViewUnitTest() {
+    feature_list_.InitWithFeatures(
+        {chromeos::features::kLoginDeviceManagementDisclosure}, {});
+  }
+
+ private:
+  base::test::ScopedFeatureList feature_list_;
+};
+
 // Show bottom status indicator if device is enrolled
-TEST_F(LockContentsViewUnitTest, ShowStatusIndicatorIfEnrolledDevice) {
+TEST_F(LockContentsViewUnitTestWithDeviceDisclosureEnabled,
+       ShowStatusIndicatorIfEnrolledDevice) {
   // If the device is enrolled, bottom_status_indicator should be visible.
   Shell::Get()->system_tray_model()->SetEnterpriseDomainInfo("BestCompanyEver",
                                                              false);
@@ -846,7 +860,8 @@
 }
 
 // Show bottom status indicator if device is enrolled
-TEST_F(LockContentsViewUnitTest, ShowManagementBubbleOnClickIfEnrolledDevice) {
+TEST_F(LockContentsViewUnitTestWithDeviceDisclosureEnabled,
+       ShowManagementBubbleOnClickIfEnrolledDevice) {
   // If the device is enrolled, bottom_status_indicator should be visible.
   Shell::Get()->system_tray_model()->SetEnterpriseDomainInfo("BestCompanyEver",
                                                              false);
@@ -881,7 +896,8 @@
 
 // Do not show the management bubble on click if ADB sideloading is enabled and
 // device is enrolled.
-TEST_F(LockContentsViewUnitTest, DoNotShowManagementBubbleOnClickIfAdb) {
+TEST_F(LockContentsViewUnitTestWithDeviceDisclosureEnabled,
+       DoNotShowManagementBubbleOnClickIfAdb) {
   // If the device is enrolled, bottom_status_indicator should be visible.
   Shell::Get()->system_tray_model()->SetEnterpriseDomainInfo("BestCompanyEver",
                                                              false);
diff --git a/ash/metrics/demo_session_metrics_recorder.cc b/ash/metrics/demo_session_metrics_recorder.cc
index f4675ec..cb2b731 100644
--- a/ash/metrics/demo_session_metrics_recorder.cc
+++ b/ash/metrics/demo_session_metrics_recorder.cc
@@ -71,8 +71,14 @@
     return DemoModeApp::kCalendar;
   if (app_id == extension_misc::kGoogleDocsDemoAppId)
     return DemoModeApp::kGoogleDocsChromeApp;
+  if (app_id == extension_misc::kGoogleDocsPwaAppId)
+    return DemoModeApp::kGoogleDocsPwa;
+  if (app_id == extension_misc::kGoogleMeetPwaAppId)
+    return DemoModeApp::kGoogleMeetPwa;
   if (app_id == extension_misc::kGoogleSheetsDemoAppId)
     return DemoModeApp::kGoogleSheetsChromeApp;
+  if (app_id == extension_misc::kGoogleSheetsPwaAppId)
+    return DemoModeApp::kGoogleSheetsPwa;
   if (app_id == extension_misc::kGoogleSlidesDemoAppId)
     return DemoModeApp::kGoogleSlidesChromeApp;
   if (app_id == kHelpAppId)
@@ -81,10 +87,18 @@
     return DemoModeApp::kGoogleKeepChromeApp;
   if (app_id == extensions::kWebStoreAppId)
     return DemoModeApp::kWebStore;
-  if (app_id == extension_misc::kYoutubeAppId ||
-      app_id == extension_misc::kYoutubePwaAppId) {
+  if (app_id == extension_misc::kYoutubeAppId)
     return DemoModeApp::kYouTube;
-  }
+  if (app_id == extension_misc::kYoutubePwaAppId)
+    return DemoModeApp::kYoutubePwa;
+  if (app_id == extension_misc::kSpotifyAppId)
+    return DemoModeApp::kSpotify;
+  if (app_id == extension_misc::kBeFunkyAppId)
+    return DemoModeApp::kBeFunky;
+  if (app_id == extension_misc::kClipchampAppId)
+    return DemoModeApp::kClipchamp;
+  if (app_id == extension_misc::kGeForceNowAppId)
+    return DemoModeApp::kGeForceNow;
 
   return DemoModeApp::kOtherChromeApp;
 }
diff --git a/ash/metrics/demo_session_metrics_recorder.h b/ash/metrics/demo_session_metrics_recorder.h
index 0c6b6a3e..1962e0ef 100644
--- a/ash/metrics/demo_session_metrics_recorder.h
+++ b/ash/metrics/demo_session_metrics_recorder.h
@@ -69,9 +69,17 @@
     kGoogleDocsChromeApp = 28,
     kGoogleSheetsChromeApp = 29,
     kGoogleSlidesChromeApp = 30,
+    kYoutubePwa = 31,
+    kGoogleDocsPwa = 32,
+    kGoogleMeetPwa = 33,
+    kGoogleSheetsPwa = 34,
+    kSpotify = 35,
+    kBeFunky = 36,
+    kClipchamp = 37,
+    kGeForceNow = 38,
     // Add future entries above this comment, in sync with enums.xml.
     // Update kMaxValue to the last value.
-    kMaxValue = kGoogleSlidesChromeApp,
+    kMaxValue = kGeForceNow,
   };
 
   // The recorder will create a normal timer by default. Tests should provide a
diff --git a/ash/shortcut_viewer/shortcut_viewer_strings.grd b/ash/shortcut_viewer/shortcut_viewer_strings.grd
index dc5ab0a..19c6941 100644
--- a/ash/shortcut_viewer/shortcut_viewer_strings.grd
+++ b/ash/shortcut_viewer/shortcut_viewer_strings.grd
@@ -462,7 +462,7 @@
     Zoom in on the page
   </message>
   <message name="IDS_KSV_DESCRIPTION_IDC_NEW_INCOGNITO_WINDOW" desc="Description of the command in keyboard shortcut viewer.">
-    Open a new window in incognito mode
+    Open a new window in Incognito mode
   </message>
   <message name="IDS_KSV_DESCRIPTION_IDC_RESTORE_TAB" desc="Description of the command in keyboard shortcut viewer.">
     Reopen the last tab or window you closed
diff --git a/base/task/common/task_annotator.cc b/base/task/common/task_annotator.cc
index 46e414a..63ec2ab 100644
--- a/base/task/common/task_annotator.cc
+++ b/base/task/common/task_annotator.cc
@@ -126,8 +126,13 @@
               "TaskAnnotator::RunTask", [&](perfetto::EventContext ctx) {
                 auto* event =
                     ctx.event<perfetto::protos::pbzero::ChromeTrackEvent>();
-                event->set_chrome_task_annotator()->set_ipc_hash(
-                    pending_task->ipc_hash);
+                auto* annotator = event->set_chrome_task_annotator();
+                annotator->set_ipc_hash(pending_task->ipc_hash);
+                if (!pending_task->delayed_run_time.is_null()) {
+                  annotator->set_task_delay_us((pending_task->delayed_run_time -
+                                                pending_task->queue_time)
+                                                   .InMicroseconds());
+                }
               });
 
   TRACE_EVENT_WITH_FLOW0("toplevel.flow", trace_event_name,
diff --git a/base/trace_event/builtin_categories.h b/base/trace_event/builtin_categories.h
index 818f8b1..90989e5 100644
--- a/base/trace_event/builtin_categories.h
+++ b/base/trace_event/builtin_categories.h
@@ -31,6 +31,7 @@
   X("accessibility")                                                     \
   X("AccountFetcherService")                                             \
   X("android_webview")                                                   \
+  /* Actions on Google Hardware, used in Google-internal code. */        \
   X("aogh")                                                              \
   X("audio")                                                             \
   X("base")                                                              \
@@ -149,7 +150,6 @@
   X("stadia_rtc")                                                        \
   X("startup")                                                           \
   X("sync")                                                              \
-  X("sync_lock_contention")                                              \
   X("test_gpu")                                                          \
   X("thread_pool")                                                       \
   X("toplevel")                                                          \
diff --git a/base/tracing/protos/chrome_track_event.proto b/base/tracing/protos/chrome_track_event.proto
index 813b174..a46f239 100644
--- a/base/tracing/protos/chrome_track_event.proto
+++ b/base/tracing/protos/chrome_track_event.proto
@@ -28,6 +28,9 @@
 
 message ChromeTaskAnnotator {
   optional uint32 ipc_hash = 1;
+  // The delay in microseconds that was specified, if any, when this task was
+  // posted. This is only valid for delayed tasks.
+  optional uint64 task_delay_us = 2;
 }
 
 message ChromeBrowserContext {
diff --git a/build/android/docs/java_optimization.md b/build/android/docs/java_optimization.md
index ec3df1df..0ba0d503 100644
--- a/build/android/docs/java_optimization.md
+++ b/build/android/docs/java_optimization.md
@@ -1,27 +1,16 @@
 # Optimizing Java Code
 
 This doc describes how Java code is optimized in Chrome on Android and how to
-deal with issues caused by the optimizer.
+deal with issues caused by the optimizer. For tips on how to write optimized
+code, see [//docs/speed/binary_size/optimization_advice.md#optimizing-java-code](/docs/speed/binary_size/optimization_advice.md#optimizing-java-code).
 
 [TOC]
 
-## History
+## ProGuard vs R8
 
-When Java code optimization was first added to Chrome the tool used was called
-[ProGuard](https://www.guardsquare.com/en/products/proguard). This was used
-in public builds until [January 3, 2019](http://crrev.com/c/1394387).
-
-On June 20, 2016, Chrome switched to using an internal fork of ProGuard for
-downstream builds because it offered better optimizations for binary size and
-method count.
-
-As of [July 20, 2019](https://crrev.com/c/1689877), all Chrome builds have
-switched to using [R8](https://r8.googlesource.com/r8), the new tool provided by
-Android Studio. R8 provides significant improvements to binary size and method
-count over both public and internal ProGuard. R8 uses the same configuration
-specification language as ProGuard and supports many of the same rules that
-ProGuard did.
-
+ProGuard is the original open-source tool used by many Android applications to
+perform whole-program bytecode optimization. [R8](https://r8.googlesource.com/r8),
+is a re-implementation that is used by Chrome (and the default for Android Studio).
 The terms "ProGuard" and "R8" are used interchangeably within Chromium but
 generally they're meant to refer to the tool providing Java code optimizations.
 
@@ -37,14 +26,11 @@
    further through various approaches (ex. inlining, outlining, class merging,
    etc).
 
-Chrome relies on ProGuard for keeping Java code size manageable. As of November
-2019, a debug build of Chrome has about 3.5x the amount of dex size of a
-release build and has 5 `.dex` files (vs. 1 in release).
-
 ## Build Process
 
-ProGuard is only enabled for release builds of Chrome because it is a slow build
-step. It can also be enabled manually via the GN arg `is_java_debug = false`.
+ProGuard is enabled only for release builds of Chrome because it is a slow build
+step and breaks Java debugging. It can also be enabled manually via the GN arg:
+```is_java_debug = false```
 
 ### ProGuard configuration files
 
@@ -59,18 +45,6 @@
 as input a list of `.jar` files, runs R8/ProGuard on those `.jar` files, and
 produces the final `.dex` file(s) that will be packaged into your `.apk`
 
-### Synchronized ProGuard
-
-Some additional steps are required for optimizing code that is shared between
-multiple application components (App Bundles and Trichrome). Because ProGuard is
-a whole program optimizer, it needs to know about *ALL* code used by the
-application or most optimizations won't work as expected.
-
-For synchronized ProGuard, the `.jar` files depended on by all application
-components are given to ProGuard to produce a single output. This is then split
-with an additional `dexsplitter` step to produce separate `.dex` files for each
-dependent application component.
-
 ## Deobfuscation
 
 Obfuscation can be turned off for local builds while leaving ProGuard enabled
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 0cf5705eb..57c55da 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
@@ -1125,7 +1125,6 @@
         # Additionally, add the ignore if we're running on a trybot and this is
         # not our final retry attempt in order to prevent unrelated CLs from
         # getting spammed if a test is flaky.
-        optional_keys = {}
         should_rewrite = False
         with open(json_path) as infile:
           # All the key/value pairs in the JSON file are strings, so convert
@@ -1142,9 +1141,7 @@
           if 'full_test_name' in json_dict:
             should_rewrite = True
             del json_dict['full_test_name']
-        if should_rewrite:
-          with open(json_path, 'w') as outfile:
-            json.dump(json_dict, outfile)
+
         running_on_unsupported = (
             device.build_version_sdk not in RENDER_TEST_MODEL_SDK_CONFIGS.get(
                 device.product_model, []) and not fail_on_unsupported)
@@ -1162,7 +1159,11 @@
         # should_ignore_in_gold != should_hide_failure.
         should_hide_failure = running_on_unsupported
         if should_ignore_in_gold:
-          optional_keys['ignore'] = '1'
+          should_rewrite = True
+          json_dict['ignore'] = '1'
+        if should_rewrite:
+          with open(json_path, 'w') as outfile:
+            json.dump(json_dict, outfile)
 
         gold_session = self._skia_gold_session_manager.GetSkiaGoldSession(
             keys_input=json_path)
@@ -1172,8 +1173,7 @@
               name=render_name,
               png_file=image_path,
               output_manager=self._env.output_manager,
-              use_luci=use_luci,
-              optional_keys=optional_keys)
+              use_luci=use_luci)
         except Exception as e:  # pylint: disable=broad-except
           _FailTestIfNecessary(results, full_test_name)
           _AppendToLog(results, full_test_name,
diff --git a/cc/layers/video_layer_impl.cc b/cc/layers/video_layer_impl.cc
index 658fbbdd..c6649455 100644
--- a/cc/layers/video_layer_impl.cc
+++ b/cc/layers/video_layer_impl.cc
@@ -162,10 +162,14 @@
   if (visible_quad_rect.IsEmpty())
     return;
 
+  base::Optional<gfx::Rect> clip_rect_opt;
+  if (is_clipped()) {
+    clip_rect_opt = clip_rect();
+  }
   updater_->AppendQuads(render_pass, frame_, transform, quad_rect,
                         visible_quad_rect, draw_properties().mask_filter_info,
-                        clip_rect(), is_clipped(), contents_opaque(),
-                        draw_opacity(), GetSortingContextId());
+                        clip_rect_opt, contents_opaque(), draw_opacity(),
+                        GetSortingContextId());
 }
 
 void VideoLayerImpl::DidDraw(viz::ClientResourceProvider* resource_provider) {
diff --git a/cc/paint/paint_op_buffer_serializer.cc b/cc/paint/paint_op_buffer_serializer.cc
index 79750de..12e8f8af1 100644
--- a/cc/paint/paint_op_buffer_serializer.cc
+++ b/cc/paint/paint_op_buffer_serializer.cc
@@ -33,11 +33,11 @@
   const int kMaxExtent = std::numeric_limits<int>::max() >> 1;
 
   return options.strike_server
-             ? std::make_unique<SkTextBlobCacheDiffCanvas>(
+             ? options.strike_server->makeAnalysisCanvas(
                    kMaxExtent, kMaxExtent,
                    skia::LegacyDisplayGlobals::ComputeSurfaceProps(
                        options.can_use_lcd_text),
-                   options.strike_server, options.color_space,
+                   options.color_space,
                    options.context_supports_distance_field_text)
              : std::make_unique<SkNoDrawCanvas>(kMaxExtent, kMaxExtent);
 }
diff --git a/cc/test/render_pass_test_utils.cc b/cc/test/render_pass_test_utils.cc
index bb46f0f..6ab178d 100644
--- a/cc/test/render_pass_test_utils.cc
+++ b/cc/test/render_pass_test_utils.cc
@@ -113,7 +113,7 @@
                                         SkColor color) {
   viz::SharedQuadState* shared_state = pass->CreateAndAppendSharedQuadState();
   shared_state->SetAll(gfx::Transform(), rect, rect, gfx::MaskFilterInfo(),
-                       rect, true, false, 1, SkBlendMode::kSrcOver, 0);
+                       rect, false, 1, SkBlendMode::kSrcOver, 0);
   auto* quad = pass->CreateAndAppendDrawQuad<viz::SolidColorDrawQuad>();
   quad->SetNew(shared_state, rect, rect, color, false);
   return quad;
@@ -124,8 +124,8 @@
                                             SkColor color,
                                             const gfx::Transform& transform) {
   viz::SharedQuadState* shared_state = pass->CreateAndAppendSharedQuadState();
-  shared_state->SetAll(transform, rect, rect, gfx::MaskFilterInfo(), rect,
-                       false, false, 1,
+  shared_state->SetAll(transform, rect, rect, gfx::MaskFilterInfo(),
+                       base::nullopt, false, 1,
 
                        SkBlendMode::kSrcOver, 0);
   auto* quad = pass->CreateAndAppendDrawQuad<viz::SolidColorDrawQuad>();
@@ -140,7 +140,7 @@
   viz::SharedQuadState* shared_state =
       to_pass->CreateAndAppendSharedQuadState();
   shared_state->SetAll(gfx::Transform(), output_rect, output_rect,
-                       gfx::MaskFilterInfo(), output_rect, false, false, 1,
+                       gfx::MaskFilterInfo(), base::nullopt, false, 1,
                        SkBlendMode::kSrcOver, 0);
   auto* quad = to_pass->template CreateAndAppendDrawQuad<QuadType>();
   quad->SetNew(shared_state, output_rect, output_rect, contributing_pass->id,
@@ -171,7 +171,7 @@
   viz::SharedQuadState* shared_state =
       to_pass->CreateAndAppendSharedQuadState();
   shared_state->SetAll(transform, output_rect, output_rect,
-                       gfx::MaskFilterInfo(), output_rect, false, false, 1,
+                       gfx::MaskFilterInfo(), base::nullopt, false, 1,
                        blend_mode, 0);
   auto* quad =
       to_pass->CreateAndAppendDrawQuad<viz::AggregatedRenderPassDrawQuad>();
@@ -219,7 +219,7 @@
   viz::SharedQuadState* shared_state =
       to_pass->CreateAndAppendSharedQuadState();
   shared_state->SetAll(gfx::Transform(), rect, rect, gfx::MaskFilterInfo(),
-                       rect, false, false, 1, SkBlendMode::kSrcOver, 0);
+                       base::nullopt, false, 1, SkBlendMode::kSrcOver, 0);
 
   auto* debug_border_quad =
       to_pass->CreateAndAppendDrawQuad<viz::DebugBorderDrawQuad>();
@@ -280,7 +280,7 @@
   viz::SharedQuadState* shared_state2 =
       to_pass->CreateAndAppendSharedQuadState();
   shared_state->SetAll(gfx::Transform(), rect, rect, gfx::MaskFilterInfo(),
-                       rect, false, false, 1, SkBlendMode::kSrcOver, 0);
+                       base::nullopt, false, 1, SkBlendMode::kSrcOver, 0);
 
   auto* tile_quad = to_pass->CreateAndAppendDrawQuad<viz::TileDrawQuad>();
   tile_quad->SetNew(shared_state2, rect, visible_rect, needs_blending,
@@ -396,7 +396,7 @@
   viz::SharedQuadState* shared_state =
       to_pass->CreateAndAppendSharedQuadState();
   shared_state->SetAll(gfx::Transform(), rect, rect, gfx::MaskFilterInfo(),
-                       rect, false, false, 1, SkBlendMode::kSrcOver, 0);
+                       base::nullopt, false, 1, SkBlendMode::kSrcOver, 0);
 
   viz::DebugBorderDrawQuad* debug_border_quad =
       to_pass->CreateAndAppendDrawQuad<viz::DebugBorderDrawQuad>();
@@ -457,7 +457,7 @@
   viz::SharedQuadState* shared_state2 =
       to_pass->CreateAndAppendSharedQuadState();
   shared_state2->SetAll(gfx::Transform(), rect, rect, gfx::MaskFilterInfo(),
-                        rect, false, false, 1, SkBlendMode::kSrcOver, 0);
+                        base::nullopt, false, 1, SkBlendMode::kSrcOver, 0);
 
   viz::TileDrawQuad* tile_quad =
       to_pass->CreateAndAppendDrawQuad<viz::TileDrawQuad>();
diff --git a/cc/test/render_pass_test_utils.h b/cc/test/render_pass_test_utils.h
index 0c4b5001..846ce3b 100644
--- a/cc/test/render_pass_test_utils.h
+++ b/cc/test/render_pass_test_utils.h
@@ -73,7 +73,7 @@
                                         SkColor color) {
   viz::SharedQuadState* shared_state = pass->CreateAndAppendSharedQuadState();
   shared_state->SetAll(gfx::Transform(), rect, rect, gfx::MaskFilterInfo(),
-                       rect, false, false, 1, SkBlendMode::kSrcOver, 0);
+                       base::nullopt, false, 1, SkBlendMode::kSrcOver, 0);
   auto* quad =
       pass->template CreateAndAppendDrawQuad<viz::SolidColorDrawQuad>();
   quad->SetNew(shared_state, rect, rect, color, false);
diff --git a/chrome/android/BUILD.gn b/chrome/android/BUILD.gn
index 7239c9e4..cadaf26 100644
--- a/chrome/android/BUILD.gn
+++ b/chrome/android/BUILD.gn
@@ -308,6 +308,8 @@
     "//chrome/browser/android/crypto:java",
     "//chrome/browser/android/lifecycle:java",
     "//chrome/browser/android/webapps/launchpad:java",
+    "//chrome/browser/attribution_reporting/android:factory_java",
+    "//chrome/browser/attribution_reporting/android:java",
     "//chrome/browser/banners/android:java",
     "//chrome/browser/browser_controls/android:java",
     "//chrome/browser/commerce/merchant_viewer/android:java",
@@ -686,6 +688,7 @@
   deps = [
     ":chrome_java",
     "//chrome/android/features/keyboard_accessory:internal_java",
+    "//chrome/browser/attribution_reporting/android/internal:java",
     "//chrome/browser/commerce/merchant_viewer/android:java",
     "//chrome/browser/commerce/subscriptions/android:java",
     "//chrome/browser/download/internal/android:java",
@@ -1153,6 +1156,7 @@
     "//chrome/browser/android/metrics:ukm_javatests",
     "//chrome/browser/android/webapps/launchpad:java",
     "//chrome/browser/android/webapps/launchpad:javatests",
+    "//chrome/browser/attribution_reporting/android:java",
     "//chrome/browser/banners/android:java",
     "//chrome/browser/browser_controls/android:java",
     "//chrome/browser/commerce/merchant_viewer/android:javatests",
@@ -3554,7 +3558,6 @@
     "java/src/org/chromium/chrome/browser/password_manager/AccountChooserDialog.java",
     "java/src/org/chromium/chrome/browser/password_manager/AutoSigninFirstRunDialog.java",
     "java/src/org/chromium/chrome/browser/password_manager/AutoSigninSnackbarController.java",
-    "java/src/org/chromium/chrome/browser/password_manager/BiometricAuthenticatorBridge.java",
     "java/src/org/chromium/chrome/browser/password_manager/Credential.java",
     "java/src/org/chromium/chrome/browser/password_manager/CredentialLeakDialogBridge.java",
     "java/src/org/chromium/chrome/browser/password_manager/PasswordChangeLauncher.java",
diff --git a/chrome/android/chrome_java_sources.gni b/chrome/android/chrome_java_sources.gni
index 2cb3b5bf..032c71c 100644
--- a/chrome/android/chrome_java_sources.gni
+++ b/chrome/android/chrome_java_sources.gni
@@ -1041,7 +1041,6 @@
   "java/src/org/chromium/chrome/browser/password_manager/AccountChooserDialog.java",
   "java/src/org/chromium/chrome/browser/password_manager/AutoSigninFirstRunDialog.java",
   "java/src/org/chromium/chrome/browser/password_manager/AutoSigninSnackbarController.java",
-  "java/src/org/chromium/chrome/browser/password_manager/BiometricAuthenticatorBridge.java",
   "java/src/org/chromium/chrome/browser/password_manager/Credential.java",
   "java/src/org/chromium/chrome/browser/password_manager/CredentialLeakDialogBridge.java",
   "java/src/org/chromium/chrome/browser/password_manager/GooglePasswordManagerUIProvider.java",
diff --git a/chrome/android/chrome_test_java_sources.gni b/chrome/android/chrome_test_java_sources.gni
index 6b4dd14..6af4276 100644
--- a/chrome/android/chrome_test_java_sources.gni
+++ b/chrome/android/chrome_test_java_sources.gni
@@ -277,6 +277,7 @@
   "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,7 +494,7 @@
   "javatests/src/org/chromium/chrome/browser/query_tiles/QueryTileUtilsTest.java",
   "javatests/src/org/chromium/chrome/browser/query_tiles/TileMatchers.java",
   "javatests/src/org/chromium/chrome/browser/query_tiles/ViewActions.java",
-  "javatests/src/org/chromium/chrome/browser/read_later/ReadLaterIphTest.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/TemplateUrlServiceTest.java",
   "javatests/src/org/chromium/chrome/browser/search_engines/settings/SearchEngineSettingsTest.java",
diff --git a/chrome/android/expectations/monochrome_public_bundle.AndroidManifest.expected b/chrome/android/expectations/monochrome_public_bundle.AndroidManifest.expected
index 8eb698b..a6a5cf73 100644
--- a/chrome/android/expectations/monochrome_public_bundle.AndroidManifest.expected
+++ b/chrome/android/expectations/monochrome_public_bundle.AndroidManifest.expected
@@ -779,6 +779,10 @@
         <action android:name="android.speech.action.VOICE_SEARCH_RESULTS"/>
         <category android:name="android.intent.category.DEFAULT"/>
       </intent-filter>  # DIFF-ANCHOR: 2a3a3c3d
+      <intent-filter>  # DIFF-ANCHOR: 19f73fdc
+        <action android:name="android.web.action.APP_ATTRIBUTION"/>
+        <category android:name="android.intent.category.DEFAULT"/>
+      </intent-filter>  # DIFF-ANCHOR: 19f73fdc
       <intent-filter>  # DIFF-ANCHOR: 83919a44
         <action android:name="com.sec.android.airview.HOVER"/>
       </intent-filter>  # DIFF-ANCHOR: 83919a44
diff --git a/chrome/android/expectations/trichrome_chrome_bundle.AndroidManifest.expected b/chrome/android/expectations/trichrome_chrome_bundle.AndroidManifest.expected
index dcb6341..2f3d450 100644
--- a/chrome/android/expectations/trichrome_chrome_bundle.AndroidManifest.expected
+++ b/chrome/android/expectations/trichrome_chrome_bundle.AndroidManifest.expected
@@ -752,6 +752,10 @@
         <action android:name="android.speech.action.VOICE_SEARCH_RESULTS"/>
         <category android:name="android.intent.category.DEFAULT"/>
       </intent-filter>  # DIFF-ANCHOR: 2a3a3c3d
+      <intent-filter>  # DIFF-ANCHOR: 19f73fdc
+        <action android:name="android.web.action.APP_ATTRIBUTION"/>
+        <category android:name="android.intent.category.DEFAULT"/>
+      </intent-filter>  # DIFF-ANCHOR: 19f73fdc
       <intent-filter>  # DIFF-ANCHOR: 83919a44
         <action android:name="com.sec.android.airview.HOVER"/>
       </intent-filter>  # DIFF-ANCHOR: 83919a44
diff --git a/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedSurfaceMediator.java b/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedSurfaceMediator.java
index 4545801d..d96db5b 100644
--- a/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedSurfaceMediator.java
+++ b/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedSurfaceMediator.java
@@ -30,6 +30,7 @@
 import org.chromium.chrome.browser.feed.shared.FeedFeatures;
 import org.chromium.chrome.browser.feed.shared.stream.Stream;
 import org.chromium.chrome.browser.feed.shared.stream.Stream.ContentChangedListener;
+import org.chromium.chrome.browser.feed.webfeed.WebFeedBridge;
 import org.chromium.chrome.browser.flags.ChromeFeatureList;
 import org.chromium.chrome.browser.native_page.ContextMenuManager;
 import org.chromium.chrome.browser.native_page.NativePageNavigationDelegate;
@@ -420,7 +421,7 @@
             mSectionHeaderModel.set(
                     SectionHeaderListProperties.MENU_DELEGATE_KEY, this::onItemSelected);
 
-            if (FeedFeatures.isWebFeedUIEnabled()) {
+            if (WebFeedBridge.isWebFeedSubscriber() && FeedFeatures.isWebFeedUIEnabled()) {
                 addHeaderAndStream(mContext.getResources().getString(R.string.ntp_following),
                         mCoordinator.createFeedStream(/* isInterestFeed = */ false));
             }
@@ -570,7 +571,7 @@
             mSignInPromo = null;
         }
 
-        mCurrentStream = null;
+        unbindStream();
         mTabToStreamMap.clear();
 
         mPrefChangeRegistrar.removeObserver(Pref.ARTICLES_LIST_VISIBLE);
diff --git a/chrome/android/java/AndroidManifest.xml b/chrome/android/java/AndroidManifest.xml
index a9c8193..4f5b3ec 100644
--- a/chrome/android/java/AndroidManifest.xml
+++ b/chrome/android/java/AndroidManifest.xml
@@ -306,6 +306,10 @@
             <intent-filter>
                 <action android:name="com.sec.android.airview.HOVER" />
             </intent-filter>
+            <intent-filter>
+                <action android:name="android.web.action.APP_ATTRIBUTION" />
+                <category android:name="android.intent.category.DEFAULT" />
+            </intent-filter>
             <meta-data android:name="android.app.searchable"
                 android:resource="@xml/searchable" />
         </activity-alias>
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/LaunchIntentDispatcher.java b/chrome/android/java/src/org/chromium/chrome/browser/LaunchIntentDispatcher.java
index cf66dac..8d54aaa 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/LaunchIntentDispatcher.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/LaunchIntentDispatcher.java
@@ -32,6 +32,8 @@
 import org.chromium.base.metrics.RecordHistogram;
 import org.chromium.chrome.browser.app.ChromeActivity;
 import org.chromium.chrome.browser.app.video_tutorials.VideoTutorialShareHelper;
+import org.chromium.chrome.browser.attribution_reporting.AttributionIntentHandler;
+import org.chromium.chrome.browser.attribution_reporting.AttributionIntentHandlerFactory;
 import org.chromium.chrome.browser.browserservices.SessionDataHolder;
 import org.chromium.chrome.browser.browserservices.ui.splashscreen.trustedwebactivity.TwaSplashController;
 import org.chromium.chrome.browser.customtabs.CustomTabActivity;
@@ -77,9 +79,10 @@
     private static final int MAX_NUM_TASKS = 100;
 
     private final Activity mActivity;
-    private final Intent mIntent;
+    private Intent mIntent;
     private final boolean mIsCustomTabIntent;
     private final boolean mIsVrIntent;
+    private final AttributionIntentHandler mAttributionIntentHandler;
 
     @IntDef({Action.CONTINUE, Action.FINISH_ACTIVITY, Action.FINISH_ACTIVITY_REMOVE_TASK})
     @Retention(RetentionPolicy.SOURCE)
@@ -130,6 +133,7 @@
     private LaunchIntentDispatcher(Activity activity, Intent intent) {
         mActivity = activity;
         mIntent = IntentUtils.sanitizeIntent(intent);
+        mAttributionIntentHandler = AttributionIntentHandlerFactory.create();
 
         // Needs to be called as early as possible, to accurately capture the
         // time at which the intent was received.
@@ -155,6 +159,10 @@
         PartnerBrowserCustomizations.getInstance().initializeAsync(
                 mActivity.getApplicationContext());
 
+        // Must come before processing other intents, as we may un-wrap |mIntent| to another type of
+        // Intent.
+        if (handleAppAttributionIntent()) return Action.FINISH_ACTIVITY;
+
         int tabId = IntentHandler.getBringTabToFrontId(mIntent);
         boolean incognito =
                 mIntent.getBooleanExtra(IntentHandler.EXTRA_OPEN_NEW_INCOGNITO_TAB, false);
@@ -534,4 +542,12 @@
         return IntentUtils.safeGetBooleanExtra(
                 intent, TrustedWebUtils.EXTRA_LAUNCH_AS_TRUSTED_WEB_ACTIVITY, false);
     }
+
+    private boolean handleAppAttributionIntent() {
+        if (mAttributionIntentHandler.handleOuterAttributionIntent(mIntent)) return true;
+
+        Intent launchIntent = mAttributionIntentHandler.handleInnerAttributionIntent(mIntent);
+        if (launchIntent != null) mIntent = IntentUtils.sanitizeIntent(launchIntent);
+        return false;
+    }
 }
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 88ad86ab..eb3f0f9 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());
+        mUmaSessionStats.startNewSession(getTabModelSelector(), getWindowAndroid());
     }
 
     /**
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 ce77ed7..7739cfe 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,6 +4,7 @@
 
 package org.chromium.chrome.browser.metrics;
 
+import android.Manifest;
 import android.content.ComponentCallbacks;
 import android.content.Context;
 import android.content.res.Configuration;
@@ -18,6 +19,7 @@
 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;
@@ -26,6 +28,7 @@
 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;
 
 /**
@@ -88,9 +91,11 @@
     /**
      * 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.
+     *        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.
      */
-    public void startNewSession(TabModelSelector tabModelSelector) {
+    public void startNewSession(TabModelSelector tabModelSelector, WindowAndroid windowAndroid) {
         ensureNativeInitialized();
 
         mTabModelSelector = tabModelSelector;
@@ -121,6 +126,24 @@
         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/offlinepages/RequestCoordinatorBridge.java b/chrome/android/java/src/org/chromium/chrome/browser/offlinepages/RequestCoordinatorBridge.java
index 83dc5abf..e1d8c24d 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/offlinepages/RequestCoordinatorBridge.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/offlinepages/RequestCoordinatorBridge.java
@@ -237,7 +237,7 @@
     }
 
     @NativeMethods
-    interface Natives {
+    public interface Natives {
         void getRequestsInQueue(Profile profile, Callback<SavePageRequest[]> callback);
         void removeRequestsFromQueue(
                 Profile profile, long[] requestIds, RequestsRemovedCallback callback);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/tab/DEPS b/chrome/android/java/src/org/chromium/chrome/browser/tab/DEPS
index 6f31712d..a1f77a1 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/tab/DEPS
+++ b/chrome/android/java/src/org/chromium/chrome/browser/tab/DEPS
@@ -13,6 +13,9 @@
   "+chrome/android/java/src/org/chromium/chrome/browser/media/MediaCaptureNotificationServiceImpl.java",
   "+chrome/android/java/src/org/chromium/chrome/browser/metrics/UkmRecorder.java",
   "+chrome/android/java/src/org/chromium/chrome/browser/native_page/NativePageFactory.java",
+  "+chrome/android/java/src/org/chromium/chrome/browser/offlinepages/OfflinePageBridge.java",
+  "+chrome/android/java/src/org/chromium/chrome/browser/offlinepages/OfflinePageOrigin.java",
+  "+chrome/android/java/src/org/chromium/chrome/browser/offlinepages/RequestCoordinatorBridge.java",
   "+chrome/android/java/src/org/chromium/chrome/browser/paint_preview/PaintPreviewTabHelper.java",
   "+chrome/android/java/src/org/chromium/chrome/browser/policy/PolicyAuditor.java",
   "+chrome/android/java/src/org/chromium/chrome/browser/tab",
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/tab/TabContextMenuItemDelegate.java b/chrome/android/java/src/org/chromium/chrome/browser/tab/TabContextMenuItemDelegate.java
index e4723e7..4f0dcc48 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/tab/TabContextMenuItemDelegate.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/tab/TabContextMenuItemDelegate.java
@@ -29,6 +29,8 @@
 import org.chromium.chrome.browser.feature_engagement.TrackerFactory;
 import org.chromium.chrome.browser.incognito.IncognitoUtils;
 import org.chromium.chrome.browser.multiwindow.MultiWindowUtils;
+import org.chromium.chrome.browser.offlinepages.OfflinePageBridge;
+import org.chromium.chrome.browser.offlinepages.RequestCoordinatorBridge;
 import org.chromium.chrome.browser.profiles.Profile;
 import org.chromium.chrome.browser.tab.state.CriticalPersistedTabData;
 import org.chromium.chrome.browser.tabmodel.TabModelSelector;
@@ -272,13 +274,22 @@
 
     @Override
     public void onReadLater(GURL url, String title) {
+        if (url == null || url.isEmpty()) return;
+        assert url.isValid();
+
         BookmarkModel bookmarkModel = new BookmarkModel();
         bookmarkModel.finishLoadingBookmarkModel(() -> {
+            // Add to reading list.
             BookmarkUtils.addToReadingList(
                     url, title, mSnackbarManagerSupplier.get(), bookmarkModel, mTab.getContext());
             TrackerFactory.getTrackerForProfile(Profile.getLastUsedRegularProfile())
                     .notifyEvent(EventConstants.READ_LATER_CONTEXT_MENU_TAPPED);
             bookmarkModel.destroy();
+
+            // Add to offline pages.
+            RequestCoordinatorBridge.getForProfile(Profile.getLastUsedRegularProfile())
+                    .savePageLater(url.getSpec(), OfflinePageBridge.BOOKMARK_NAMESPACE,
+                            /*userRequested*/ true);
         });
     }
 
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/IntentFilterUnitTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/IntentFilterUnitTest.java
index 2a2f66b..26afff4 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/IntentFilterUnitTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/IntentFilterUnitTest.java
@@ -19,6 +19,7 @@
 import org.chromium.base.ContextUtils;
 import org.chromium.base.test.BaseJUnit4ClassRunner;
 import org.chromium.base.test.util.Batch;
+import org.chromium.chrome.browser.attribution_reporting.AttributionConstants;
 
 /**
  * Unit tests for Intent Filters in chrome/android/java/AndroidManifest.xml
@@ -196,4 +197,11 @@
         mIntent.addCategory(Intent.CATEGORY_BROWSABLE);
         verifyIntent(false);
     }
+
+    @Test
+    @SmallTest
+    public void testAttributionIntent() {
+        mIntent.setAction(AttributionConstants.ACTION_APP_ATTRIBUTION);
+        verifyIntent(true);
+    }
 }
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/LauncherShortcutTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/LauncherShortcutTest.java
index 0930eaa..16a71b9 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/LauncherShortcutTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/LauncherShortcutTest.java
@@ -201,7 +201,7 @@
         List<ShortcutInfo> shortcuts = shortcutManager.getDynamicShortcuts();
         Assert.assertEquals("Incorrect number of dynamic shortcuts.", 1, shortcuts.size());
         Assert.assertEquals(
-                "Incorrect label", "New incognito tab", shortcuts.get(0).getLongLabel());
+                "Incorrect label", "New Incognito tab", shortcuts.get(0).getLongLabel());
 
         LauncherShortcutActivity.setDynamicShortcutStringForTesting("Foo");
         callbackHelper = new CallbackHelper();
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
new file mode 100644
index 0000000..c658e141
--- /dev/null
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/metrics/StartupPermissionsMetricsTest.java
@@ -0,0 +1,115 @@
+// 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/ntp/IncognitoDescriptionViewRenderTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/ntp/IncognitoDescriptionViewRenderTest.java
index 871fb2b..6acc943c 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/ntp/IncognitoDescriptionViewRenderTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/ntp/IncognitoDescriptionViewRenderTest.java
@@ -40,7 +40,7 @@
 
     @Rule
     public ChromeRenderTestRule mRenderTestRule =
-            ChromeRenderTestRule.Builder.withPublicCorpus().build();
+            ChromeRenderTestRule.Builder.withPublicCorpus().setRevision(1).build();
 
     public IncognitoDescriptionViewRenderTest(boolean nightModeEnabled) {
         NightModeTestUtils.setUpNightModeForDummyUiActivity(nightModeEnabled);
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/read_later/ReadLaterIphTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/read_later/ReadLaterContextMenuTest.java
similarity index 75%
rename from chrome/android/javatests/src/org/chromium/chrome/browser/read_later/ReadLaterIphTest.java
rename to chrome/android/javatests/src/org/chromium/chrome/browser/read_later/ReadLaterContextMenuTest.java
index 4f11fc0..5b1f1dec 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/read_later/ReadLaterIphTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/read_later/ReadLaterContextMenuTest.java
@@ -13,7 +13,11 @@
 import static org.hamcrest.Matchers.is;
 import static org.hamcrest.Matchers.not;
 import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
 import static org.chromium.chrome.browser.toolbar.top.ButtonHighlightMatcher.withHighlight;
@@ -39,12 +43,15 @@
 import org.chromium.base.Callback;
 import org.chromium.base.test.util.Batch;
 import org.chromium.base.test.util.CommandLineFlags;
+import org.chromium.base.test.util.JniMocker;
 import org.chromium.base.test.util.Restriction;
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.app.ChromeActivity;
 import org.chromium.chrome.browser.feature_engagement.TrackerFactory;
 import org.chromium.chrome.browser.flags.ChromeFeatureList;
 import org.chromium.chrome.browser.flags.ChromeSwitches;
+import org.chromium.chrome.browser.offlinepages.RequestCoordinatorBridge;
+import org.chromium.chrome.browser.offlinepages.RequestCoordinatorBridgeJni;
 import org.chromium.chrome.browser.tab.Tab;
 import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
 import org.chromium.chrome.test.ChromeTabbedActivityTestRule;
@@ -60,18 +67,24 @@
 @CommandLineFlags.Add({ChromeSwitches.DISABLE_FIRST_RUN_EXPERIENCE})
 @Features.EnableFeatures(ChromeFeatureList.READ_LATER)
 @Batch(Batch.PER_CLASS)
-public class ReadLaterIphTest {
+public class ReadLaterContextMenuTest {
     @Rule
     public ChromeTabbedActivityTestRule mActivityTestRule = new ChromeTabbedActivityTestRule();
     @Rule
     public EmbeddedTestServerRule mTestServer = new EmbeddedTestServerRule();
     @Rule
     public MockitoRule mMockitoRule = MockitoJUnit.rule();
+    @Rule
+    public JniMocker mocker = new JniMocker();
     @Mock
     private Tracker mTracker;
+    @Mock
+    RequestCoordinatorBridge.Natives mRequestCoordinatorBridgeJniMock;
 
     private static final String CONTEXT_MENU_TEST_URL =
             "/chrome/test/data/android/contextmenu/context_menu_test.html";
+    private static final String CONTEXT_MENU_LINK_URL =
+            "/chrome/test/data/android/contextmenu/test_link.html";
     private static final String CONTEXT_MENU_LINK_DOM_ID = "testLink";
 
     @Before
@@ -86,6 +99,7 @@
                 .addOnInitializedCallback(any());
         TrackerFactory.setTrackerForTests(mTracker);
         mActivityTestRule.startMainActivityOnBlankPage();
+        mocker.mock(RequestCoordinatorBridgeJni.TEST_HOOKS, mRequestCoordinatorBridgeJniMock);
     }
 
     @After
@@ -111,6 +125,21 @@
         waitForHelpBubble(withText(R.string.reading_list_save_pages_for_later));
     }
 
+    @Test
+    @MediumTest
+    @Restriction({UiRestriction.RESTRICTION_TYPE_PHONE})
+    public void testContextMenuAddToOfflinePage() throws Throwable {
+        String url = mTestServer.getServer().getURL(CONTEXT_MENU_TEST_URL);
+        mActivityTestRule.loadUrlInNewTab(url);
+        ChromeActivity activity = mActivityTestRule.getActivity();
+        Tab tab = activity.getActivityTab();
+        RevampedContextMenuUtils.selectContextMenuItem(InstrumentationRegistry.getInstrumentation(),
+                activity, tab, CONTEXT_MENU_LINK_DOM_ID, R.id.contextmenu_read_later);
+        String linkUrl = mTestServer.getServer().getURL(CONTEXT_MENU_LINK_URL);
+        verify(mRequestCoordinatorBridgeJniMock, times(1))
+                .savePageLater(any(), any(), eq(linkUrl), any(), any(), any(), anyBoolean());
+    }
+
     private ViewInteraction waitForHelpBubble(Matcher<View> matcher) {
         View mainDecorView = mActivityTestRule.getActivity().getWindow().getDecorView();
         return onView(isRoot())
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/settings/MainSettingsFragmentTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/settings/MainSettingsFragmentTest.java
index 0856f88..682fb3ad 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/settings/MainSettingsFragmentTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/settings/MainSettingsFragmentTest.java
@@ -95,7 +95,7 @@
 public class MainSettingsFragmentTest {
     private static final String SEARCH_ENGINE_SHORT_NAME = "Google";
 
-    private static final int RENDER_TEST_REVISION = 2;
+    private static final int RENDER_TEST_REVISION = 3;
 
     private final HomepageTestRule mHomepageTestRule = new HomepageTestRule();
 
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/toolbar/top/TabSwitcherActionMenuRenderTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/toolbar/top/TabSwitcherActionMenuRenderTest.java
index 91cbfebd..2cf2d66 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/toolbar/top/TabSwitcherActionMenuRenderTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/toolbar/top/TabSwitcherActionMenuRenderTest.java
@@ -45,7 +45,7 @@
 
     @Rule
     public ChromeRenderTestRule mRenderTestRule =
-            ChromeRenderTestRule.Builder.withPublicCorpus().build();
+            ChromeRenderTestRule.Builder.withPublicCorpus().setRevision(1).build();
 
     private View mView;
 
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/webapps/launchpad/LaunchpadActivityTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/webapps/launchpad/LaunchpadActivityTest.java
index f04891f..641d8f41 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/webapps/launchpad/LaunchpadActivityTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/webapps/launchpad/LaunchpadActivityTest.java
@@ -6,7 +6,9 @@
 
 import android.graphics.Bitmap;
 import android.support.test.InstrumentationRegistry;
+import android.view.View;
 
+import androidx.recyclerview.widget.RecyclerView;
 import androidx.test.filters.MediumTest;
 
 import org.junit.After;
@@ -18,10 +20,18 @@
 import org.chromium.base.test.util.CommandLineFlags;
 import org.chromium.base.test.util.Feature;
 import org.chromium.chrome.browser.flags.ChromeSwitches;
+import org.chromium.chrome.browser.profiles.Profile;
 import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
 import org.chromium.chrome.test.ChromeTabbedActivityTestRule;
 import org.chromium.chrome.test.util.ActivityTestUtils;
+import org.chromium.components.browser_ui.site_settings.PermissionInfo;
+import org.chromium.components.content_settings.ContentSettingValues;
+import org.chromium.components.content_settings.ContentSettingsType;
 import org.chromium.content_public.browser.test.util.TestThreadUtils;
+import org.chromium.content_public.browser.test.util.TouchCommon;
+import org.chromium.ui.modaldialog.ModalDialogManager;
+import org.chromium.ui.modaldialog.ModalDialogProperties;
+import org.chromium.ui.modelutil.PropertyModel;
 import org.chromium.ui.test.util.RenderTestRule;
 
 import java.io.IOException;
@@ -84,4 +94,45 @@
         openLaunchpadActivity();
         mRenderTestRule.render(mLaunchpadCoordinator.getView(), "LaunchpadActivity");
     }
+
+    @Test
+    @MediumTest
+    @Feature({"RenderTest"})
+    public void testAppManagementMenu() throws IOException {
+        openLaunchpadActivity();
+        setPermissionDefaults();
+
+        View item = ((RecyclerView) mLaunchpadCoordinator.getView().findViewById(
+                             R.id.launchpad_recycler))
+                            .getChildAt(0);
+        TouchCommon.longPressView(item);
+
+        ModalDialogManager modalDialogManager = mLaunchpadActivity.getModalDialogManager();
+        PropertyModel dialogModel = modalDialogManager.getCurrentDialogForTest();
+        View dialogView = dialogModel.get(ModalDialogProperties.CUSTOM_VIEW);
+
+        mRenderTestRule.render(dialogView, "launchpad_management_menu");
+    }
+
+    /**
+     * Set permissions to run the test. Notifications set to Block, Mic set to Allow, Camera set to
+     * Allow, Location set to ASK.
+     */
+    private void setPermissionDefaults() {
+        TestThreadUtils.runOnUiThreadBlocking(() -> {
+            Profile profile = Profile.getLastUsedRegularProfile();
+            PermissionInfo notifications = new PermissionInfo(ContentSettingsType.NOTIFICATIONS,
+                    APP_URL, null /* embedder */, false /* isEmbargoed */);
+            notifications.setContentSetting(profile, ContentSettingValues.BLOCK);
+            PermissionInfo mic = new PermissionInfo(ContentSettingsType.MEDIASTREAM_MIC, APP_URL,
+                    null /* embedder */, false /* isEmbargoed */);
+            mic.setContentSetting(profile, ContentSettingValues.ALLOW);
+            PermissionInfo camera = new PermissionInfo(ContentSettingsType.MEDIASTREAM_CAMERA,
+                    APP_URL, null /* embedder */, false /* isEmbargoed */);
+            camera.setContentSetting(profile, ContentSettingValues.ALLOW);
+            PermissionInfo location = new PermissionInfo(ContentSettingsType.GEOLOCATION, APP_URL,
+                    null /* embedder */, false /* isEmbargoed */);
+            location.setContentSetting(profile, ContentSettingValues.ASK);
+        });
+    }
 }
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/feed/FeedSurfaceCoordinatorTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/feed/FeedSurfaceCoordinatorTest.java
index bccd5359..e0fd465 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/feed/FeedSurfaceCoordinatorTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/feed/FeedSurfaceCoordinatorTest.java
@@ -39,6 +39,7 @@
 import org.chromium.chrome.browser.feed.v2.FakeLinearLayoutManager;
 import org.chromium.chrome.browser.feed.v2.FeedStream;
 import org.chromium.chrome.browser.feed.v2.FeedStreamJni;
+import org.chromium.chrome.browser.feed.webfeed.WebFeedBridge;
 import org.chromium.chrome.browser.flags.ChromeFeatureList;
 import org.chromium.chrome.browser.native_page.NativePageNavigationDelegate;
 import org.chromium.chrome.browser.ntp.SnapScrollHelper;
@@ -134,6 +135,8 @@
     private FeedStream.Natives mFeedStreamJniMock;
     @Mock
     private FeedServiceBridge.Natives mFeedServiceBridgeJniMock;
+    @Mock
+    private WebFeedBridge.Natives mWebFeedBridgeJniMock;
 
     // Mocked xSurface setup.
     @Mock
@@ -177,6 +180,7 @@
         mActivity = Robolectric.buildActivity(Activity.class).get();
         mocker.mock(FeedStreamJni.TEST_HOOKS, mFeedStreamJniMock);
         mocker.mock(FeedServiceBridgeJni.TEST_HOOKS, mFeedServiceBridgeJniMock);
+        mocker.mock(WebFeedBridge.getTestHooksForTesting(), mWebFeedBridgeJniMock);
 
         when(mFeedServiceBridgeJniMock.getLoadMoreTriggerLookahead()).thenReturn(5);
 
diff --git a/chrome/app/bookmarks_strings.grdp b/chrome/app/bookmarks_strings.grdp
index 5903db6..85a5cc2 100644
--- a/chrome/app/bookmarks_strings.grdp
+++ b/chrome/app/bookmarks_strings.grdp
@@ -79,11 +79,11 @@
           =1 {Open in &amp;new window}
           other {Open all ({COUNT}) in &amp;new window}}
     </message>
-    <message name="IDS_BOOKMARK_BAR_OPEN_ALL_COUNT_INCOGNITO" desc="Menu description for opening all urls in a bookmark folder in an incognito window">
+    <message name="IDS_BOOKMARK_BAR_OPEN_ALL_COUNT_INCOGNITO" desc="Menu description for opening all urls in a bookmark folder in an Incognito window">
         {COUNT, plural,
-          =0 {Open all in &amp;incognito window}
-          =1 {Open in &amp;incognito window}
-          other {Open all ({COUNT}) in &amp;incognito window}}
+          =0 {Open all in &amp;Incognito window}
+          =1 {Open in &amp;Incognito window}
+          other {Open all ({COUNT}) in &amp;Incognito window}}
     </message>
     <message name="IDS_BOOKMARK_BAR_OPEN_IN_NEW_TAB" desc="Menu description for loading bookmark in a new tab">
       &amp;Open in new tab
@@ -91,8 +91,8 @@
     <message name="IDS_BOOKMARK_BAR_OPEN_IN_NEW_WINDOW" desc="Menu description for loading bookmark in a new window">
       Open in &amp;new window
     </message>
-    <message name="IDS_BOOKMARK_BAR_OPEN_INCOGNITO" desc="Menu description for opening bookmark in incognito window">
-      Open in &amp;incognito window
+    <message name="IDS_BOOKMARK_BAR_OPEN_INCOGNITO" desc="Menu description for opening bookmark in Incognito window">
+      Open in &amp;Incognito window
     </message>
     <message name="IDS_BOOKMARK_BAR_EDIT" desc="Title of the bookmark context menu item that brings up the bookmark editor">
       &amp;Edit...
@@ -131,7 +131,7 @@
           =1 {Open in &amp;New Window}
           other {Open All ({COUNT}) in &amp;New Window}}
     </message>
-    <message name="IDS_BOOKMARK_BAR_OPEN_ALL_COUNT_INCOGNITO" desc="In Title Case: Menu description for opening all urls in a bookmark folder in an incognito window">
+    <message name="IDS_BOOKMARK_BAR_OPEN_ALL_COUNT_INCOGNITO" desc="In Title Case: Menu description for opening all urls in a bookmark folder in an Incognito window">
         {COUNT, plural,
           =0 {Open All in &amp;Incognito Window}
           =1 {Open in &amp;Incognito Window}
@@ -143,7 +143,7 @@
     <message name="IDS_BOOKMARK_BAR_OPEN_IN_NEW_WINDOW" desc="In Title Case: Menu description for loading bookmark in a new window">
       Open in &amp;New Window
     </message>
-    <message name="IDS_BOOKMARK_BAR_OPEN_INCOGNITO" desc="In Title Case: Menu description for opening bookmark in incognito window">
+    <message name="IDS_BOOKMARK_BAR_OPEN_INCOGNITO" desc="In Title Case: Menu description for opening bookmark in Incognito window">
       Open in &amp;Incognito Window
     </message>
     <message name="IDS_BOOKMARK_BAR_EDIT" desc="In Title Case: Title of the bookmark context menu item that brings up the bookmark editor">
@@ -391,11 +391,11 @@
   <message name="IDS_BOOKMARK_MANAGER_MENU_OPEN_ALL_NEW_WINDOW_WITH_COUNT" desc="Menu title for opening all urls in a bookmark folder in a new window, which includes a count of urls">
     Open all (<ph name="URL_COUNT">$1<ex>5</ex></ph>) in new window
   </message>
-  <message name="IDS_BOOKMARK_MANAGER_MENU_OPEN_ALL_INCOGNITO" desc="Menu description for opening all urls in a bookmark folder in an incognito window">
-    Open all in incognito window
+  <message name="IDS_BOOKMARK_MANAGER_MENU_OPEN_ALL_INCOGNITO" desc="Menu description for opening all urls in a bookmark folder in an Incognito window">
+    Open all in Incognito window
   </message>
-  <message name="IDS_BOOKMARK_MANAGER_MENU_OPEN_ALL_INCOGNITO_WITH_COUNT" desc="Menu description for opening all urls in a bookmark folder in an incognito window, which includes a count of urls">
-    Open all (<ph name="URL_COUNT">$1<ex>5</ex></ph>) in incognito window
+  <message name="IDS_BOOKMARK_MANAGER_MENU_OPEN_ALL_INCOGNITO_WITH_COUNT" desc="Menu description for opening all urls in a bookmark folder in an Incognito window, which includes a count of urls">
+    Open all (<ph name="URL_COUNT">$1<ex>5</ex></ph>) in Incognito window
   </message>
   <message name="IDS_BOOKMARK_MANAGER_MENU_OPEN_IN_NEW_TAB" desc="Menu description for opening a bookmark in a new tab">
     Open in new tab
@@ -403,8 +403,8 @@
   <message name="IDS_BOOKMARK_MANAGER_MENU_OPEN_IN_NEW_WINDOW" desc="Menu description for opening a bookmark in a new window">
     Open in new window
   </message>
-  <message name="IDS_BOOKMARK_MANAGER_MENU_OPEN_INCOGNITO" desc="Menu description for opening a bookmark in incognito window">
-    Open in incognito window
+  <message name="IDS_BOOKMARK_MANAGER_MENU_OPEN_INCOGNITO" desc="Menu description for opening a bookmark in Incognito window">
+    Open in Incognito window
   </message>
   <message name="IDS_BOOKMARK_MANAGER_MENU_RENAME" desc="Title of the bookmark list dropdown menu item that renames folders.">
     Rename
diff --git a/chrome/app/chromeos_strings.grdp b/chrome/app/chromeos_strings.grdp
index 6290b7bc..0c5dfee 100644
--- a/chrome/app/chromeos_strings.grdp
+++ b/chrome/app/chromeos_strings.grdp
@@ -2368,7 +2368,7 @@
   <message name="IDS_CHROMEOS_ACC_LOGIN_SIGNING_IN" desc="Status when signing in.">
     Signing in.
   </message>
-  <message name="IDS_CHROMEOS_ACC_LOGIN_SIGNIN_OFFRECORD" desc="Status when signing in incognito.">
+  <message name="IDS_CHROMEOS_ACC_LOGIN_SIGNIN_OFFRECORD" desc="Status when signing in Incognito.">
     Entering as Guest.
   </message>
   <message name="IDS_CHROMEOS_ACC_LOGIN_SIGNIN_PUBLIC_ACCOUNT" desc="Status when signing in into a public account.">
@@ -5399,9 +5399,9 @@
     Add another Google Account for <ph name="USER_NAME">$1<ex>John</ex></ph>
   </message>
   <message name="IDS_ACCOUNT_MANAGER_DIALOG_WELCOME_BODY" desc="Text body for the Welcome screen in Chrome OS 'Add account' dialog.">
-    You can manage signed-in Google Accounts from <ph name="LINK_BEGIN">&lt;a id="osSettingsLink" href="$1<ex>https://google.com/</ex>"&gt;</ph>Settings<ph name="LINK_END">&lt;/a&gt;</ph>. Permissions you have granted to websites and apps may apply to all accounts. If you don't want sites or apps to access your account info, you can sign into your <ph name="DEVICE_TYPE">$2<ex>Chromebook</ex></ph> as a guest or browse the web in an <ph name="LINK_2_BEGIN">&lt;a id="incognitoLink" href="#"&gt;</ph>incognito window<ph name="LINK_2_END">&lt;/a&gt;</ph>.
+    You can manage signed-in Google Accounts from <ph name="LINK_BEGIN">&lt;a id="osSettingsLink" href="$1<ex>https://google.com/</ex>"&gt;</ph>Settings<ph name="LINK_END">&lt;/a&gt;</ph>. Permissions you have granted to websites and apps may apply to all accounts. If you don't want sites or apps to access your account info, you can sign into your <ph name="DEVICE_TYPE">$2<ex>Chromebook</ex></ph> as a guest or browse the web in an <ph name="LINK_2_BEGIN">&lt;a id="incognitoLink" href="#"&gt;</ph>Incognito window<ph name="LINK_2_END">&lt;/a&gt;</ph>.
   </message>
-  <message name="IDS_ACCOUNT_MANAGER_DIALOG_WELCOME_BODY_WITHOUT_INCOGNITO" desc="Text body for the Welcome screen in Chrome OS 'Add account' dialog without incognito link.">
+  <message name="IDS_ACCOUNT_MANAGER_DIALOG_WELCOME_BODY_WITHOUT_INCOGNITO" desc="Text body for the Welcome screen in Chrome OS 'Add account' dialog without Incognito link.">
     You can manage signed-in Google Accounts from <ph name="LINK_BEGIN">&lt;a id="osSettingsLink" href="$1<ex>https://google.com/</ex>"&gt;</ph>Settings<ph name="LINK_END">&lt;/a&gt;</ph>. Permissions you have granted to websites and apps may apply to all accounts. If you don't want sites or apps to access your account info, you can sign into your <ph name="DEVICE_TYPE">$2<ex>Chromebook</ex></ph> as a guest.
   </message>
   <message name="IDS_ACCOUNT_MANAGER_DIALOG_WELCOME_CHECKBOX" desc="Checkbox text for the Welcome screen in Chrome OS 'Add account' dialog. If user checks this checkbox, this screen will not be shown the next time they open the dialog.">
diff --git a/chrome/app/chromium_strings.grd b/chrome/app/chromium_strings.grd
index b2a2977..0172632c 100644
--- a/chrome/app/chromium_strings.grd
+++ b/chrome/app/chromium_strings.grd
@@ -582,8 +582,8 @@
       </if>
 
       <!-- chrome://settings/extensions page -->
-      <message name="IDS_EXTENSIONS_INCOGNITO_WARNING" desc="Warns the user that Chromium cannot prevent extensions from recording history in incognito mode. Displayed in extensions management UI after an extension is selected to be run in incognito mode.">
-        Warning: Chromium cannot prevent extensions from recording your browsing history. To disable this extension in incognito mode, unselect this option.
+      <message name="IDS_EXTENSIONS_INCOGNITO_WARNING" desc="Warns the user that Chromium cannot prevent extensions from recording history in Incognito mode. Displayed in extensions management UI after an extension is selected to be run in Incognito mode.">
+        Warning: Chromium cannot prevent extensions from recording your browsing history. To disable this extension in Incognito mode, unselect this option.
       </message>
       <message name="IDS_EXTENSIONS_UNINSTALL" desc="The link for uninstalling extensions.">
         Remove from Chromium...
@@ -943,7 +943,7 @@
           <message name="IDS_CONTENT_CONTEXT_OPENLINKNEWTAB_INAPP" desc="The name of the command to open a link in a newly created browser tab when the user is in an app window">
             Open link in new Chromium &amp;tab
           </message>
-          <message name="IDS_CONTENT_CONTEXT_OPENLINKOFFTHERECORD_INAPP" desc="The name of the command to open a link in an incognito browser window when the user is in an app window">
+          <message name="IDS_CONTENT_CONTEXT_OPENLINKOFFTHERECORD_INAPP" desc="The name of the command to open a link in an Incognito browser window when the user is in an app window">
             Open link in Chromium inco&amp;gnito window
           </message>
         </if>
@@ -951,7 +951,7 @@
           <message name="IDS_CONTENT_CONTEXT_OPENLINKNEWTAB_INAPP" desc="In Title Case: The name of the command to open a link in a newly created browser tab when the user is in an app window">
             Open Link in New Chromium &amp;tab
           </message>
-          <message name="IDS_CONTENT_CONTEXT_OPENLINKOFFTHERECORD_INAPP" desc="In Title Case: The name of the command to open a link in an incognito browser window when the user is in an app window">
+          <message name="IDS_CONTENT_CONTEXT_OPENLINKOFFTHERECORD_INAPP" desc="In Title Case: The name of the command to open a link in an Incognito browser window when the user is in an app window">
             Open Link in Chromium Inco&amp;gnito Window
           </message>
         </if>
@@ -962,7 +962,7 @@
         <message name="IDS_UPDATE_RECOMMENDED_DIALOG_TITLE" desc="The window title for the Update Recommended dialog.">
           Relaunch Chromium
         </message>
-        <message name="IDS_UPDATE_RECOMMENDED" desc="The main text of the Update Recommended dialog with a count of open incognito windows.">
+        <message name="IDS_UPDATE_RECOMMENDED" desc="The main text of the Update Recommended dialog with a count of open Incognito windows.">
           {COUNT, plural,
           =0 {A new update for Chromium is available and will be applied as soon as you relaunch.}
           =1 {A new update for Chromium is available and will be applied as soon as you relaunch. Your Incognito window won't reopen.}
diff --git a/chrome/app/extensions_strings.grdp b/chrome/app/extensions_strings.grdp
index 4de447f9..6599c948 100644
--- a/chrome/app/extensions_strings.grdp
+++ b/chrome/app/extensions_strings.grdp
@@ -61,7 +61,7 @@
   <message name="IDS_EXTENSIONS_VIEW_INACTIVE" desc="Text that signifies that the extension view is an inactive transient page.">
     (Inactive)
   </message>
-  <message name="IDS_EXTENSIONS_VIEW_INCOGNITO" desc="Text that signifies that the extension view is in an incognito process.">
+  <message name="IDS_EXTENSIONS_VIEW_INCOGNITO" desc="Text that signifies that the extension view is in an Incognito process.">
     (Incognito)
   </message>
   <message name="IDS_EXTENSIONS_DEVELOPER_MODE" desc="The text displayed next to the checkbox to toggle developer mode in the extensions page.">
@@ -199,8 +199,8 @@
   <message name="IDS_EXTENSIONS_ITEM_NO_ACTIVE_VIEWS" desc="Placeholder text when the extension Inspect Views list is empty.">
     No active views
   </message>
-  <message name="IDS_EXTENSIONS_ITEM_ALLOW_INCOGNITO" desc="The text next to the checkbox to enable an extension in incognito mode.">
-    Allow in incognito
+  <message name="IDS_EXTENSIONS_ITEM_ALLOW_INCOGNITO" desc="The text next to the checkbox to enable an extension in Incognito mode.">
+    Allow in Incognito
   </message>
   <message name="IDS_EXTENSIONS_ITEM_DEPENDENCIES" desc="The label above a list of any extensions that depend on this extension.">
     The following extensions depend on this extension:
diff --git a/chrome/app/generated_resources.grd b/chrome/app/generated_resources.grd
index 77912d68..c2e27fc4 100644
--- a/chrome/app/generated_resources.grd
+++ b/chrome/app/generated_resources.grd
@@ -581,7 +581,7 @@
             Open link in new &amp;window
           </message>
           <!-- Also update IDS_CONTENT_CONTEXT_OPENLINKOFFTHERECORD_INAPP in google_chrome_strings.grd and chromium_strings.grd. -->
-          <message name="IDS_CONTENT_CONTEXT_OPENLINKOFFTHERECORD" desc="The name of the open a link in incognito window command">
+          <message name="IDS_CONTENT_CONTEXT_OPENLINKOFFTHERECORD" desc="The name of the open a link in Incognito window command">
             Open link in inco&amp;gnito window
           </message>
           <message name="IDS_CONTENT_CONTEXT_OPENLINKINPROFILES" desc="The name of the open a link as a different user context menu">
@@ -810,7 +810,7 @@
             Open Link in New &amp;Window
           </message>
           <!-- Also update IDS_CONTENT_CONTEXT_OPENLINKOFFTHERECORD_INAPP in google_chrome_strings.grd and chromium_strings.grd. -->
-          <message name="IDS_CONTENT_CONTEXT_OPENLINKOFFTHERECORD" desc="In Title Case: The name of the open a link in incognito window command">
+          <message name="IDS_CONTENT_CONTEXT_OPENLINKOFFTHERECORD" desc="In Title Case: The name of the open a link in Incognito window command">
             Open Link in Inco&amp;gnito Window
           </message>
           <message name="IDS_CONTENT_CONTEXT_OPENLINKINPROFILES" desc="In Title Case: The name of the open a link as a different user context menu">
@@ -1045,8 +1045,8 @@
           <message name="IDS_NEW_WINDOW" desc="The text label of a menu item for opening a new window">
             &amp;New window
           </message>
-          <message name="IDS_NEW_INCOGNITO_WINDOW" desc="The text label of a menu item for opening a new incognito window">
-            New &amp;incognito window
+          <message name="IDS_NEW_INCOGNITO_WINDOW" desc="The text label of a menu item for opening a new Incognito window">
+            New &amp;Incognito window
           </message>
           <message name="IDS_PIN_TO_START_SCREEN" desc="The text label of the Pin to start screen menu item">
             Pin this page to Start screen...
@@ -1125,7 +1125,7 @@
           <message name="IDS_NEW_WINDOW" desc="In Title Case: The text label of a menu item for opening a new window">
             &amp;New Window
           </message>
-          <message name="IDS_NEW_INCOGNITO_WINDOW" desc="In Title Case: The text label of a menu item for opening a new incognito window">
+          <message name="IDS_NEW_INCOGNITO_WINDOW" desc="In Title Case: The text label of a menu item for opening a new Incognito window">
             New &amp;Incognito Window
           </message>
           <message name="IDS_PIN_TO_START_SCREEN" desc="In Title Case: The text label of the Pin to start screen menu item">
@@ -2349,39 +2349,39 @@
       </if>
 
       <!-- Abandon in-progress downloads confirmation dialog -->
-      <message name="IDS_ABANDON_DOWNLOAD_DIALOG_TITLE" desc="Title of the dialog shown when the user closes the browser or the last incognito window while one or more downloads are in progress. If necessary, add '#' for plural cases. [ICU Syntax]">
+      <message name="IDS_ABANDON_DOWNLOAD_DIALOG_TITLE" desc="Title of the dialog shown when the user closes the browser or the last Incognito window while one or more downloads are in progress. If necessary, add '#' for plural cases. [ICU Syntax]">
         {NUM_DOWNLOAD, plural,
         =1 {Download is in progress} other {Downloads are in progress}}
       </message>
       <if expr="not use_titlecase">
-        <message name="IDS_ABANDON_DOWNLOAD_DIALOG_CONTINUE_BUTTON" desc="Button text on a dialog shown when the user closes the browser or the last incognito window while one or more downloads are in progress. When clicked, the button will continue the download without exiting.">
+        <message name="IDS_ABANDON_DOWNLOAD_DIALOG_CONTINUE_BUTTON" desc="Button text on a dialog shown when the user closes the browser or the last Incognito window while one or more downloads are in progress. When clicked, the button will continue the download without exiting.">
           Continue downloading
         </message>
       </if>
       <if expr="use_titlecase">
-        <message name="IDS_ABANDON_DOWNLOAD_DIALOG_CONTINUE_BUTTON" desc="In Title Case: Button text on a dialog shown when the user closes the browser or the last incognito window while one or more downloads are in progress. When clicked, the button will continue the download without exiting.">
+        <message name="IDS_ABANDON_DOWNLOAD_DIALOG_CONTINUE_BUTTON" desc="In Title Case: Button text on a dialog shown when the user closes the browser or the last Incognito window while one or more downloads are in progress. When clicked, the button will continue the download without exiting.">
           Continue Downloading
         </message>
       </if>
       <if expr="not is_macosx">
-        <message name="IDS_ABANDON_DOWNLOAD_DIALOG_INCOGNITO_MESSAGE" desc="Message on a dialog shown when the user closes the last incognito window while one or more downloads are in progress. This string is shown on Windows, Chrome OS, and Linux, which all use 'Exit' to refer to closing a browser.">
-          Exit incognito mode anyway?
+        <message name="IDS_ABANDON_DOWNLOAD_DIALOG_INCOGNITO_MESSAGE" desc="Message on a dialog shown when the user closes the last Incognito window while one or more downloads are in progress. This string is shown on Windows, Chrome OS, and Linux, which all use 'Exit' to refer to closing a browser.">
+          Exit Incognito mode anyway?
         </message>
         <message name="IDS_ABANDON_DOWNLOAD_DIALOG_GUEST_MESSAGE" desc="Message on a dialog shown when the user closes the last guest window while one or more downloads are in progress. This string is shown on Windows, Chrome OS, and Linux, which all use 'Exit' to refer to closing a browser.">
           Exit guest mode anyway?
         </message>
-        <message name="IDS_ABANDON_DOWNLOAD_DIALOG_EXIT_BUTTON" desc="Button text on a dialog shown when the user closes the browser or the last incognito window while one or more downloads are in progress. When clicked, the button will abandon the download and exit. This string is shown on Windows, Chrome OS, and Linux, which all use 'Exit' to refer to closing a browser.">
+        <message name="IDS_ABANDON_DOWNLOAD_DIALOG_EXIT_BUTTON" desc="Button text on a dialog shown when the user closes the browser or the last Incognito window while one or more downloads are in progress. When clicked, the button will abandon the download and exit. This string is shown on Windows, Chrome OS, and Linux, which all use 'Exit' to refer to closing a browser.">
           Exit
         </message>
       </if>
       <if expr="is_macosx">
-        <message name="IDS_ABANDON_DOWNLOAD_DIALOG_INCOGNITO_MESSAGE" desc="Message on a dialog shown when the user closes the last incognito window while one or more downloads are in progress. This string is shown on Mac OSX only, which uses 'Quit' to refer to closing a browser.">
-          Quit incognito mode anyway?
+        <message name="IDS_ABANDON_DOWNLOAD_DIALOG_INCOGNITO_MESSAGE" desc="Message on a dialog shown when the user closes the last Incognito window while one or more downloads are in progress. This string is shown on Mac OSX only, which uses 'Quit' to refer to closing a browser.">
+          Quit Incognito mode anyway?
         </message>
         <message name="IDS_ABANDON_DOWNLOAD_DIALOG_GUEST_MESSAGE" desc="Message on a dialog shown when the user closes the last guest window while one or more downloads are in progress. This string is shown on Mac OSX only, which uses 'Exit' to refer to closing a browser.">
           Exit guest mode anyway?
         </message>
-        <message name="IDS_ABANDON_DOWNLOAD_DIALOG_EXIT_BUTTON" desc="Button text on a dialog shown when the user closes the browser or the last incognito window while one or more downloads are in progress. When clicked, the button will abandon the download and quit. This string is shown on Mac OSX only, which uses 'Quit' to refer to closing a browser.">
+        <message name="IDS_ABANDON_DOWNLOAD_DIALOG_EXIT_BUTTON" desc="Button text on a dialog shown when the user closes the browser or the last Incognito window while one or more downloads are in progress. When clicked, the button will abandon the download and quit. This string is shown on Mac OSX only, which uses 'Quit' to refer to closing a browser.">
           Quit
         </message>
       </if>
@@ -3620,19 +3620,19 @@
         <message name="IDS_TASK_MANAGER_EXTENSION_PREFIX" desc="The prefix for a Task Manager extension row (always visible if the extension has a view)">
           Extension: <ph name="EXTENSION_NAME">$1<ex>Sample Extension</ex></ph>
         </message>
-        <message name="IDS_TASK_MANAGER_EXTENSION_INCOGNITO_PREFIX" desc="The prefix for a Task Manager incognito extension row (may not be visible if incognito is not open)">
+        <message name="IDS_TASK_MANAGER_EXTENSION_INCOGNITO_PREFIX" desc="The prefix for a Task Manager Incognito extension row (may not be visible if Incognito is not open)">
           Incognito Extension: <ph name="EXTENSION_NAME">$1<ex>Sample Extension</ex></ph>
         </message>
         <message name="IDS_TASK_MANAGER_APP_PREFIX" desc="The prefix for a Task Manager app row (always visible if the app has a view)">
           App: <ph name="APP_NAME">$1<ex>Sample App</ex></ph>
         </message>
-        <message name="IDS_TASK_MANAGER_APP_INCOGNITO_PREFIX" desc="The prefix for a Task Manager incognito app row (may not be visible if the app is not open in incognito)">
+        <message name="IDS_TASK_MANAGER_APP_INCOGNITO_PREFIX" desc="The prefix for a Task Manager Incognito app row (may not be visible if the app is not open in Incognito)">
           Incognito App: <ph name="APP_NAME">$1<ex>Sample App</ex></ph>
         </message>
         <message name="IDS_TASK_MANAGER_TAB_PREFIX" desc="The prefix for a Task Manager Tab row">
           Tab: <ph name="TAB_NAME">$1<ex>Google</ex></ph>
         </message>
-        <message name="IDS_TASK_MANAGER_TAB_INCOGNITO_PREFIX" desc="The prefix for a Task Manager incognito Tab row (may not be visible if incognito is not open)">
+        <message name="IDS_TASK_MANAGER_TAB_INCOGNITO_PREFIX" desc="The prefix for a Task Manager Incognito Tab row (may not be visible if Incognito is not open)">
           Incognito Tab: <ph name="TAB_NAME">$1<ex>Google</ex></ph>
         </message>
         <message name="IDS_TASK_MANAGER_BACKGROUND_APP_PREFIX" desc="The prefix for a Task Manager background app task representing a BackgroundContents">
@@ -3686,13 +3686,13 @@
         <message name="IDS_TASK_MANAGER_SUBFRAME_PREFIX" desc="The prefix for a out-of-process-iframe row in the Task Manager (these processes are created when site isolation is enabled)">
           Subframe: <ph name="SUBFRAME_SITE">$1<ex>https://youtube.com/</ex></ph>
         </message>
-        <message name="IDS_TASK_MANAGER_SUBFRAME_INCOGNITO_PREFIX" desc="The prefix for an Incognito out-of-process-iframe process row in the Task Manager (these processes are created when site isolation is enabled, in incognito mode)">
+        <message name="IDS_TASK_MANAGER_SUBFRAME_INCOGNITO_PREFIX" desc="The prefix for an Incognito out-of-process-iframe process row in the Task Manager (these processes are created when site isolation is enabled, in Incognito mode)">
           Incognito Subframe: <ph name="SUBFRAME_SITE">$1<ex>https://youtube.com/</ex></ph>
         </message>
         <message name="IDS_TASK_MANAGER_PORTAL_PREFIX" desc="The prefix for a portal row in the Task Manager (these processes are created when portals is enabled)">
           Portal: <ph name="SUBFRAME_SITE">$1<ex>https://youtube.com/</ex></ph>
         </message>
-        <message name="IDS_TASK_MANAGER_PORTAL_INCOGNITO_PREFIX" desc="The prefix for an Incognito portal row in the Task Manager (these processes are created when portals is enabled, in incognito mode)">
+        <message name="IDS_TASK_MANAGER_PORTAL_INCOGNITO_PREFIX" desc="The prefix for an Incognito portal row in the Task Manager (these processes are created when portals is enabled, in Incognito mode)">
           Incognito Portal: <ph name="SUBFRAME_SITE">$1<ex>https://youtube.com/</ex></ph>
         </message>
         <message name="IDS_TASK_MANAGER_ARC_PREFIX" desc="The prefix for an ARC general process row">
@@ -3911,10 +3911,10 @@
       </message>
 
       <!-- Extension/App install dialog strings -->
-      <message name="IDS_EXTENSION_PROMPT_APP_CONNECT_FROM_INCOGNITO" desc="Shown when a website tries to connect to an app from incognito mode, prompting the user to allow or deny the request.">
+      <message name="IDS_EXTENSION_PROMPT_APP_CONNECT_FROM_INCOGNITO" desc="Shown when a website tries to connect to an app from Incognito mode, prompting the user to allow or deny the request.">
         <ph name="ORIGIN">$1<ex>http://www.google.com</ex></ph> wants to communicate with the app "<ph name="EXTENSION_NAME">$2<ex>Gmail</ex></ph>"
       </message>
-      <message name="IDS_EXTENSION_PROMPT_EXTENSION_CONNECT_FROM_INCOGNITO" desc="Shown when a website tries to connect to an extension from incognito mode, prompting the user to allow or deny the request.">
+      <message name="IDS_EXTENSION_PROMPT_EXTENSION_CONNECT_FROM_INCOGNITO" desc="Shown when a website tries to connect to an extension from Incognito mode, prompting the user to allow or deny the request.">
         <ph name="ORIGIN">$1<ex>http://www.google.com</ex></ph> wants to communicate with the extension "<ph name="EXTENSION_NAME">$2<ex>Gmail Checker</ex></ph>"
       </message>
       <message name="IDS_EXTENSION_PROMPT_WILL_HAVE_ACCESS_TO" desc="Second line in the content area of the extension or app installation prompt. Note that the exact wording is important. This should mean that the extension or app _can_ access the listed privileges, but not necessarily that it will or needs to.">
@@ -5968,7 +5968,7 @@
       </if>
 
       <!--Accessible name/action strings-->
-      <message name="IDS_ACCESSIBLE_INCOGNITO_WINDOW_TITLE_FORMAT" desc="The format for the accessible title of an incognito window">
+      <message name="IDS_ACCESSIBLE_INCOGNITO_WINDOW_TITLE_FORMAT" desc="The format for the accessible title of an Incognito window">
         <ph name="WINDOW_TITLE">$1</ph> (Incognito)
       </message>
       <message name="IDS_ACCESSIBLE_GUEST_WINDOW_TITLE_FORMAT" desc="The format for the accessible title of a guest mode window">
@@ -6087,16 +6087,16 @@
         New! Control your music, videos, and more.
       </message>
       <message name="IDS_INCOGNITOWINDOW_PROMO_0" desc="Variations option 1. Text shown on promotional UI appearing next to the App Menu button, which encourages users to use it.">
-        You can browse privately using an incognito window
+        You can browse privately using an Incognito window
       </message>
       <message name="IDS_INCOGNITOWINDOW_PROMO_1" desc="Variations option 2. Text shown on promotional UI appearing next to the App Menu button, which encourages users to use it.">
-        Use the web without saving your browsing history with an incognito window
+        Use the web without saving your browsing history with an Incognito window
       </message>
       <message name="IDS_INCOGNITOWINDOW_PROMO_2" desc="Variations option 3. Text shown on promotional UI appearing next to the App Menu button, which encourages users to use it.">
-        Using a shared computer? Try opening an incognito window.
+        Using a shared computer? Try opening an Incognito window.
       </message>
       <message name="IDS_INCOGNITOWINDOW_PROMO_3" desc="Variations option 4. Text shown on promotional UI appearing next to the App Menu button, which encourages users to use it.">
-        To browse privately, click the dots icon menu to open an incognito window
+        To browse privately, click the dots icon menu to open an Incognito window
       </message>
       <message name="IDS_NEWTAB_PROMO_0" desc="Variations option 1. Text shown on promotional UI appearing next to the New Tab button, which encourages users to use it.">
         Open a new tab with one click
@@ -8973,7 +8973,7 @@
         <message name="IDS_NEW_WINDOW_MAC" desc="The Mac menu item for opening a new window in the file menu.">
           New Window
         </message>
-        <message name="IDS_NEW_INCOGNITO_WINDOW_MAC" desc="The Mac menu item for opening a new incognito window in the file menu.">
+        <message name="IDS_NEW_INCOGNITO_WINDOW_MAC" desc="The Mac menu item for opening a new Incognito window in the file menu.">
           New Incognito Window
         </message>
         <message name="IDS_BACKGROUND_APPS_MAC" desc="The Mac submenu for opening Background Apps.">
@@ -9865,8 +9865,8 @@
         <message name="IDS_APP_LIST_NEW_WINDOW" desc="The text label of the New Window menu item">
           New window
         </message>
-        <message name="IDS_APP_LIST_NEW_INCOGNITO_WINDOW" desc="The text label of the New incognito window menu item">
-          New incognito window
+        <message name="IDS_APP_LIST_NEW_INCOGNITO_WINDOW" desc="The text label of the New Incognito window menu item">
+          New Incognito window
         </message>
         <message name="IDS_APP_LIST_OEM_DEFAULT_FOLDER_NAME" desc="The default name for OEM folders in the App Launcher">
           OEM folder
@@ -11024,7 +11024,7 @@
    </if>
 
    <!-- Incognito Profile Menu bubble. -->
-    <message name="IDS_INCOGNITO_PROFILE_MENU_TITLE" desc="The title of the incognito version of profile menu.">
+    <message name="IDS_INCOGNITO_PROFILE_MENU_TITLE" desc="The title of the Incognito version of profile menu.">
       Incognito
     </message>
     <message name="IDS_INCOGNITO_WINDOW_COUNT_MESSAGE" desc="The message showing the number of open incogntio windows. This is not used for zero windows.[ICU Syntax]">
@@ -11033,13 +11033,13 @@
         other {# open windows}
       }
     </message>
-    <message name="IDS_INCOGNITO_PROFILE_MENU_CREATE_SHORTCUT_BUTTON" desc="Label of the button to create a desktop shortcut for incognito mode.">
+    <message name="IDS_INCOGNITO_PROFILE_MENU_CREATE_SHORTCUT_BUTTON" desc="Label of the button to create a desktop shortcut for Incognito mode.">
       Create shortcut
     </message>
-    <message name="IDS_INCOGNITO_PROFILE_MENU_CLOSE_BUTTON" desc="The text of the button offering to close all incognito windows.">
+    <message name="IDS_INCOGNITO_PROFILE_MENU_CLOSE_BUTTON" desc="The text of the button offering to close all Incognito windows.">
       Exit Incognito
     </message>
-    <message name="IDS_INCOGNITO_PROFILE_MENU_CLOSE_BUTTON_NEW" desc="The text of the button offering to close all incognito windows.">
+    <message name="IDS_INCOGNITO_PROFILE_MENU_CLOSE_BUTTON_NEW" desc="The text of the button offering to close all Incognito windows.">
       Close Incognito
     </message>
 
diff --git a/chrome/app/google_chrome_strings.grd b/chrome/app/google_chrome_strings.grd
index 5bfe3d4..f574502 100644
--- a/chrome/app/google_chrome_strings.grd
+++ b/chrome/app/google_chrome_strings.grd
@@ -590,8 +590,8 @@
       </if>
 
       <!-- chrome://settings/extensions page -->
-      <message name="IDS_EXTENSIONS_INCOGNITO_WARNING" desc="Warns the user that Chrome cannot prevent extensions from recording history in incognito mode. Displayed in extensions management UI after an extension is selected to be run in incognito mode.">
-        Warning: Google Chrome cannot prevent extensions from recording your browsing history. To disable this extension in incognito mode, unselect this option.
+      <message name="IDS_EXTENSIONS_INCOGNITO_WARNING" desc="Warns the user that Chrome cannot prevent extensions from recording history in Incognito mode. Displayed in extensions management UI after an extension is selected to be run in Incognito mode.">
+        Warning: Google Chrome cannot prevent extensions from recording your browsing history. To disable this extension in Incognito mode, unselect this option.
       </message>
       <message name="IDS_EXTENSIONS_UNINSTALL" desc="The link for uninstalling extensions.">
         Remove from Chrome...
@@ -958,7 +958,7 @@
           <message name="IDS_CONTENT_CONTEXT_OPENLINKNEWTAB_INAPP" desc="The name of the command to open a link in a newly created browser tab when the user is in an app window">
             Open link in new Chrome &amp;tab
           </message>
-          <message name="IDS_CONTENT_CONTEXT_OPENLINKOFFTHERECORD_INAPP" desc="The name of the command to open a link in an incognito browser window when the user is in an app window">
+          <message name="IDS_CONTENT_CONTEXT_OPENLINKOFFTHERECORD_INAPP" desc="The name of the command to open a link in an Incognito browser window when the user is in an app window">
             Open link in Chrome inco&amp;gnito window
           </message>
         </if>
@@ -966,7 +966,7 @@
           <message name="IDS_CONTENT_CONTEXT_OPENLINKNEWTAB_INAPP" desc="In Title Case: The name of the command to open a link in a newly created browser tab when the user is in an app window">
             Open Link in New Chrome &amp;tab
           </message>
-          <message name="IDS_CONTENT_CONTEXT_OPENLINKOFFTHERECORD_INAPP" desc="In Title Case: The name of the command to open a link in an incognito browser window when the user is in an app window">
+          <message name="IDS_CONTENT_CONTEXT_OPENLINKOFFTHERECORD_INAPP" desc="In Title Case: The name of the command to open a link in an Incognito browser window when the user is in an app window">
             Open Link in Chrome Inco&amp;gnito Window
           </message>
         </if>
@@ -977,7 +977,7 @@
         <message name="IDS_UPDATE_RECOMMENDED_DIALOG_TITLE" desc="The window title for the Update Recommended dialog.">
           Relaunch Chrome
         </message>
-        <message name="IDS_UPDATE_RECOMMENDED" desc="The main text of the Update Recommended dialog with a count of open incognito windows.">
+        <message name="IDS_UPDATE_RECOMMENDED" desc="The main text of the Update Recommended dialog with a count of open Incognito windows.">
           {COUNT, plural,
            =0 {A new update for Chrome is available and will be applied as soon as you relaunch.}
            =1 {A new update for Chrome is available and will be applied as soon as you relaunch. Your Incognito window won't reopen.}
diff --git a/chrome/app/profiles_strings.grdp b/chrome/app/profiles_strings.grdp
index 8268e3886..b5b231e 100644
--- a/chrome/app/profiles_strings.grdp
+++ b/chrome/app/profiles_strings.grdp
@@ -23,20 +23,20 @@
         other {# open guest windows}
       }
   </message>
-  <message name="IDS_INCOGNITO_BUBBLE_ACCESSIBLE_TITLE" desc="Accessible title of the avatar menu, opened in incognito mode. The message also shows the number of open incognito windows if they are more than one. [ICU Syntax]">
+  <message name="IDS_INCOGNITO_BUBBLE_ACCESSIBLE_TITLE" desc="Accessible title of the avatar menu, opened in Incognito mode. The message also shows the number of open Incognito windows if they are more than one. [ICU Syntax]">
       {0, plural,
         =1 {Incognito}
-        other {# open incognito windows}
+        other {# open Incognito windows}
       }
   </message>
-  <message name="IDS_AVATAR_BUTTON_INCOGNITO" desc="Short label for the avatar button when the user is in incognito mode and the number of open incognito windows if there are more than one incognito window open. [ICU Syntax]" meaning="This window is browsing in incognito mode and there are more than one incognito windows open. The counter is not shown when there is only one incognito window open.">
+  <message name="IDS_AVATAR_BUTTON_INCOGNITO" desc="Short label for the avatar button when the user is in Incognito mode and the number of open Incognito windows if there are more than one Incognito window open. [ICU Syntax]" meaning="This window is browsing in Incognito mode and there are more than one Incognito windows open. The counter is not shown when there is only one Incognito window open.">
     {0, plural,
       =1 {Incognito}
       other {Incognito (#)}
     }
   </message>
-  <message name="IDS_AVATAR_BUTTON_INCOGNITO_TOOLTIP" desc="Tooltip for the avatar button when the user is in incognito mode.">
-    You're incognito
+  <message name="IDS_AVATAR_BUTTON_INCOGNITO_TOOLTIP" desc="Tooltip for the avatar button when the user is in Incognito mode.">
+    You're Incognito
   </message>
   <message name="IDS_AVATAR_BUTTON_SYNC_ERROR" desc="Short label for the avatar button when there are sync errors for the current profile." meaning="An error has occurred during sync">
     Error
@@ -618,7 +618,7 @@
     <message name="IDS_PROFILE_PICKER_PROFILE_MENU_CUSTOMIZE_TEXT" desc="Text of the customize button in profile card menu">
       Edit
     </message>
-    <message name="IDS_PROFILE_PICKER_PROFILE_MENU_INCOGNITO_TEXT" desc="Text of the incognito button in profile card menu">
+    <message name="IDS_PROFILE_PICKER_PROFILE_MENU_INCOGNITO_TEXT" desc="Text of the Incognito button in profile card menu">
       Incognito
     </message>
     <message name="IDS_PROFILE_PICKER_ASK_ON_STARTUP" desc="Text for the checkbox on the profile picker main view">
diff --git a/chrome/app/settings_strings.grdp b/chrome/app/settings_strings.grdp
index 38d0edb..f62bb774 100644
--- a/chrome/app/settings_strings.grdp
+++ b/chrome/app/settings_strings.grdp
@@ -2268,14 +2268,14 @@
   <message name="IDS_SETTINGS_COOKIES_ALLOW_ALL_BULLET_TWO" desc="Description for the first bullet of allow all cookies radio toggle">
     Sites can use cookies to see your browsing activity across different sites, for example, to personalize ads
   </message>
-  <message name="IDS_SETTINGS_COOKIES_BLOCK_THIRD_PARTY_INCOGNITO" desc="Label for block third party cookies in incognito radio toggle">
+  <message name="IDS_SETTINGS_COOKIES_BLOCK_THIRD_PARTY_INCOGNITO" desc="Label for block third party cookies in Incognito radio toggle">
     Block third-party cookies in Incognito
   </message>
-  <message name="IDS_SETTINGS_COOKIES_BLOCK_THIRD_PARTY_INCOGNITO_BULLET_ONE" desc="Description for the first bullet of block third party cookies in incognito radio toggle">
+  <message name="IDS_SETTINGS_COOKIES_BLOCK_THIRD_PARTY_INCOGNITO_BULLET_ONE" desc="Description for the first bullet of block third party cookies in Incognito radio toggle">
     Sites can use cookies to improve your browsing experience, for example, to keep you signed in or to remember items in your shopping cart
   </message>
-  <message name="IDS_SETTINGS_COOKIES_BLOCK_THIRD_PARTY_INCOGNITO_BULLET_TWO" desc="Description for the first bullet of block third party cookies in incognito radio toggle">
-    While in incognito, sites can't use your cookies to see your browsing activity across different sites, for example, to personalize ads. Features on some sites may break.
+  <message name="IDS_SETTINGS_COOKIES_BLOCK_THIRD_PARTY_INCOGNITO_BULLET_TWO" desc="Description for the first bullet of block third party cookies in Incognito radio toggle">
+    While in Incognito, sites can't use your cookies to see your browsing activity across different sites, for example, to personalize ads. Features on some sites may break.
   </message>
   <message name="IDS_SETTINGS_COOKIES_BLOCK_THIRD_PARTY" desc="Label for block third party cookies radio toggle">
     Block third-party cookies
@@ -2613,7 +2613,7 @@
   <message name="IDS_SETTINGS_SITE_SETTINGS_COOKIES_BLOCK_THIRD_PARTY" desc="The sub-label for links to manage cookies when the current cookie setting is to block third party cookies.">
     Third-party cookies are blocked
   </message>
-  <message name="IDS_SETTINGS_SITE_SETTINGS_COOKIES_BLOCK_THIRD_PARTY_INCOGNITO" desc="The sub-label for links to manage cookies when the current cookie setting is to block third party cookies in incognito.">
+  <message name="IDS_SETTINGS_SITE_SETTINGS_COOKIES_BLOCK_THIRD_PARTY_INCOGNITO" desc="The sub-label for links to manage cookies when the current cookie setting is to block third party cookies in Incognito.">
     Third-party cookies are blocked in Incognito mode
   </message>
   <message name="IDS_SETTINGS_SITE_SETTINGS_COOKIES_ALLOW_SITES" desc="The 'allow cookies' label in site settings.">
@@ -2971,10 +2971,10 @@
   <message name="IDS_SETTINGS_SITE_SETTINGS_REMOVE" desc="A label for the action of removing (deleting) a certain handler.">
     Remove
   </message>
-  <message name="IDS_SETTINGS_SITE_SETTINGS_INCOGNITO_ONLY" desc="A label for the checkbox to make the rule apply to the current incognito session only.">
-    Current incognito session only
+  <message name="IDS_SETTINGS_SITE_SETTINGS_INCOGNITO_ONLY" desc="A label for the checkbox to make the rule apply to the current Incognito session only.">
+    Current Incognito session only
   </message>
-  <message name="IDS_SETTINGS_SITE_SETTINGS_INCOGNITO_SITE_EXCEPTION_DESC" desc="The text displayed in order to explain to a user that a content setting only applies to the current incognito session.">
+  <message name="IDS_SETTINGS_SITE_SETTINGS_INCOGNITO_SITE_EXCEPTION_DESC" desc="The text displayed in order to explain to a user that a content setting only applies to the current Incognito session.">
     This custom setting will be removed when you close all your Incognito windows
   </message>
   <message name="IDS_SETTINGS_SITE_SETTINGS_NO_ZOOMED_SITES" desc="A label explaining that no sites have a configured zoom in/out value.">
diff --git a/chrome/app/vr_strings.grdp b/chrome/app/vr_strings.grdp
index 5059a31..093162f 100644
--- a/chrome/app/vr_strings.grdp
+++ b/chrome/app/vr_strings.grdp
@@ -142,19 +142,19 @@
   <message name="IDS_VR_BUTTON_APP_REPOSITION" desc="This is the label for the app button while in reposition mode">
     Finish
   </message>
-  <message name="IDS_VR_MENU_NEW_INCOGNITO_TAB" desc="Menu item for opening a new incognito tab that facilitates pseudononymous browsing. [CHAR-LIMIT=27]">
-    New incognito tab
+  <message name="IDS_VR_MENU_NEW_INCOGNITO_TAB" desc="Menu item for opening a new Incognito tab that facilitates pseudononymous browsing. [CHAR-LIMIT=27]">
+    New Incognito tab
   </message>
   <message name="IDS_VR_MENU_PREFERENCES" desc="Menu item for opening browser preferences. [CHAR-LIMIT=27]">
     Settings
   </message>
-  <message name="IDS_VR_MENU_CLOSE_INCOGNITO_TABS" desc="Menu item for closing all open incognito tabs. [CHAR-LIMIT=27]">
-    Close incognito tabs
+  <message name="IDS_VR_MENU_CLOSE_INCOGNITO_TABS" desc="Menu item for closing all open Incognito tabs. [CHAR-LIMIT=27]">
+    Close Incognito tabs
   </message>
   <message name="IDS_VR_TABS_BUTTON_REGULAR" desc="Text on button for selecting regular tabs">
     Tabs
   </message>
-  <message name="IDS_VR_TABS_BUTTON_INCOGNITO" desc="Text on button for selecting incognito tabs">
+  <message name="IDS_VR_TABS_BUTTON_INCOGNITO" desc="Text on button for selecting Incognito tabs">
     Incognito
   </message>
 </grit-part>
diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn
index 9f82c255..f455bf9 100644
--- a/chrome/browser/BUILD.gn
+++ b/chrome/browser/BUILD.gn
@@ -2435,6 +2435,7 @@
     ]
     if (!is_official_build) {
       deps += [
+        "//chromeos/components/demo_mode_app_ui",
         "//chromeos/components/file_manager:file_manager_ui",
         "//chromeos/components/file_manager/mojom",
         "//chromeos/components/telemetry_extension_ui",
diff --git a/chrome/browser/android/feed/v2/web_feed_bridge.cc b/chrome/browser/android/feed/v2/web_feed_bridge.cc
index e375f08..c07a106 100644
--- a/chrome/browser/android/feed/v2/web_feed_bridge.cc
+++ b/chrome/browser/android/feed/v2/web_feed_bridge.cc
@@ -287,4 +287,9 @@
       std::move(callback), &TaskTracker());
 }
 
+static jboolean JNI_WebFeedBridge_IsWebFeedSubscriber(JNIEnv*) {
+  WebFeedSubscriptions* subscriptions = GetSubscriptions();
+  return subscriptions && subscriptions->IsWebFeedSubscriber();
+}
+
 }  // namespace feed
diff --git a/chrome/browser/android/webapps/launchpad/BUILD.gn b/chrome/browser/android/webapps/launchpad/BUILD.gn
index 684835e8..2c210468 100644
--- a/chrome/browser/android/webapps/launchpad/BUILD.gn
+++ b/chrome/browser/android/webapps/launchpad/BUILD.gn
@@ -11,6 +11,11 @@
     "java/src/org/chromium/chrome/browser/webapps/launchpad/AppManagementMenuCoordinator.java",
     "java/src/org/chromium/chrome/browser/webapps/launchpad/AppManagementMenuHeaderProperties.java",
     "java/src/org/chromium/chrome/browser/webapps/launchpad/AppManagementMenuHeaderViewBinder.java",
+    "java/src/org/chromium/chrome/browser/webapps/launchpad/AppManagementMenuPermissionsCoordinator.java",
+    "java/src/org/chromium/chrome/browser/webapps/launchpad/AppManagementMenuPermissionsMediator.java",
+    "java/src/org/chromium/chrome/browser/webapps/launchpad/AppManagementMenuPermissionsProperties.java",
+    "java/src/org/chromium/chrome/browser/webapps/launchpad/AppManagementMenuPermissionsView.java",
+    "java/src/org/chromium/chrome/browser/webapps/launchpad/AppManagementMenuPermissionsViewBinder.java",
     "java/src/org/chromium/chrome/browser/webapps/launchpad/LaunchpadCoordinator.java",
     "java/src/org/chromium/chrome/browser/webapps/launchpad/LaunchpadItem.java",
     "java/src/org/chromium/chrome/browser/webapps/launchpad/LaunchpadPage.java",
@@ -20,8 +25,12 @@
     ":java_resources",
     "//base:base_java",
     "//chrome/android/webapk/libs/client:client_java",
+    "//chrome/browser/profiles/android:java",
     "//chrome/browser/ui/android/native_page:java",
+    "//components/browser_ui/settings/android:java",
+    "//components/browser_ui/site_settings/android:java",
     "//components/browser_ui/widget/android:java",
+    "//components/content_settings/android:content_settings_enums_java",
     "//components/embedder_support/android:util_java",
     "//third_party/androidx:androidx_annotation_annotation_java",
     "//third_party/androidx:androidx_appcompat_appcompat_java",
@@ -33,7 +42,12 @@
 
 android_resources("java_resources") {
   sources = [
+    "java/res/drawable/gm_filled_location_off_24.xml",
+    "java/res/drawable/gm_filled_mic_off_24.xml",
+    "java/res/drawable/gm_filled_notifications_off_24.xml",
+    "java/res/drawable/gm_filled_videocam_off_24.xml",
     "java/res/layout/launchpad_app_menu_header.xml",
+    "java/res/layout/launchpad_app_menu_permissions.xml",
     "java/res/layout/launchpad_menu_dialog_layout.xml",
     "java/res/layout/launchpad_menu_dialog_layout.xml",
     "java/res/layout/launchpad_page_layout.xml",
@@ -45,6 +59,7 @@
   deps = [
     "//chrome/browser/ui/android/strings:ui_strings_grd",
     "//components/browser_ui/widget/android:java_resources",
+    "//components/permissions/android:java_resources",
   ]
 }
 
@@ -58,14 +73,19 @@
     "//base:base_java_test_support",
     "//chrome/android:chrome_java",
     "//chrome/browser/flags:java",
+    "//chrome/browser/profiles/android:java",
     "//chrome/browser/tab:java",
     "//chrome/browser/ui/messages/android:java",
     "//chrome/test/android:chrome_java_test_support",
+    "//components/browser_ui/site_settings/android:java",
     "//components/browser_ui/widget/android:java",
+    "//components/content_settings/android:content_settings_enums_java",
     "//components/embedder_support/android:util_java",
     "//content/public/test/android:content_java_test_support",
     "//third_party/android_deps:espresso_java",
+    "//third_party/android_support_test_runner:runner_java",
     "//third_party/androidx:androidx_appcompat_appcompat_java",
+    "//third_party/androidx:androidx_appcompat_appcompat_resources_java",
     "//third_party/androidx:androidx_recyclerview_recyclerview_java",
     "//third_party/androidx:androidx_test_runner_java",
     "//third_party/hamcrest:hamcrest_java",
@@ -79,14 +99,22 @@
   bypass_platform_checks = true
   testonly = true
 
-  sources = [ "java/src/org/chromium/chrome/browser/webapps/launchpad/LaunchpadCoordinatorTest.java" ]
+  sources = [
+    "java/src/org/chromium/chrome/browser/webapps/launchpad/AppManagementMenuPermissionsCoordinatorTest.java",
+    "java/src/org/chromium/chrome/browser/webapps/launchpad/LaunchpadCoordinatorTest.java",
+  ]
 
   deps = [
     ":java",
     ":java_resources",
     "//base:base_java",
+    "//base:base_java_test_support",
     "//base:base_junit_test_support",
+    "//chrome/browser/profiles/android:java",
     "//chrome/test/android:chrome_java_test_support",
+    "//components/browser_ui/site_settings/android:java",
+    "//components/content_settings/android:content_settings_enums_java",
+    "//components/embedder_support/android:browser_context_java",
     "//third_party/android_deps:robolectric_all_java",
     "//third_party/androidx:androidx_appcompat_appcompat_java",
     "//third_party/junit",
diff --git a/chrome/browser/android/webapps/launchpad/java/res/drawable/gm_filled_location_off_24.xml b/chrome/browser/android/webapps/launchpad/java/res/drawable/gm_filled_location_off_24.xml
new file mode 100644
index 0000000..ed2ae134
--- /dev/null
+++ b/chrome/browser/android/webapps/launchpad/java/res/drawable/gm_filled_location_off_24.xml
@@ -0,0 +1,15 @@
+<?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.-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportWidth="24"
+    android:viewportHeight="24"
+    android:tint="?attr/colorControlNormal">
+  <path
+      android:fillColor="@android:color/white"
+      android:pathData="M12,6.88c1.38,0 2.5,1.12 2.5,2.5c0,0.64 -0.25,1.21 -0.64,1.65l3.38,3.38C18.24,12.95 19,11.39 19,9.13c0,-3.87 -3.13,-7 -7,-7c-1.94,0 -3.7,0.79 -4.97,2.07l3.32,3.32C10.79,7.13 11.36,6.88 12,6.88z"/>
+  <path
+      android:fillColor="@android:color/white"
+      android:pathData="M2.81,2.81L1.39,4.22l3.72,3.72C5.04,8.33 5,8.72 5,9.13c0,5.34 4.21,6.79 6.03,12.28c0.14,0.42 0.52,0.72 0.97,0.72s0.83,-0.3 0.97,-0.72c0.49,-1.49 1.17,-2.68 1.88,-3.74l4.93,4.93l1.41,-1.41L2.81,2.81z"/>
+</vector>
diff --git a/chrome/browser/android/webapps/launchpad/java/res/drawable/gm_filled_mic_off_24.xml b/chrome/browser/android/webapps/launchpad/java/res/drawable/gm_filled_mic_off_24.xml
new file mode 100644
index 0000000..233628c
--- /dev/null
+++ b/chrome/browser/android/webapps/launchpad/java/res/drawable/gm_filled_mic_off_24.xml
@@ -0,0 +1,12 @@
+<?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.-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportWidth="24"
+    android:viewportHeight="24"
+    android:tint="?attr/colorControlNormal">
+  <path
+      android:fillColor="@android:color/white"
+      android:pathData="M19,11h-2c0,0.91 -0.25,1.76 -0.68,2.49l1.45,1.45C18.54,13.82 19,12.47 19,11zM2.81,2.81L1.39,4.22l11.66,11.66C12.71,15.96 12.36,16 12,16c-2.76,0 -5,-2.24 -5,-5H5c0,3.53 2.61,6.43 6,6.92V21h2v-3.08c0.57,-0.08 1.12,-0.24 1.64,-0.45l5.14,5.14l1.41,-1.41L2.81,2.81zM15,11V5c0,-1.66 -1.34,-3 -3,-3S9,3.34 9,5v1.17l5.81,5.81C14.92,11.67 15,11.35 15,11z"/>
+</vector>
diff --git a/chrome/browser/android/webapps/launchpad/java/res/drawable/gm_filled_notifications_off_24.xml b/chrome/browser/android/webapps/launchpad/java/res/drawable/gm_filled_notifications_off_24.xml
new file mode 100644
index 0000000..2f659c3
--- /dev/null
+++ b/chrome/browser/android/webapps/launchpad/java/res/drawable/gm_filled_notifications_off_24.xml
@@ -0,0 +1,12 @@
+<?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.-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportWidth="24"
+    android:viewportHeight="24"
+    android:tint="?attr/colorControlNormal">
+  <path
+      android:fillColor="@android:color/white"
+      android:pathData="M18,10c0,-2.79 -1.91,-5.14 -4.5,-5.8V3.5C13.5,2.67 12.83,2 12,2s-1.5,0.67 -1.5,1.5v0.7C9.64,4.42 8.87,4.84 8.21,5.38L18,15.17V10zM12,22c1.1,0 2,-0.9 2,-2h-4C10,21.1 10.9,22 12,22zM2.81,2.81L1.39,4.22L6.1,8.93C6.04,9.28 6,9.63 6,10v7H4v2h12.17l3.61,3.61l1.41,-1.41L2.81,2.81z"/>
+</vector>
diff --git a/chrome/browser/android/webapps/launchpad/java/res/drawable/gm_filled_videocam_off_24.xml b/chrome/browser/android/webapps/launchpad/java/res/drawable/gm_filled_videocam_off_24.xml
new file mode 100644
index 0000000..670cfa1
--- /dev/null
+++ b/chrome/browser/android/webapps/launchpad/java/res/drawable/gm_filled_videocam_off_24.xml
@@ -0,0 +1,15 @@
+<?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.-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportWidth="24"
+    android:viewportHeight="24"
+    android:tint="?attr/colorControlNormal">
+  <path
+      android:fillColor="@android:color/white"
+      android:pathData="M18,15.17v-1.65l4,3.98v-11l-4,3.98V6c0,-1.1 -0.9,-2 -2,-2H6.83L18,15.17z"/>
+  <path
+      android:fillColor="@android:color/white"
+      android:pathData="M18,18L2.1,2.1L0.69,3.51l1.56,1.56C2.09,5.35 2,5.66 2,6v12c0,1.1 0.9,2 2,2h12c0.34,0 0.65,-0.09 0.93,-0.24l3.56,3.56l1.41,-1.41L18,18L18,18z"/>
+</vector>
diff --git a/chrome/browser/android/webapps/launchpad/java/res/layout/launchpad_app_menu_permissions.xml b/chrome/browser/android/webapps/launchpad/java/res/layout/launchpad_app_menu_permissions.xml
new file mode 100644
index 0000000..75d7653
--- /dev/null
+++ b/chrome/browser/android/webapps/launchpad/java/res/layout/launchpad_app_menu_permissions.xml
@@ -0,0 +1,56 @@
+<?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. -->
+
+<org.chromium.chrome.browser.webapps.launchpad.AppManagementMenuPermissionsView
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:paddingTop="8dp"
+    android:paddingBottom="8dp"
+    android:paddingStart="16dp"
+    android:paddingEnd="16dp"
+    android:orientation="horizontal">
+
+  <org.chromium.ui.widget.ChromeImageButton
+      android:id="@+id/notifications_button"
+      android:layout_width="wrap_content"
+      android:layout_height="wrap_content"
+      android:background="@null"
+      android:src="@drawable/gm_filled_notifications_24"
+      android:contentDescription="@string/push_notifications_permission_title" />
+  <Space
+      android:layout_width="wrap_content"
+      android:layout_height="wrap_content"
+      android:layout_weight="1" />
+  <org.chromium.ui.widget.ChromeImageButton
+      android:id="@+id/mic_button"
+      android:layout_width="wrap_content"
+      android:layout_height="wrap_content"
+      android:background="@null"
+      android:src="@drawable/gm_filled_mic_24"
+      android:contentDescription="@string/website_settings_use_mic" />
+  <Space
+      android:layout_width="wrap_content"
+      android:layout_height="wrap_content"
+      android:layout_weight="1" />
+  <org.chromium.ui.widget.ChromeImageButton
+      android:id="@+id/camera_button"
+      android:layout_width="wrap_content"
+      android:layout_height="wrap_content"
+      android:background="@null"
+      android:src="@drawable/gm_filled_videocam_24"
+      android:contentDescription="@string/website_settings_use_camera" />
+  <Space
+      android:layout_width="wrap_content"
+      android:layout_height="wrap_content"
+      android:layout_weight="1" />
+  <org.chromium.ui.widget.ChromeImageButton
+      android:id="@+id/location_button"
+      android:layout_width="wrap_content"
+      android:layout_height="wrap_content"
+      android:background="@null"
+      android:src="@drawable/gm_filled_location_on_24"
+      android:contentDescription="@string/website_settings_device_location" />
+</org.chromium.chrome.browser.webapps.launchpad.AppManagementMenuPermissionsView>
diff --git a/chrome/browser/android/webapps/launchpad/java/res/layout/launchpad_menu_dialog_layout.xml b/chrome/browser/android/webapps/launchpad/java/res/layout/launchpad_menu_dialog_layout.xml
index 67a13b21..8f4e8c7 100644
--- a/chrome/browser/android/webapps/launchpad/java/res/layout/launchpad_menu_dialog_layout.xml
+++ b/chrome/browser/android/webapps/launchpad/java/res/layout/launchpad_menu_dialog_layout.xml
@@ -17,6 +17,10 @@
       android:id="@+id/dialog_header"
       layout="@layout/launchpad_app_menu_header" />
 
+  <include
+      android:id="@+id/permissions"
+      layout="@layout/launchpad_app_menu_permissions" />
+
   <View
       style="@style/HorizontalDivider"/>
 
diff --git a/chrome/browser/android/webapps/launchpad/java/src/org/chromium/chrome/browser/webapps/launchpad/AppManagementMenuCoordinator.java b/chrome/browser/android/webapps/launchpad/java/src/org/chromium/chrome/browser/webapps/launchpad/AppManagementMenuCoordinator.java
index 370d0969d..f3b5010 100644
--- a/chrome/browser/android/webapps/launchpad/java/src/org/chromium/chrome/browser/webapps/launchpad/AppManagementMenuCoordinator.java
+++ b/chrome/browser/android/webapps/launchpad/java/src/org/chromium/chrome/browser/webapps/launchpad/AppManagementMenuCoordinator.java
@@ -23,6 +23,8 @@
     private final Supplier<ModalDialogManager> mModalDialogManagerSupplier;
     private PropertyModel mDialogModel;
 
+    private AppManagementMenuPermissionsCoordinator mPermissionsCoordinator;
+
     AppManagementMenuCoordinator(
             Context context, Supplier<ModalDialogManager> modalDialogManagerSupplier) {
         mContext = context;
@@ -40,6 +42,10 @@
     @Override
     public void onDismiss(PropertyModel model, int dismissalCause) {
         mDialogModel = null;
+        if (mPermissionsCoordinator != null) {
+            mPermissionsCoordinator.destroy();
+            mPermissionsCoordinator = null;
+        }
     }
 
     void show(LaunchpadItem item) {
@@ -62,6 +68,9 @@
         PropertyModelChangeProcessor.create(
                 headerModel, headerView, new AppManagementMenuHeaderViewBinder());
 
+        mPermissionsCoordinator = new AppManagementMenuPermissionsCoordinator(
+                (AppManagementMenuPermissionsView) dialogView.findViewById(R.id.permissions), item);
+
         return dialogView;
     }
 }
diff --git a/chrome/browser/android/webapps/launchpad/java/src/org/chromium/chrome/browser/webapps/launchpad/AppManagementMenuPermissionsCoordinator.java b/chrome/browser/android/webapps/launchpad/java/src/org/chromium/chrome/browser/webapps/launchpad/AppManagementMenuPermissionsCoordinator.java
new file mode 100644
index 0000000..ccb3d2e
--- /dev/null
+++ b/chrome/browser/android/webapps/launchpad/java/src/org/chromium/chrome/browser/webapps/launchpad/AppManagementMenuPermissionsCoordinator.java
@@ -0,0 +1,40 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.chrome.browser.webapps.launchpad;
+
+import androidx.annotation.VisibleForTesting;
+
+import org.chromium.ui.modelutil.PropertyModelChangeProcessor;
+
+/**
+ * Coordinator for displaying the selected app's site permission in the management menu.
+ */
+class AppManagementMenuPermissionsCoordinator {
+    private final AppManagementMenuPermissionsView mView;
+    AppManagementMenuPermissionsMediator mMediator;
+
+    /**
+     * Creates a new AppManagementMenuPermissionsCoordinator.
+     * @param view The associated AppManagementMenuPermissionsView..
+     * @param item The LaunchpadItem that are displaying in the management menu.
+     */
+    AppManagementMenuPermissionsCoordinator(
+            AppManagementMenuPermissionsView view, LaunchpadItem item) {
+        mView = view;
+
+        mMediator = new AppManagementMenuPermissionsMediator(item.url);
+        PropertyModelChangeProcessor.create(
+                mMediator.getModel(), mView, AppManagementMenuPermissionsViewBinder::bind);
+    }
+
+    @VisibleForTesting
+    AppManagementMenuPermissionsMediator getMediatorForTesting() {
+        return mMediator;
+    }
+
+    void destroy() {
+        mMediator = null;
+    }
+}
diff --git a/chrome/browser/android/webapps/launchpad/java/src/org/chromium/chrome/browser/webapps/launchpad/AppManagementMenuPermissionsCoordinatorTest.java b/chrome/browser/android/webapps/launchpad/java/src/org/chromium/chrome/browser/webapps/launchpad/AppManagementMenuPermissionsCoordinatorTest.java
new file mode 100644
index 0000000..a429b353
--- /dev/null
+++ b/chrome/browser/android/webapps/launchpad/java/src/org/chromium/chrome/browser/webapps/launchpad/AppManagementMenuPermissionsCoordinatorTest.java
@@ -0,0 +1,132 @@
+// 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.webapps.launchpad;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.app.Activity;
+import android.view.LayoutInflater;
+
+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.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
+import org.robolectric.Robolectric;
+import org.robolectric.annotation.Config;
+
+import org.chromium.base.test.BaseRobolectricTestRunner;
+import org.chromium.base.test.util.JniMocker;
+import org.chromium.chrome.browser.profiles.Profile;
+import org.chromium.components.browser_ui.site_settings.WebsitePreferenceBridge;
+import org.chromium.components.browser_ui.site_settings.WebsitePreferenceBridgeJni;
+import org.chromium.components.content_settings.ContentSettingValues;
+import org.chromium.components.content_settings.ContentSettingsType;
+import org.chromium.components.embedder_support.browser_context.BrowserContextHandle;
+import org.chromium.ui.modelutil.PropertyModel;
+
+/**
+ * Tests for {@link AppManagementMenuPermissionsCoordinator}.
+ */
+@RunWith(BaseRobolectricTestRunner.class)
+@Config(manifest = Config.NONE)
+public class AppManagementMenuPermissionsCoordinatorTest {
+    private static final String APP_PACKAGE_NAME = "package.name";
+    private static final String APP_SHORT_NAME = "App";
+    private static final String APP_NAME = "App Name";
+    private static final String APP_URL = "https://example.com/";
+
+    private static final LaunchpadItem MOCK_ITEM =
+            new LaunchpadItem(APP_PACKAGE_NAME, APP_SHORT_NAME, APP_NAME, APP_URL, null);
+
+    @Mock
+    Activity mActivity;
+
+    @Rule
+    public final MockitoRule mMockitoRule = MockitoJUnit.rule();
+
+    @Rule
+    public JniMocker mocker = new JniMocker();
+
+    @Mock
+    WebsitePreferenceBridge.Natives mWebsitePreferenceBridgeJniMock;
+
+    private AppManagementMenuPermissionsCoordinator mCoordinator;
+    private AppManagementMenuPermissionsView mView;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+
+        mocker.mock(WebsitePreferenceBridgeJni.TEST_HOOKS, mWebsitePreferenceBridgeJniMock);
+
+        Profile.setLastUsedProfileForTesting(mock(Profile.class));
+
+        setPermission(ContentSettingsType.NOTIFICATIONS, ContentSettingValues.ASK);
+        setPermission(ContentSettingsType.MEDIASTREAM_MIC, ContentSettingValues.ASK);
+        setPermission(ContentSettingsType.MEDIASTREAM_CAMERA, ContentSettingValues.ASK);
+        setPermission(ContentSettingsType.GEOLOCATION, ContentSettingValues.ASK);
+
+        mActivity = Robolectric.buildActivity(Activity.class).setup().get();
+        mView = (AppManagementMenuPermissionsView) LayoutInflater.from(mActivity).inflate(
+                R.layout.launchpad_app_menu_permissions, null);
+        mCoordinator = new AppManagementMenuPermissionsCoordinator(mView, MOCK_ITEM);
+    }
+
+    private void setPermission(@ContentSettingsType int type, @ContentSettingValues int value) {
+        when(mWebsitePreferenceBridgeJniMock.getSettingForOrigin(
+                     any(BrowserContextHandle.class), eq(type), anyString(), anyString()))
+                .thenReturn(value);
+    }
+
+    @Test
+    public void testMediatorInitialization() {
+        setPermission(ContentSettingsType.NOTIFICATIONS, ContentSettingValues.BLOCK);
+        setPermission(ContentSettingsType.MEDIASTREAM_MIC, ContentSettingValues.ALLOW);
+        setPermission(ContentSettingsType.MEDIASTREAM_CAMERA, ContentSettingValues.ASK);
+        setPermission(ContentSettingsType.GEOLOCATION, ContentSettingValues.ASK);
+
+        AppManagementMenuPermissionsMediator mediator =
+                new AppManagementMenuPermissionsMediator(APP_URL);
+        PropertyModel model = mediator.getModel();
+        assertEquals(ContentSettingValues.BLOCK,
+                model.get(AppManagementMenuPermissionsProperties.NOTIFICATIONS));
+        assertEquals(
+                ContentSettingValues.ALLOW, model.get(AppManagementMenuPermissionsProperties.MIC));
+        assertEquals(
+                ContentSettingValues.ASK, model.get(AppManagementMenuPermissionsProperties.CAMERA));
+        assertEquals(ContentSettingValues.ASK,
+                model.get(AppManagementMenuPermissionsProperties.LOCATION));
+        assertNotNull(model.get(AppManagementMenuPermissionsProperties.ON_CLICK));
+    }
+
+    @Test
+    public void testClickListener() {
+        AppManagementMenuPermissionsMediator mediator = mCoordinator.getMediatorForTesting();
+        AppManagementMenuPermissionsView.OnButtonClickListener mockPermissionButtonListener =
+                mock(AppManagementMenuPermissionsView.OnButtonClickListener.class);
+        mediator.getModel().set(
+                AppManagementMenuPermissionsProperties.ON_CLICK, mockPermissionButtonListener);
+
+        mView.findViewById(R.id.location_button).callOnClick();
+        verify(mockPermissionButtonListener, times(1))
+                .onButtonClick(AppManagementMenuPermissionsProperties.LOCATION);
+
+        mView.findViewById(R.id.camera_button).callOnClick();
+        verify(mockPermissionButtonListener, times(1))
+                .onButtonClick(AppManagementMenuPermissionsProperties.CAMERA);
+    }
+}
diff --git a/chrome/browser/android/webapps/launchpad/java/src/org/chromium/chrome/browser/webapps/launchpad/AppManagementMenuPermissionsMediator.java b/chrome/browser/android/webapps/launchpad/java/src/org/chromium/chrome/browser/webapps/launchpad/AppManagementMenuPermissionsMediator.java
new file mode 100644
index 0000000..d04da52
--- /dev/null
+++ b/chrome/browser/android/webapps/launchpad/java/src/org/chromium/chrome/browser/webapps/launchpad/AppManagementMenuPermissionsMediator.java
@@ -0,0 +1,75 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.chrome.browser.webapps.launchpad;
+
+import org.chromium.chrome.browser.profiles.Profile;
+import org.chromium.components.browser_ui.site_settings.PermissionInfo;
+import org.chromium.components.content_settings.ContentSettingValues;
+import org.chromium.components.content_settings.ContentSettingsType;
+import org.chromium.ui.modelutil.PropertyKey;
+import org.chromium.ui.modelutil.PropertyModel;
+
+/**
+ * Mediator class for fetching and updating app's permissions in Launchpad management menu.
+ */
+class AppManagementMenuPermissionsMediator {
+    private final String mUrl;
+    private final Profile mProfile;
+    private PropertyModel mModel;
+
+    /**
+     * Creates a new AppManagementMenuPermissionsMediator.
+     * @param url The launch url of the WebAPK that is showing in the management menu.
+     */
+    AppManagementMenuPermissionsMediator(String url) {
+        mUrl = url;
+        mProfile = Profile.getLastUsedRegularProfile();
+        mModel = buildModel();
+    }
+
+    PropertyModel getModel() {
+        return mModel;
+    }
+
+    private PropertyModel buildModel() {
+        return new PropertyModel.Builder(AppManagementMenuPermissionsProperties.ALL_KEYS)
+                .with(AppManagementMenuPermissionsProperties.NOTIFICATIONS,
+                        getContentSetting(toContentSettingsType(
+                                AppManagementMenuPermissionsProperties.NOTIFICATIONS)))
+                .with(AppManagementMenuPermissionsProperties.MIC,
+                        getContentSetting(
+                                toContentSettingsType(AppManagementMenuPermissionsProperties.MIC)))
+                .with(AppManagementMenuPermissionsProperties.CAMERA,
+                        getContentSetting(toContentSettingsType(
+                                AppManagementMenuPermissionsProperties.CAMERA)))
+                .with(AppManagementMenuPermissionsProperties.LOCATION,
+                        getContentSetting(toContentSettingsType(
+                                AppManagementMenuPermissionsProperties.LOCATION)))
+                .with(AppManagementMenuPermissionsProperties.ON_CLICK,
+                        (propertyKey) -> onButtonClick(propertyKey))
+                .build();
+    }
+
+    private void onButtonClick(PropertyModel.WritableIntPropertyKey key) {}
+
+    private @ContentSettingValues int getContentSetting(@ContentSettingsType int type) {
+        PermissionInfo permissionInfo = new PermissionInfo(type, mUrl, null, false);
+        return permissionInfo.getContentSetting(mProfile);
+    }
+
+    private @ContentSettingsType int toContentSettingsType(PropertyKey key) {
+        if (key == AppManagementMenuPermissionsProperties.NOTIFICATIONS) {
+            return ContentSettingsType.NOTIFICATIONS;
+        } else if (key == AppManagementMenuPermissionsProperties.MIC) {
+            return ContentSettingsType.MEDIASTREAM_MIC;
+        } else if (key == AppManagementMenuPermissionsProperties.CAMERA) {
+            return ContentSettingsType.MEDIASTREAM_CAMERA;
+        } else if (key == AppManagementMenuPermissionsProperties.LOCATION) {
+            return ContentSettingsType.GEOLOCATION;
+        }
+        assert false;
+        return ContentSettingsType.DEFAULT;
+    }
+}
diff --git a/chrome/browser/android/webapps/launchpad/java/src/org/chromium/chrome/browser/webapps/launchpad/AppManagementMenuPermissionsProperties.java b/chrome/browser/android/webapps/launchpad/java/src/org/chromium/chrome/browser/webapps/launchpad/AppManagementMenuPermissionsProperties.java
new file mode 100644
index 0000000..06941ec
--- /dev/null
+++ b/chrome/browser/android/webapps/launchpad/java/src/org/chromium/chrome/browser/webapps/launchpad/AppManagementMenuPermissionsProperties.java
@@ -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.
+
+package org.chromium.chrome.browser.webapps.launchpad;
+
+import org.chromium.ui.modelutil.PropertyKey;
+import org.chromium.ui.modelutil.PropertyModel.WritableIntPropertyKey;
+import org.chromium.ui.modelutil.PropertyModel.WritableObjectPropertyKey;
+
+/**
+ * Properties for Launchpad app management menu's app permissions.
+ */
+class AppManagementMenuPermissionsProperties {
+    public static final WritableIntPropertyKey NOTIFICATIONS = new WritableIntPropertyKey();
+    public static final WritableIntPropertyKey MIC = new WritableIntPropertyKey();
+    public static final WritableIntPropertyKey CAMERA = new WritableIntPropertyKey();
+    public static final WritableIntPropertyKey LOCATION = new WritableIntPropertyKey();
+
+    public static final WritableObjectPropertyKey<
+            AppManagementMenuPermissionsView.OnButtonClickListener> ON_CLICK =
+            new WritableObjectPropertyKey<>();
+
+    public static final PropertyKey[] ALL_KEYS = {NOTIFICATIONS, MIC, CAMERA, LOCATION, ON_CLICK};
+}
diff --git a/chrome/browser/android/webapps/launchpad/java/src/org/chromium/chrome/browser/webapps/launchpad/AppManagementMenuPermissionsView.java b/chrome/browser/android/webapps/launchpad/java/src/org/chromium/chrome/browser/webapps/launchpad/AppManagementMenuPermissionsView.java
new file mode 100644
index 0000000..fd08e22d
--- /dev/null
+++ b/chrome/browser/android/webapps/launchpad/java/src/org/chromium/chrome/browser/webapps/launchpad/AppManagementMenuPermissionsView.java
@@ -0,0 +1,57 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.chrome.browser.webapps.launchpad;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.widget.ImageView;
+import android.widget.LinearLayout;
+
+import org.chromium.ui.modelutil.PropertyModel.WritableIntPropertyKey;
+
+/**
+ * View for app management menu permission buttons.
+ */
+public class AppManagementMenuPermissionsView extends LinearLayout {
+    private ImageView mNotificationsIcon;
+    private ImageView mMicIcon;
+    private ImageView mCameraIcon;
+    private ImageView mLocationIcon;
+
+    /**
+     * Interface for receiving click events on permission buttons.
+     */
+    public interface OnButtonClickListener {
+        void onButtonClick(WritableIntPropertyKey propertyKey);
+    }
+
+    /**
+     * Constructor for inflating from XML.
+     */
+    public AppManagementMenuPermissionsView(Context context, AttributeSet attrs) {
+        super(context, attrs);
+    }
+
+    @Override
+    protected void onFinishInflate() {
+        super.onFinishInflate();
+
+        mNotificationsIcon = findViewById(R.id.notifications_button);
+        mMicIcon = findViewById(R.id.mic_button);
+        mCameraIcon = findViewById(R.id.camera_button);
+        mLocationIcon = findViewById(R.id.location_button);
+    }
+
+    void setOnImageButtonClick(OnButtonClickListener listener) {
+        mNotificationsIcon.setOnClickListener(
+                v -> listener.onButtonClick(AppManagementMenuPermissionsProperties.NOTIFICATIONS));
+        mMicIcon.setOnClickListener(
+                v -> listener.onButtonClick(AppManagementMenuPermissionsProperties.MIC));
+        mCameraIcon.setOnClickListener(
+                v -> listener.onButtonClick(AppManagementMenuPermissionsProperties.CAMERA));
+        mLocationIcon.setOnClickListener(
+                v -> listener.onButtonClick(AppManagementMenuPermissionsProperties.LOCATION));
+    }
+}
diff --git a/chrome/browser/android/webapps/launchpad/java/src/org/chromium/chrome/browser/webapps/launchpad/AppManagementMenuPermissionsViewBinder.java b/chrome/browser/android/webapps/launchpad/java/src/org/chromium/chrome/browser/webapps/launchpad/AppManagementMenuPermissionsViewBinder.java
new file mode 100644
index 0000000..ee1bb22
--- /dev/null
+++ b/chrome/browser/android/webapps/launchpad/java/src/org/chromium/chrome/browser/webapps/launchpad/AppManagementMenuPermissionsViewBinder.java
@@ -0,0 +1,58 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+package org.chromium.chrome.browser.webapps.launchpad;
+
+import android.content.res.ColorStateList;
+import android.view.View;
+import android.widget.ImageView;
+
+import org.chromium.components.content_settings.ContentSettingValues;
+import org.chromium.ui.modelutil.PropertyKey;
+import org.chromium.ui.modelutil.PropertyModel;
+
+/**
+ * Class responsible for binding the model and the AppManagementMenuPermissionsView..
+ */
+class AppManagementMenuPermissionsViewBinder {
+    static void bind(
+            PropertyModel model, AppManagementMenuPermissionsView view, PropertyKey propertyKey) {
+        if (propertyKey == AppManagementMenuPermissionsProperties.NOTIFICATIONS) {
+            @ContentSettingValues
+            int setting = model.get(AppManagementMenuPermissionsProperties.NOTIFICATIONS);
+            updatePermissionIcon(view, R.id.notifications_button, setting,
+                    R.drawable.gm_filled_notifications_24,
+                    R.drawable.gm_filled_notifications_off_24);
+        } else if (propertyKey == AppManagementMenuPermissionsProperties.MIC) {
+            @ContentSettingValues
+            int setting = model.get(AppManagementMenuPermissionsProperties.MIC);
+            updatePermissionIcon(view, R.id.mic_button, setting, R.drawable.gm_filled_mic_24,
+                    R.drawable.gm_filled_mic_off_24);
+        } else if (propertyKey == AppManagementMenuPermissionsProperties.CAMERA) {
+            @ContentSettingValues
+            int setting = model.get(AppManagementMenuPermissionsProperties.CAMERA);
+            updatePermissionIcon(view, R.id.camera_button, setting,
+                    R.drawable.gm_filled_videocam_24, R.drawable.gm_filled_videocam_off_24);
+        } else if (propertyKey == AppManagementMenuPermissionsProperties.LOCATION) {
+            @ContentSettingValues
+            int setting = model.get(AppManagementMenuPermissionsProperties.LOCATION);
+            updatePermissionIcon(view, R.id.location_button, setting,
+                    R.drawable.gm_filled_location_on_24, R.drawable.gm_filled_location_off_24);
+        } else if (propertyKey == AppManagementMenuPermissionsProperties.ON_CLICK) {
+            view.setOnImageButtonClick(model.get(AppManagementMenuPermissionsProperties.ON_CLICK));
+        }
+    }
+
+    private static void updatePermissionIcon(
+            View view, int iconId, int setting, int enabledIcon, int disabledIcon) {
+        ImageView icon = (ImageView) view.findViewById(iconId);
+
+        int resId = setting == ContentSettingValues.ASK ? disabledIcon : enabledIcon;
+        icon.setImageResource(resId);
+
+        ColorStateList tint = ColorStateList.valueOf(view.getResources().getColor(
+                setting == ContentSettingValues.ALLOW ? R.color.default_icon_color
+                                                      : R.color.default_icon_color_disabled));
+        icon.setImageTintList(tint);
+    }
+}
diff --git a/chrome/browser/android/webapps/launchpad/java/src/org/chromium/chrome/browser/webapps/launchpad/LaunchpadPageTest.java b/chrome/browser/android/webapps/launchpad/java/src/org/chromium/chrome/browser/webapps/launchpad/LaunchpadPageTest.java
index 373d875..5436b47 100644
--- a/chrome/browser/android/webapps/launchpad/java/src/org/chromium/chrome/browser/webapps/launchpad/LaunchpadPageTest.java
+++ b/chrome/browser/android/webapps/launchpad/java/src/org/chromium/chrome/browser/webapps/launchpad/LaunchpadPageTest.java
@@ -14,14 +14,18 @@
 
 import android.app.Activity;
 import android.app.Instrumentation.ActivityResult;
+import android.content.Context;
 import android.graphics.Bitmap;
 import android.graphics.drawable.BitmapDrawable;
+import android.support.test.InstrumentationRegistry;
 import android.view.View;
 import android.widget.ImageView;
 import android.widget.TextView;
 
+import androidx.appcompat.content.res.AppCompatResources;
 import androidx.recyclerview.widget.RecyclerView;
 import androidx.test.espresso.intent.Intents;
+import androidx.test.filters.MediumTest;
 import androidx.test.filters.SmallTest;
 
 import org.junit.Assert;
@@ -33,10 +37,14 @@
 import org.chromium.base.test.util.CommandLineFlags;
 import org.chromium.chrome.browser.flags.ChromeFeatureList;
 import org.chromium.chrome.browser.flags.ChromeSwitches;
+import org.chromium.chrome.browser.profiles.Profile;
 import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
 import org.chromium.chrome.test.ChromeTabbedActivityTestRule;
 import org.chromium.chrome.test.util.browser.Features;
+import org.chromium.components.browser_ui.site_settings.PermissionInfo;
 import org.chromium.components.browser_ui.widget.tile.TileView;
+import org.chromium.components.content_settings.ContentSettingValues;
+import org.chromium.components.content_settings.ContentSettingsType;
 import org.chromium.components.embedder_support.util.UrlConstants;
 import org.chromium.content_public.browser.test.util.TestThreadUtils;
 import org.chromium.content_public.browser.test.util.TouchCommon;
@@ -131,7 +139,7 @@
     }
 
     @Test
-    @SmallTest
+    @MediumTest
     public void testShowAppManagementMenu() {
         openLaunchpadPage();
         ModalDialogManager modalDialogManager =
@@ -151,4 +159,55 @@
         ImageView icon = (ImageView) dialogView.findViewById(R.id.menu_header_image);
         Assert.assertEquals(TEST_ICON, ((BitmapDrawable) icon.getDrawable()).getBitmap());
     }
+
+    @Test
+    @MediumTest
+    public void testAppPermission() {
+        TestThreadUtils.runOnUiThreadBlocking(() -> {
+            // Set permission for Notifications to Allow, Mic to Ask, Camera to Block, and Location
+            // to Allow.
+            Profile profile = Profile.getLastUsedRegularProfile();
+            PermissionInfo notifications = new PermissionInfo(ContentSettingsType.NOTIFICATIONS,
+                    APP_URL_1, null /* embedder */, false /* isEmbargoed */);
+            notifications.setContentSetting(profile, ContentSettingValues.ALLOW);
+            PermissionInfo mic = new PermissionInfo(ContentSettingsType.MEDIASTREAM_MIC, APP_URL_1,
+                    null /* embedder */, false /* isEmbargoed */);
+            mic.setContentSetting(profile, ContentSettingValues.ASK);
+            PermissionInfo camera = new PermissionInfo(ContentSettingsType.MEDIASTREAM_CAMERA,
+                    APP_URL_1, null /* embedder */, false /* isEmbargoed */);
+            camera.setContentSetting(profile, ContentSettingValues.BLOCK);
+            PermissionInfo location = new PermissionInfo(ContentSettingsType.GEOLOCATION, APP_URL_1,
+                    null /* embedder */, false /* isEmbargoed */);
+            location.setContentSetting(profile, ContentSettingValues.ALLOW);
+        });
+
+        openLaunchpadPage();
+        ModalDialogManager modalDialogManager =
+                mActivityTestRule.getActivity().getModalDialogManager();
+
+        View item = mItemContainer.getChildAt(0);
+        TouchCommon.longPressView(item);
+        PropertyModel dialogModel = modalDialogManager.getCurrentDialogForTest();
+        View dialogView = dialogModel.get(ModalDialogProperties.CUSTOM_VIEW);
+
+        Context context = InstrumentationRegistry.getTargetContext();
+
+        ImageView notificationsIcon =
+                (ImageView) dialogView.findViewById(R.id.notifications_button);
+        Assert.assertEquals(
+                AppCompatResources.getColorStateList(context, R.color.default_icon_color),
+                notificationsIcon.getImageTintList());
+        ImageView micIcon = (ImageView) dialogView.findViewById(R.id.mic_button);
+        Assert.assertEquals(
+                AppCompatResources.getColorStateList(context, R.color.default_icon_color_disabled),
+                micIcon.getImageTintList());
+        ImageView cameraIcon = (ImageView) dialogView.findViewById(R.id.camera_button);
+        Assert.assertEquals(
+                AppCompatResources.getColorStateList(context, R.color.default_icon_color_disabled),
+                cameraIcon.getImageTintList());
+        ImageView locationIcon = (ImageView) dialogView.findViewById(R.id.location_button);
+        Assert.assertEquals(
+                AppCompatResources.getColorStateList(context, R.color.default_icon_color),
+                locationIcon.getImageTintList());
+    }
 }
diff --git a/chrome/browser/ash/web_applications/demo_mode_app_integration_browsertest.cc b/chrome/browser/ash/web_applications/demo_mode_app_integration_browsertest.cc
new file mode 100644
index 0000000..5439897
--- /dev/null
+++ b/chrome/browser/ash/web_applications/demo_mode_app_integration_browsertest.cc
@@ -0,0 +1,29 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ash/constants/ash_features.h"
+#include "base/test/scoped_feature_list.h"
+#include "chrome/browser/ash/web_applications/system_web_app_integration_test.h"
+#include "chromeos/components/demo_mode_app_ui/url_constants.h"
+#include "content/public/test/browser_test.h"
+
+class DemoModeAppIntegrationTest : public SystemWebAppIntegrationTest {
+ public:
+  DemoModeAppIntegrationTest() {
+    scoped_feature_list_.InitAndEnableFeature(chromeos::features::kDemoModeSWA);
+  }
+
+ private:
+  base::test::ScopedFeatureList scoped_feature_list_;
+};
+
+// Test that the Demo Mode App installs and launches correctly
+IN_PROC_BROWSER_TEST_P(DemoModeAppIntegrationTest, DemoModeApp) {
+  const GURL url(chromeos::kChromeUIDemoModeAppURL);
+  EXPECT_NO_FATAL_FAILURE(ExpectSystemWebAppValid(
+      web_app::SystemAppType::DEMO_MODE, url, "Demo Mode App"));
+}
+
+INSTANTIATE_SYSTEM_WEB_APP_MANAGER_TEST_SUITE_GUEST_SESSION_P(
+    DemoModeAppIntegrationTest);
diff --git a/chrome/browser/ash/web_applications/demo_mode_web_app_info.cc b/chrome/browser/ash/web_applications/demo_mode_web_app_info.cc
new file mode 100644
index 0000000..7ffa733
--- /dev/null
+++ b/chrome/browser/ash/web_applications/demo_mode_web_app_info.cc
@@ -0,0 +1,29 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/ash/web_applications/demo_mode_web_app_info.h"
+#include "base/strings/utf_string_conversions.h"
+#include "chrome/browser/ash/web_applications/system_web_app_install_utils.h"
+#include "chrome/browser/web_applications/components/web_application_info.h"
+#include "chromeos/components/demo_mode_app_ui/url_constants.h"
+#include "chromeos/grit/chromeos_demo_mode_app_resources.h"
+
+std::unique_ptr<WebApplicationInfo> CreateWebAppInfoForDemoModeApp() {
+  std::unique_ptr<WebApplicationInfo> info =
+      std::make_unique<WebApplicationInfo>();
+  info->start_url = GURL(chromeos::kChromeUIDemoModeAppURL);
+  info->scope = GURL(chromeos::kChromeUIDemoModeAppURL);
+  // TODO(b/185608502): Convert the title to a localized string
+  info->title = u"Demo Mode App";
+  web_app::CreateIconInfoForSystemWebApp(
+      info->start_url,
+      {{"app_icon_192.png", 192, IDR_CHROMEOS_DEMO_MODE_APP_APP_ICON_192_PNG}},
+      *info);
+  info->theme_color = 0xFF4285F4;
+  info->background_color = 0xFFFFFFFF;
+  info->display_mode = blink::mojom::DisplayMode::kStandalone;
+  info->open_as_window = true;
+
+  return info;
+}
diff --git a/chrome/browser/ash/web_applications/demo_mode_web_app_info.h b/chrome/browser/ash/web_applications/demo_mode_web_app_info.h
new file mode 100644
index 0000000..5787170
--- /dev/null
+++ b/chrome/browser/ash/web_applications/demo_mode_web_app_info.h
@@ -0,0 +1,14 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_ASH_WEB_APPLICATIONS_DEMO_MODE_WEB_APP_INFO_H_
+#define CHROME_BROWSER_ASH_WEB_APPLICATIONS_DEMO_MODE_WEB_APP_INFO_H_
+
+#include <memory>
+
+struct WebApplicationInfo;
+
+std::unique_ptr<WebApplicationInfo> CreateWebAppInfoForDemoModeApp();
+
+#endif  // CHROME_BROWSER_ASH_WEB_APPLICATIONS_DEMO_MODE_WEB_APP_INFO_H_
diff --git a/chrome/browser/conversions/OWNERS b/chrome/browser/attribution_reporting/OWNERS
similarity index 100%
rename from chrome/browser/conversions/OWNERS
rename to chrome/browser/attribution_reporting/OWNERS
diff --git a/chrome/browser/attribution_reporting/android/BUILD.gn b/chrome/browser/attribution_reporting/android/BUILD.gn
new file mode 100644
index 0000000..3a20409
--- /dev/null
+++ b/chrome/browser/attribution_reporting/android/BUILD.gn
@@ -0,0 +1,15 @@
+import("//build/config/android/rules.gni")
+import("//chrome/android/features/android_library_factory_tmpl.gni")
+
+android_library("java") {
+  sources = [
+    "java/src/org/chromium/chrome/browser/attribution_reporting/AttributionConstants.java",
+    "java/src/org/chromium/chrome/browser/attribution_reporting/AttributionIntentHandler.java",
+  ]
+}
+
+android_library_factory("factory_java") {
+  deps = [ ":java" ]
+
+  sources = [ "internal/java/src/org/chromium/chrome/browser/attribution_reporting/AttributionIntentHandlerFactory.java" ]
+}
diff --git a/chrome/browser/attribution_reporting/android/DIR_METADATA b/chrome/browser/attribution_reporting/android/DIR_METADATA
new file mode 100644
index 0000000..5777c4c
--- /dev/null
+++ b/chrome/browser/attribution_reporting/android/DIR_METADATA
@@ -0,0 +1,12 @@
+# Metadata information for this directory.
+#
+# For more information on DIR_METADATA files, see:
+#   https://source.chromium.org/chromium/infra/infra/+/master:go/src/infra/tools/dirmd/README.md
+#
+# For the schema of this file, see Metadata message:
+#   https://source.chromium.org/chromium/infra/infra/+/master:go/src/infra/tools/dirmd/proto/dir_metadata.proto
+
+monorail {
+  component: "Internals>ConversionMeasurement"
+}
+os: ANDROID
diff --git a/chrome/browser/attribution_reporting/android/OWNERS b/chrome/browser/attribution_reporting/android/OWNERS
new file mode 100644
index 0000000..c19374d6
--- /dev/null
+++ b/chrome/browser/attribution_reporting/android/OWNERS
@@ -0,0 +1 @@
+mthiesse@chromium.org
diff --git a/chrome/browser/attribution_reporting/android/internal/BUILD.gn b/chrome/browser/attribution_reporting/android/internal/BUILD.gn
new file mode 100644
index 0000000..605c7cc
--- /dev/null
+++ b/chrome/browser/attribution_reporting/android/internal/BUILD.gn
@@ -0,0 +1,9 @@
+import("//build/config/android/rules.gni")
+
+android_library("java") {
+  sources = [
+    "java/src/org/chromium/chrome/browser/attribution_reporting/AttributionIntentHandlerFactory.java",
+    "java/src/org/chromium/chrome/browser/attribution_reporting/NoopAttributionIntentHandler.java",
+  ]
+  deps = [ "//chrome/browser/attribution_reporting/android:java" ]
+}
diff --git a/chrome/browser/attribution_reporting/android/internal/java/src/org/chromium/chrome/browser/attribution_reporting/AttributionIntentHandlerFactory.java b/chrome/browser/attribution_reporting/android/internal/java/src/org/chromium/chrome/browser/attribution_reporting/AttributionIntentHandlerFactory.java
new file mode 100644
index 0000000..ebf154a
--- /dev/null
+++ b/chrome/browser/attribution_reporting/android/internal/java/src/org/chromium/chrome/browser/attribution_reporting/AttributionIntentHandlerFactory.java
@@ -0,0 +1,18 @@
+// 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.attribution_reporting;
+
+/**
+ * Factory for creating instances of the AttributionIntentHandler from the attribution_reporting
+ * module.
+ */
+public class AttributionIntentHandlerFactory {
+    /**
+     * @return a AttributionIntentHandler instance.
+     */
+    public static AttributionIntentHandler create() {
+        return new NoopAttributionIntentHandler();
+    }
+}
diff --git a/chrome/browser/attribution_reporting/android/internal/java/src/org/chromium/chrome/browser/attribution_reporting/NoopAttributionIntentHandler.java b/chrome/browser/attribution_reporting/android/internal/java/src/org/chromium/chrome/browser/attribution_reporting/NoopAttributionIntentHandler.java
new file mode 100644
index 0000000..3cc3c77
--- /dev/null
+++ b/chrome/browser/attribution_reporting/android/internal/java/src/org/chromium/chrome/browser/attribution_reporting/NoopAttributionIntentHandler.java
@@ -0,0 +1,20 @@
+// 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.attribution_reporting;
+
+import android.content.Intent;
+
+/** Noops Attribution Intent handling when the App Attribution feature isn't enabled. */
+public class NoopAttributionIntentHandler implements AttributionIntentHandler {
+    @Override
+    public boolean handleOuterAttributionIntent(Intent intent) {
+        return false;
+    }
+
+    @Override
+    public Intent handleInnerAttributionIntent(Intent intent) {
+        return null;
+    }
+}
diff --git a/chrome/browser/attribution_reporting/android/java/src/org/chromium/chrome/browser/attribution_reporting/AttributionConstants.java b/chrome/browser/attribution_reporting/android/java/src/org/chromium/chrome/browser/attribution_reporting/AttributionConstants.java
new file mode 100644
index 0000000..0caa288
--- /dev/null
+++ b/chrome/browser/attribution_reporting/android/java/src/org/chromium/chrome/browser/attribution_reporting/AttributionConstants.java
@@ -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.
+
+package org.chromium.chrome.browser.attribution_reporting;
+
+/** Public constants for the attribution_reporting module. */
+public class AttributionConstants {
+    public static final String ACTION_APP_ATTRIBUTION = "android.web.action.APP_ATTRIBUTION";
+    public static final String EXTRA_ATTRIBUTION_INTENT = "android.web.extra.ATTRIBUTION_INTENT";
+}
diff --git a/chrome/browser/attribution_reporting/android/java/src/org/chromium/chrome/browser/attribution_reporting/AttributionIntentHandler.java b/chrome/browser/attribution_reporting/android/java/src/org/chromium/chrome/browser/attribution_reporting/AttributionIntentHandler.java
new file mode 100644
index 0000000..3bb0248
--- /dev/null
+++ b/chrome/browser/attribution_reporting/android/java/src/org/chromium/chrome/browser/attribution_reporting/AttributionIntentHandler.java
@@ -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.
+
+package org.chromium.chrome.browser.attribution_reporting;
+
+import android.content.Intent;
+
+/** Public interface for the AttributionIntentHandler from the attribution_reporting module. */
+public interface AttributionIntentHandler {
+    /**
+     * If |intent| is an outer App to Web Attribution Intent, processes the Intent and returns true,
+     * otherwise returns false.
+     */
+    boolean handleOuterAttributionIntent(Intent intent);
+
+    /**
+     * If the intent is an inner App to Web Attribution Intent, processes the Attribution data, and
+     * returns the View intent provided by the original outer intent.
+     */
+    Intent handleInnerAttributionIntent(Intent intent);
+}
diff --git a/chrome/browser/conversions/conversions_usage_restriction_trial_browsertest.cc b/chrome/browser/attribution_reporting/conversions_usage_restriction_trial_browsertest.cc
similarity index 100%
rename from chrome/browser/conversions/conversions_usage_restriction_trial_browsertest.cc
rename to chrome/browser/attribution_reporting/conversions_usage_restriction_trial_browsertest.cc
diff --git a/chrome/browser/conversions/conversions_usecounter_browsertest.cc b/chrome/browser/attribution_reporting/conversions_usecounter_browsertest.cc
similarity index 100%
rename from chrome/browser/conversions/conversions_usecounter_browsertest.cc
rename to chrome/browser/attribution_reporting/conversions_usecounter_browsertest.cc
diff --git a/chrome/browser/autofill/android/save_address_profile_flow_manager.cc b/chrome/browser/autofill/android/save_address_profile_flow_manager.cc
index 72e332f..7ceb4fe 100644
--- a/chrome/browser/autofill/android/save_address_profile_flow_manager.cc
+++ b/chrome/browser/autofill/android/save_address_profile_flow_manager.cc
@@ -19,6 +19,7 @@
 void SaveAddressProfileFlowManager::OfferSave(
     content::WebContents* web_contents,
     const AutofillProfile& profile,
+    const AutofillProfile* original_profile,
     AutofillClient::AddressProfileSavePromptCallback callback) {
   DCHECK(web_contents);
   DCHECK(callback);
@@ -31,7 +32,8 @@
 
   if (base::FeatureList::IsEnabled(
           messages::kMessagesForAndroidInfrastructure)) {
-    ShowSaveAddressProfileMessage(web_contents, profile, std::move(callback));
+    ShowSaveAddressProfileMessage(web_contents, profile, original_profile,
+                                  std::move(callback));
   } else {
     // Fallback to the default behavior without confirmation.
     std::move(callback).Run(
@@ -43,17 +45,30 @@
 void SaveAddressProfileFlowManager::ShowSaveAddressProfileMessage(
     content::WebContents* web_contents,
     const AutofillProfile& profile,
+    const AutofillProfile* original_profile,
     AutofillClient::AddressProfileSavePromptCallback callback) {
   save_address_profile_message_controller_.DisplayMessage(
-      web_contents, profile, std::move(callback),
-      base::BindOnce(
-          &SaveAddressProfileFlowManager::ShowSaveAddressProfileDetails,
-          // Passing base::Unretained(this) is safe since |this|
-          // owns the controller.
-          base::Unretained(this)));
+      web_contents, profile, original_profile, std::move(callback),
+      base::BindOnce(&SaveAddressProfileFlowManager::OnMessageActionTriggered,
+                     // Passing base::Unretained(this) is safe since |this|
+                     // owns the controller.
+                     base::Unretained(this)));
 }
 
-void SaveAddressProfileFlowManager::ShowSaveAddressProfileDetails(
+void SaveAddressProfileFlowManager::OnMessageActionTriggered(
+    content::WebContents* web_contents,
+    const AutofillProfile& profile,
+    const AutofillProfile* original_profile,
+    AutofillClient::AddressProfileSavePromptCallback callback) {
+  if (original_profile) {
+    ShowUpdateAddressProfileDetails(web_contents, profile, original_profile,
+                                    std::move(callback));
+  } else {
+    ShowNewAddressProfileDetails(web_contents, profile, std::move(callback));
+  }
+}
+
+void SaveAddressProfileFlowManager::ShowNewAddressProfileDetails(
     content::WebContents* web_contents,
     const AutofillProfile& profile,
     AutofillClient::AddressProfileSavePromptCallback callback) {
@@ -71,6 +86,16 @@
   save_address_profile_prompt_controller_->DisplayPrompt();
 }
 
+void SaveAddressProfileFlowManager::ShowUpdateAddressProfileDetails(
+    content::WebContents* web_contents,
+    const AutofillProfile& profile,
+    const AutofillProfile* original_profile,
+    AutofillClient::AddressProfileSavePromptCallback callback) {
+  // TODO(crbug.com/1167061): Show prompt with changes, for now just accept.
+  std::move(callback).Run(
+      AutofillClient::SaveAddressProfileOfferUserDecision::kAccepted, profile);
+}
+
 void SaveAddressProfileFlowManager::OnSaveAddressProfileDetailsShown() {
   save_address_profile_prompt_controller_.reset();
 }
diff --git a/chrome/browser/autofill/android/save_address_profile_flow_manager.h b/chrome/browser/autofill/android/save_address_profile_flow_manager.h
index 9957505..b548160 100644
--- a/chrome/browser/autofill/android/save_address_profile_flow_manager.h
+++ b/chrome/browser/autofill/android/save_address_profile_flow_manager.h
@@ -27,19 +27,38 @@
       const SaveAddressProfileFlowManager&) = delete;
   ~SaveAddressProfileFlowManager();
 
+  // Triggers a confirmation flow for saving the `profile` using the given
+  // `web_contents`. If another flow is in progress, it will be cancelled and
+  // replaced by the new one. The `original_profile` is nullptr for a new
+  // address or points to the existing profile which will be updated if the
+  // user accepts. The `callback` is triggered once the user makes a decision.
   void OfferSave(content::WebContents* web_contents,
                  const AutofillProfile& profile,
+                 const AutofillProfile* original_profile,
                  AutofillClient::AddressProfileSavePromptCallback callback);
 
  private:
   void ShowSaveAddressProfileMessage(
       content::WebContents* web_contents,
       const AutofillProfile& profile,
+      const AutofillProfile* original_profile,
       AutofillClient::AddressProfileSavePromptCallback callback);
 
-  void ShowSaveAddressProfileDetails(
+  void OnMessageActionTriggered(
       content::WebContents* web_contents,
       const AutofillProfile& profile,
+      const AutofillProfile* original_profile,
+      AutofillClient::AddressProfileSavePromptCallback callback);
+
+  void ShowNewAddressProfileDetails(
+      content::WebContents* web_contents,
+      const AutofillProfile& profile,
+      AutofillClient::AddressProfileSavePromptCallback callback);
+
+  void ShowUpdateAddressProfileDetails(
+      content::WebContents* web_contents,
+      const AutofillProfile& profile,
+      const AutofillProfile* original_profile,
       AutofillClient::AddressProfileSavePromptCallback callback);
 
   void OnSaveAddressProfileDetailsShown();
diff --git a/chrome/browser/autofill/android/save_address_profile_message_controller.cc b/chrome/browser/autofill/android/save_address_profile_message_controller.cc
index c6c36a6..b4d79c5 100644
--- a/chrome/browser/autofill/android/save_address_profile_message_controller.cc
+++ b/chrome/browser/autofill/android/save_address_profile_message_controller.cc
@@ -7,6 +7,8 @@
 #include <utility>
 
 #include "base/strings/utf_string_conversions.h"
+#include "chrome/browser/browser_process.h"
+#include "components/autofill/core/browser/autofill_address_util.h"
 #include "components/autofill/core/browser/autofill_client.h"
 #include "components/autofill/core/common/autofill_features.h"
 #include "components/messages/android/message_dispatcher_bridge.h"
@@ -23,6 +25,7 @@
 void SaveAddressProfileMessageController::DisplayMessage(
     content::WebContents* web_contents,
     const AutofillProfile& profile,
+    const AutofillProfile* original_profile,
     AutofillClient::AddressProfileSavePromptCallback
         save_address_profile_callback,
     PrimaryActionCallback primary_action_callback) {
@@ -39,6 +42,7 @@
 
   web_contents_ = web_contents;
   profile_ = profile;
+  original_profile_ = original_profile;
   save_address_profile_callback_ = std::move(save_address_profile_callback);
   primary_action_callback_ = std::move(primary_action_callback);
 
@@ -51,10 +55,9 @@
       base::BindOnce(&SaveAddressProfileMessageController::OnMessageDismissed,
                      base::Unretained(this)));
 
-  // TODO(crbug.com/1167061): Replace with proper localized string.
-  message_->SetTitle(u"Save address?");
-  message_->SetDescription(u"Fill forms faster in Chrome");
-  message_->SetPrimaryButtonText(u"Save...");
+  message_->SetTitle(GetTitle());
+  message_->SetDescription(GetDescription());
+  message_->SetPrimaryButtonText(GetPrimaryButtonText());
 
   messages::MessageDispatcherBridge::Get()->EnqueueMessage(
       message_.get(), web_contents, messages::MessageScopeType::WEB_CONTENTS);
@@ -69,7 +72,8 @@
 
 void SaveAddressProfileMessageController::OnPrimaryAction() {
   std::move(primary_action_callback_)
-      .Run(web_contents_, profile_, std::move(save_address_profile_callback_));
+      .Run(web_contents_, profile_, original_profile_,
+           std::move(save_address_profile_callback_));
 }
 
 void SaveAddressProfileMessageController::OnMessageDismissed(
@@ -102,4 +106,26 @@
   primary_action_callback_.Reset();
 }
 
+std::u16string SaveAddressProfileMessageController::GetTitle() {
+  // TODO(crbug.com/1167061): Replace with proper localized strings.
+  // TODO(crbug.com/1167061): Make update title reflect fields to be updated.
+  return original_profile_ ? u"Update address?" : u"Save address?";
+}
+
+std::u16string SaveAddressProfileMessageController::GetDescription() {
+  const std::string locale = g_browser_process->GetApplicationLocale();
+  if (original_profile_) {
+    // TODO(crbug.com/1167061): Replace with proper localized string.
+    return u"For " +
+           GetDescriptionForProfileToUpdate(*original_profile_, locale);
+  } else {
+    return GetDescriptionForProfileToSave(profile_, locale);
+  }
+}
+
+std::u16string SaveAddressProfileMessageController::GetPrimaryButtonText() {
+  // TODO(crbug.com/1167061): Replace with proper localized strings.
+  return original_profile_ ? u"Update…" : u"Save…";
+}
+
 }  // namespace autofill
diff --git a/chrome/browser/autofill/android/save_address_profile_message_controller.h b/chrome/browser/autofill/android/save_address_profile_message_controller.h
index 6c3e0e8..62a1e6d 100644
--- a/chrome/browser/autofill/android/save_address_profile_message_controller.h
+++ b/chrome/browser/autofill/android/save_address_profile_message_controller.h
@@ -32,10 +32,12 @@
   using PrimaryActionCallback = base::OnceCallback<void(
       content::WebContents*,
       const AutofillProfile&,
+      const AutofillProfile* original_profile,
       AutofillClient::AddressProfileSavePromptCallback)>;
 
   void DisplayMessage(content::WebContents* web_contents,
                       const AutofillProfile& profile,
+                      const AutofillProfile* original_profile,
                       AutofillClient::AddressProfileSavePromptCallback
                           save_address_profile_callback,
                       PrimaryActionCallback primary_action_callback);
@@ -54,11 +56,17 @@
   void RunSaveAddressProfileCallback(
       AutofillClient::SaveAddressProfileOfferUserDecision decision);
 
+  std::u16string GetTitle();
+  std::u16string GetDescription();
+  std::u16string GetPrimaryButtonText();
+
   content::WebContents* web_contents_ = nullptr;
   std::unique_ptr<messages::MessageWrapper> message_;
 
   // The profile which is being confirmed by the user.
   AutofillProfile profile_;
+  // The profile (if exists) which will be updated if the user confirms.
+  const AutofillProfile* original_profile_;
   // The callback to run once the user makes the final decision.
   AutofillClient::AddressProfileSavePromptCallback
       save_address_profile_callback_;
diff --git a/chrome/browser/autofill/android/save_address_profile_message_controller_unittest.cc b/chrome/browser/autofill/android/save_address_profile_message_controller_unittest.cc
index aa93383..336e14e 100644
--- a/chrome/browser/autofill/android/save_address_profile_message_controller_unittest.cc
+++ b/chrome/browser/autofill/android/save_address_profile_message_controller_unittest.cc
@@ -6,6 +6,7 @@
 
 #include "base/test/mock_callback.h"
 #include "base/test/scoped_feature_list.h"
+#include "chrome/browser/browser_process.h"
 #include "chrome/test/base/chrome_render_view_host_test_harness.h"
 #include "components/autofill/core/browser/autofill_client.h"
 #include "components/autofill/core/browser/autofill_test_utils.h"
@@ -27,28 +28,45 @@
   void SetUp() override;
   void TearDown() override;
 
-  void EnqueueMessage(
+  void EnqueueSaveMessage(
       const AutofillProfile& profile,
       AutofillClient::AddressProfileSavePromptCallback save_callback,
       SaveAddressProfileMessageController::PrimaryActionCallback
-          action_callback);
+          action_callback) {
+    EnqueueMessage(profile, nullptr, std::move(save_callback),
+                   std::move(action_callback));
+  }
+  void EnqueueUpdateMessage(
+      const AutofillProfile& profile,
+      const AutofillProfile* original_profile,
+      AutofillClient::AddressProfileSavePromptCallback save_callback,
+      SaveAddressProfileMessageController::PrimaryActionCallback
+          action_callback) {
+    EnqueueMessage(profile, original_profile, std::move(save_callback),
+                   std::move(action_callback));
+  }
   void ExpectDismissMessageCall();
 
   void TriggerActionClick();
   void TriggerMessageDismissedCallback(messages::DismissReason dismiss_reason);
 
   messages::MessageWrapper* GetMessageWrapper();
-  messages::MockMessageDispatcherBridge* message_dispatcher_bridge() {
-    return &message_dispatcher_bridge_;
-  }
 
   AutofillProfile profile_;
+  AutofillProfile original_profile_;
   base::MockCallback<AutofillClient::AddressProfileSavePromptCallback>
       save_callback_;
   base::MockCallback<SaveAddressProfileMessageController::PrimaryActionCallback>
       action_callback_;
 
  private:
+  void EnqueueMessage(
+      const AutofillProfile& profile,
+      const AutofillProfile* original_profile,
+      AutofillClient::AddressProfileSavePromptCallback save_callback,
+      SaveAddressProfileMessageController::PrimaryActionCallback
+          action_callback);
+
   SaveAddressProfileMessageController controller_;
   messages::MockMessageDispatcherBridge message_dispatcher_bridge_;
   base::test::ScopedFeatureList feature_list_;
@@ -61,6 +79,7 @@
   messages::MessageDispatcherBridge::SetInstanceForTesting(
       &message_dispatcher_bridge_);
   profile_ = test::GetFullProfile();
+  original_profile_ = test::GetFullProfile2();
 }
 
 void SaveAddressProfileMessageControllerTest::TearDown() {
@@ -70,11 +89,13 @@
 
 void SaveAddressProfileMessageControllerTest::EnqueueMessage(
     const AutofillProfile& profile,
+    const AutofillProfile* original_profile,
     AutofillClient::AddressProfileSavePromptCallback save_callback,
     SaveAddressProfileMessageController::PrimaryActionCallback
         action_callback) {
   EXPECT_CALL(message_dispatcher_bridge_, EnqueueMessage);
-  controller_.DisplayMessage(web_contents(), profile, std::move(save_callback),
+  controller_.DisplayMessage(web_contents(), profile, original_profile,
+                             std::move(save_callback),
                              std::move(action_callback));
 }
 
@@ -103,13 +124,57 @@
   return controller_.message_.get();
 }
 
+// Tests that the save message properties (title, description with profile
+// details, primary button text) are set correctly.
+TEST_F(SaveAddressProfileMessageControllerTest, SaveMessageContent) {
+  EnqueueSaveMessage(profile_, save_callback_.Get(), action_callback_.Get());
+
+  EXPECT_EQ(u"Save address?", GetMessageWrapper()->GetTitle());
+  EXPECT_EQ(u"Save…", GetMessageWrapper()->GetPrimaryButtonText());
+  EXPECT_EQ(u"John H. Doe, 666 Erebus St.",
+            GetMessageWrapper()->GetDescription());
+
+  TriggerMessageDismissedCallback(messages::DismissReason::UNKNOWN);
+}
+
+// Tests that the update message properties (title, description with original
+// profile details, primary button text) are set correctly.
+TEST_F(SaveAddressProfileMessageControllerTest, UpdateMessageContent) {
+  EnqueueUpdateMessage(profile_, &original_profile_, save_callback_.Get(),
+                       action_callback_.Get());
+
+  EXPECT_EQ(u"Update address?", GetMessageWrapper()->GetTitle());
+  EXPECT_EQ(u"Update…", GetMessageWrapper()->GetPrimaryButtonText());
+  EXPECT_EQ(u"For Jane A. Smith — 123 Main Street",
+            GetMessageWrapper()->GetDescription());
+
+  TriggerMessageDismissedCallback(messages::DismissReason::UNKNOWN);
+}
+
 // Tests that the action callback is triggered when the user clicks on the
-// primary action button.
-TEST_F(SaveAddressProfileMessageControllerTest, ProceedOnActionClick) {
-  EnqueueMessage(profile_, save_callback_.Get(), action_callback_.Get());
+// primary action button of the save message.
+TEST_F(SaveAddressProfileMessageControllerTest, ProceedOnActionClickWhenSave) {
+  EnqueueSaveMessage(profile_, save_callback_.Get(), action_callback_.Get());
   EXPECT_NE(nullptr, GetMessageWrapper());
 
-  EXPECT_CALL(action_callback_, Run(_, profile_, _));
+  EXPECT_CALL(action_callback_, Run(_, profile_, nullptr, _));
+  TriggerActionClick();
+  EXPECT_NE(nullptr, GetMessageWrapper());
+
+  EXPECT_CALL(save_callback_, Run(_, profile_)).Times(0);
+  TriggerMessageDismissedCallback(messages::DismissReason::PRIMARY_ACTION);
+  EXPECT_EQ(nullptr, GetMessageWrapper());
+}
+
+// Tests that the action callback is triggered when the user clicks on the
+// primary action button of the update message.
+TEST_F(SaveAddressProfileMessageControllerTest,
+       ProceedOnActionClickWhenUpdate) {
+  EnqueueUpdateMessage(profile_, &original_profile_, save_callback_.Get(),
+                       action_callback_.Get());
+  EXPECT_NE(nullptr, GetMessageWrapper());
+
+  EXPECT_CALL(action_callback_, Run(_, profile_, &original_profile_, _));
   TriggerActionClick();
   EXPECT_NE(nullptr, GetMessageWrapper());
 
@@ -122,7 +187,7 @@
 // |SaveAddressProfileOfferUserDecision::kDeclined| when the user dismisses the
 // message.
 TEST_F(SaveAddressProfileMessageControllerTest, DeclineOnGestureDismiss) {
-  EnqueueMessage(profile_, save_callback_.Get(), action_callback_.Get());
+  EnqueueSaveMessage(profile_, save_callback_.Get(), action_callback_.Get());
   EXPECT_NE(nullptr, GetMessageWrapper());
 
   EXPECT_CALL(
@@ -137,7 +202,7 @@
 // |SaveAddressProfileOfferUserDecision::kIgnored| when the message is
 // autodismissed.
 TEST_F(SaveAddressProfileMessageControllerTest, IgnoreOnTimerAutodismiss) {
-  EnqueueMessage(profile_, save_callback_.Get(), action_callback_.Get());
+  EnqueueSaveMessage(profile_, save_callback_.Get(), action_callback_.Get());
   EXPECT_NE(nullptr, GetMessageWrapper());
 
   EXPECT_CALL(save_callback_,
@@ -149,7 +214,8 @@
 
 // Tests that the previous prompt gets dismissed when the new one is enqueued.
 TEST_F(SaveAddressProfileMessageControllerTest, OnlyOnePromptAtATime) {
-  EnqueueMessage(profile_, save_callback_.Get(), action_callback_.Get());
+  EnqueueUpdateMessage(profile_, &original_profile_, save_callback_.Get(),
+                       action_callback_.Get());
 
   AutofillProfile another_profile = test::GetFullProfile();
   base::MockCallback<AutofillClient::AddressProfileSavePromptCallback>
@@ -160,8 +226,8 @@
               Run(AutofillClient::SaveAddressProfileOfferUserDecision::kIgnored,
                   profile_));
   ExpectDismissMessageCall();
-  EnqueueMessage(another_profile, another_save_callback.Get(),
-                 another_action_callback.Get());
+  EnqueueSaveMessage(another_profile, another_save_callback.Get(),
+                     another_action_callback.Get());
 
   TriggerMessageDismissedCallback(messages::DismissReason::UNKNOWN);
 }
diff --git a/chrome/browser/autofill/android/save_address_profile_prompt_controller.cc b/chrome/browser/autofill/android/save_address_profile_prompt_controller.cc
index 5484c14..a4d2b6f 100644
--- a/chrome/browser/autofill/android/save_address_profile_prompt_controller.cc
+++ b/chrome/browser/autofill/android/save_address_profile_prompt_controller.cc
@@ -51,32 +51,9 @@
 }
 
 std::u16string SaveAddressProfilePromptController::GetAddress() {
-  const std::string locale = g_browser_process->GetApplicationLocale();
-  const AutofillType kCountryCode(HTML_TYPE_COUNTRY_CODE, HTML_MODE_NONE);
-  const std::u16string& country_code = profile_.GetInfo(kCountryCode, locale);
-
-  std::vector<std::vector<::i18n::addressinput::AddressUiComponent>> components;
-  autofill::GetAddressComponents(base::UTF16ToUTF8(country_code), locale,
-                                 /*include_literals=*/false, &components,
-                                 nullptr);
-
-  std::vector<std::u16string> address_lines;
-  for (const std::vector<::i18n::addressinput::AddressUiComponent>& line :
-       components) {
-    std::vector<std::u16string> line_components;
-    for (const ::i18n::addressinput::AddressUiComponent& component : line) {
-      std::u16string component_str = profile_.GetInfo(
-          autofill::AddressFieldToServerFieldType(component.field), locale);
-      if (!component_str.empty())
-        line_components.push_back(component_str);
-    }
-    if (!line_components.empty())
-      address_lines.push_back(base::JoinString(line_components, u" "));
-  }
-  std::u16string country = profile_.GetInfo(ADDRESS_HOME_COUNTRY, locale);
-  if (!country.empty())
-    address_lines.push_back(country);
-  return base::JoinString(address_lines, u"\n");
+  return GetEnvelopeStyleAddress(profile_,
+                                 g_browser_process->GetApplicationLocale(),
+                                 /*include_country=*/true);
 }
 
 std::u16string SaveAddressProfilePromptController::GetEmail() {
diff --git a/chrome/browser/autofill/android/save_address_profile_prompt_controller_unittest.cc b/chrome/browser/autofill/android/save_address_profile_prompt_controller_unittest.cc
index c943f374..111efbf 100644
--- a/chrome/browser/autofill/android/save_address_profile_prompt_controller_unittest.cc
+++ b/chrome/browser/autofill/android/save_address_profile_prompt_controller_unittest.cc
@@ -148,7 +148,7 @@
 
 TEST_F(SaveAddressProfilePromptControllerTest, ShouldReturnProfileData) {
   EXPECT_EQ(
-      u"John H. Doe\nUnderworld\n666 Erebus St.\nApt 8\nElysium CA "
+      u"John H. Doe\nUnderworld\n666 Erebus St.\nApt 8\nElysium, CA "
       u"91111\nUnited States",
       controller_->GetAddress());
   EXPECT_EQ(u"johndoe@hades.com", controller_->GetEmail());
diff --git a/chrome/browser/autofill/autofill_provider_browsertest.cc b/chrome/browser/autofill/autofill_provider_browsertest.cc
index 7611885..a227f45 100644
--- a/chrome/browser/autofill/autofill_provider_browsertest.cc
+++ b/chrome/browser/autofill/autofill_provider_browsertest.cc
@@ -43,20 +43,20 @@
   ~MockAutofillProvider() override {}
 
   MOCK_METHOD4(OnFormSubmitted,
-               void(AutofillHandlerProxy* handler,
+               void(AndroidAutofillManager* manager,
                     const FormData& form,
                     bool,
                     SubmissionSource));
 
   MOCK_METHOD6(OnQueryFormFieldAutofill,
-               void(AutofillHandlerProxy* handler,
+               void(AndroidAutofillManager* manager,
                     int32_t id,
                     const FormData& form,
                     const FormFieldData& field,
                     const gfx::RectF& bounding_box,
                     bool autoselect_first_suggestion));
 
-  void OnQueryFormFieldAutofillImpl(AutofillHandlerProxy* handler,
+  void OnQueryFormFieldAutofillImpl(AndroidAutofillManager* manager,
                                     int32_t id,
                                     const FormData& form,
                                     const FormFieldData& field,
@@ -65,7 +65,7 @@
     queried_form_ = form;
   }
 
-  void OnFormSubmittedImpl(AutofillHandlerProxy*,
+  void OnFormSubmittedImpl(AndroidAutofillManager*,
                            const FormData& form,
                            bool success,
                            SubmissionSource source) {
diff --git a/chrome/browser/badging/OWNERS b/chrome/browser/badging/OWNERS
index dfa01f6..d2b902d1 100644
--- a/chrome/browser/badging/OWNERS
+++ b/chrome/browser/badging/OWNERS
@@ -1,4 +1,4 @@
 cmp@chromium.org
-dmurph@chromiumr.org
+dmurph@chromium.org
 msw@chromium.org
 mgiuca@chromium.org
diff --git a/chrome/browser/chrome_content_browser_client.cc b/chrome/browser/chrome_content_browser_client.cc
index b348998..5e200fe 100644
--- a/chrome/browser/chrome_content_browser_client.cc
+++ b/chrome/browser/chrome_content_browser_client.cc
@@ -3822,7 +3822,7 @@
     case sandbox::policy::SandboxType::kSpeechRecognition:
     case sandbox::policy::SandboxType::kProxyResolver:
     case sandbox::policy::SandboxType::kPdfConversion:
-    case sandbox::policy::SandboxType::kSharingService:
+    case sandbox::policy::SandboxType::kService:
     case sandbox::policy::SandboxType::kVideoCapture:
     case sandbox::policy::SandboxType::kIconReader:
     case sandbox::policy::SandboxType::kMediaFoundationCdm:
@@ -3871,7 +3871,7 @@
     case sandbox::policy::SandboxType::kSpeechRecognition:
     case sandbox::policy::SandboxType::kProxyResolver:
     case sandbox::policy::SandboxType::kPdfConversion:
-    case sandbox::policy::SandboxType::kSharingService:
+    case sandbox::policy::SandboxType::kService:
     case sandbox::policy::SandboxType::kVideoCapture:
     case sandbox::policy::SandboxType::kIconReader:
     case sandbox::policy::SandboxType::kMediaFoundationCdm:
diff --git a/chrome/browser/chromeos/BUILD.gn b/chrome/browser/chromeos/BUILD.gn
index 808e3d2e..8eaefcc 100644
--- a/chrome/browser/chromeos/BUILD.gn
+++ b/chrome/browser/chromeos/BUILD.gn
@@ -3275,9 +3275,11 @@
 
   if (!is_official_build) {
     deps += [
+      "//chromeos/components/demo_mode_app_ui",
       "//chromeos/components/file_manager:file_manager_ui",
       "//chromeos/components/sample_system_web_app_ui",
       "//chromeos/components/telemetry_extension_ui",
+      "//chromeos/resources:demo_mode_app_resources_grit",
       "//chromeos/resources:file_manager_resources_grit",
       "//chromeos/resources:sample_system_web_app_resources_grit",
       "//chromeos/resources:telemetry_extension_resources",
@@ -3285,6 +3287,8 @@
     sources += [
       "../ash/web_applications/chrome_file_manager_ui_delegate.cc",
       "../ash/web_applications/chrome_file_manager_ui_delegate.h",
+      "../ash/web_applications/demo_mode_web_app_info.cc",
+      "../ash/web_applications/demo_mode_web_app_info.h",
       "../ash/web_applications/file_manager_web_app_info.cc",
       "../ash/web_applications/file_manager_web_app_info.h",
       "../ash/web_applications/sample_system_web_app_info.cc",
diff --git a/chrome/browser/component_updater/DEPS b/chrome/browser/component_updater/DEPS
index 12ab8214..a2454a88 100644
--- a/chrome/browser/component_updater/DEPS
+++ b/chrome/browser/component_updater/DEPS
@@ -3,7 +3,7 @@
   "+components/cdm/common",
   "+components/live_caption",
   "+components/soda",
-  "+media/cdm/supported_cdm_versions.h",
+  "+media/cdm",
   "+ppapi/thunk",
   "+third_party/widevine",
   "+third_party/zxcvbn-cpp"
diff --git a/chrome/browser/component_updater/cros_component_installer_chromeos.cc b/chrome/browser/component_updater/cros_component_installer_chromeos.cc
index fe9c944..5c69489f 100644
--- a/chrome/browser/component_updater/cros_component_installer_chromeos.cc
+++ b/chrome/browser/component_updater/cros_component_installer_chromeos.cc
@@ -39,9 +39,9 @@
      "5.0", "1913a5e0a6cad30b6f03e176177e0d7ed62c5d6700a9c66da556d7c3f5d6a47e"},
     {"cros-termina", ComponentConfig::PolicyType::kEnvVersion, "900.1",
      "e9d960f84f628e1f42d05de4046bb5b3154b6f1f65c08412c6af57a29aecaffb"},
-    {"rtanalytics-light", ComponentConfig::PolicyType::kEnvVersion, "90.0",
+    {"rtanalytics-light", ComponentConfig::PolicyType::kEnvVersion, "92.0",
      "69f09d33c439c2ab55bbbe24b47ab55cb3f6c0bd1f1ef46eefea3216ec925038"},
-    {"rtanalytics-full", ComponentConfig::PolicyType::kEnvVersion, "90.0",
+    {"rtanalytics-full", ComponentConfig::PolicyType::kEnvVersion, "92.0",
      "c93c3e1013c52100a20038b405ac854d69fa889f6dc4fa6f188267051e05e444"},
     {"star-cups-driver", ComponentConfig::PolicyType::kEnvVersion, "1.1",
      "6d24de30f671da5aee6d463d9e446cafe9ddac672800a9defe86877dcde6c466"},
diff --git a/chrome/browser/component_updater/widevine_cdm_component_installer.cc b/chrome/browser/component_updater/widevine_cdm_component_installer.cc
index 0ba001a7..c751e576 100644
--- a/chrome/browser/component_updater/widevine_cdm_component_installer.cc
+++ b/chrome/browser/component_updater/widevine_cdm_component_installer.cc
@@ -35,6 +35,7 @@
 #include "content/public/common/cdm_info.h"
 #include "content/public/common/content_paths.h"
 #include "crypto/sha2.h"
+#include "media/cdm/cdm_capability.h"
 #include "third_party/widevine/cdm/buildflags.h"
 #include "third_party/widevine/cdm/widevine_cdm_common.h"
 
@@ -113,7 +114,7 @@
   // This check must be a subset of the check in VerifyInstallation() to
   // avoid the case where the CDM is accepted by the component updater
   // but not registered.
-  content::CdmCapability capability;
+  media::CdmCapability capability;
   if (!ParseCdmManifest(*manifest, &capability)) {
     VLOG(1) << "Not registering Widevine CDM due to malformed manifest.";
     return;
@@ -216,7 +217,7 @@
     const base::FilePath& install_dir) const {
   base::FilePath cdm_path = GetCdmPathFromInstallDir(install_dir);
 
-  content::CdmCapability capability;
+  media::CdmCapability capability;
   return IsCdmManifestCompatibleWithChrome(manifest) &&
          base::PathExists(cdm_path) && ParseCdmManifest(manifest, &capability);
 }
diff --git a/chrome/browser/extensions/api/declarative_content/content_action.cc b/chrome/browser/extensions/api/declarative_content/content_action.cc
index 256ec61..5a9f3b1 100644
--- a/chrome/browser/extensions/api/declarative_content/content_action.cc
+++ b/chrome/browser/extensions/api/declarative_content/content_action.cc
@@ -362,7 +362,6 @@
 void RequestContentScript::InstructRenderProcessToInject(
     content::WebContents* contents,
     const Extension* extension) const {
-  DCHECK(contents->GetMainFrame()->IsRenderFrameLive());
   ContentScriptTracker::WillExecuteCode(base::PassKey<RequestContentScript>(),
                                         contents->GetMainFrame(), *extension);
 
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 0a0d363..6f7a2c7 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
@@ -6066,6 +6066,131 @@
   EXPECT_EQ(nullptr, content::EvalJs(web_contents(), run_auction_command));
 }
 
+class DeclarativeNetRequestBackForwardCacheBrowserTest
+    : public DeclarativeNetRequestBrowserTest {
+ public:
+  DeclarativeNetRequestBackForwardCacheBrowserTest() {
+    feature_list_.InitWithFeaturesAndParameters(
+        {{features::kBackForwardCache,
+          {{"TimeToLiveInBackForwardCacheInSeconds", "3600"}}}},
+        {features::kBackForwardCacheMemoryControls});
+  }
+
+  // Setups the back forward cache with one entry (a.com) and returns a
+  // RenderFrameDeletedObserver that watches for it being destroyed.
+  std::unique_ptr<content::RenderFrameDeletedObserver>
+  NavigateForBackForwardCache() {
+    GURL url_a(embedded_test_server()->GetURL("a.com", "/title1.html"));
+    GURL url_b(embedded_test_server()->GetURL("b.com", "/title1.html"));
+
+    // 1) Navigate to A.
+    content::RenderFrameHost* rfh_a =
+        ui_test_utils::NavigateToURL(browser(), url_a);
+    auto delete_observer_rfh_a =
+        std::make_unique<content::RenderFrameDeletedObserver>(rfh_a);
+
+    // 2) Navigate to B.
+    content::RenderFrameHost* rfh_b =
+        ui_test_utils::NavigateToURL(browser(), url_b);
+
+    // 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(),
+              content::RenderFrameHost::LifecycleState::kInBackForwardCache);
+    return delete_observer_rfh_a;
+  }
+
+ private:
+  base::test::ScopedFeatureList feature_list_;
+};
+
+// Ensure that Back Forward is cleared on adding dynamic rules.
+IN_PROC_BROWSER_TEST_P(DeclarativeNetRequestBackForwardCacheBrowserTest,
+                       BackForwardCacheClearedOnAddingDynamicRules) {
+  set_config_flags(ConfigFlag::kConfig_HasBackgroundScript);
+
+  // Now block requests to script.js.
+  TestRule rule = CreateGenericRule();
+  rule.condition->url_filter = std::string("script.js");
+  ASSERT_NO_FATAL_FAILURE(LoadExtensionWithRules({rule}));
+
+  auto bfcache_rfh_delete_observer = NavigateForBackForwardCache();
+  const ExtensionId extension_id = last_loaded_extension_id();
+
+  // Add dynamic rule.
+  rule.condition->url_filter = std::string("dynamic.com");
+  ASSERT_NO_FATAL_FAILURE(AddDynamicRules(extension_id, {rule}));
+
+  // Expect that |rfh_a| is destroyed as the cache would get cleared due to
+  // addition of new rule.
+  bfcache_rfh_delete_observer->WaitUntilDeleted();
+}
+
+// Ensure that Back Forward is cleared on updating session rules.
+IN_PROC_BROWSER_TEST_P(DeclarativeNetRequestBackForwardCacheBrowserTest,
+                       BackForwardCacheClearedOnUpdatingSessionRules) {
+  set_config_flags(ConfigFlag::kConfig_HasBackgroundScript);
+
+  // Now block requests to script.js.
+  TestRule rule = CreateGenericRule();
+  rule.condition->url_filter = std::string("script.js");
+  ASSERT_NO_FATAL_FAILURE(LoadExtensionWithRules({rule}));
+
+  auto bfcache_rfh_delete_observer = NavigateForBackForwardCache();
+  const ExtensionId extension_id = last_loaded_extension_id();
+
+  // Add session-scoped rule to block requests to "session.example".
+  rule.condition->url_filter = std::string("session.example");
+  ASSERT_NO_FATAL_FAILURE(UpdateSessionRules(extension_id, {}, {rule}));
+
+  // Expect that |rfh_a| is destroyed as the cache would get cleared due to
+  // addition of new rule.
+  bfcache_rfh_delete_observer->WaitUntilDeleted();
+}
+
+// Ensure that Back Forward is cleared on updating enabled rulesets.
+IN_PROC_BROWSER_TEST_P(DeclarativeNetRequestBackForwardCacheBrowserTest,
+                       BackForwardCacheClearedOnUpdatingEnabledRulesets) {
+  set_config_flags(ConfigFlag::kConfig_HasBackgroundScript);
+
+  std::vector<TestRulesetInfo> rulesets;
+  rulesets.emplace_back("ruleset_1", *ToListValue({CreateGenericRule(1)}));
+  rulesets.emplace_back("ruleset_2", *ToListValue({CreateGenericRule(2)}),
+                        false /* enabled */);
+
+  ASSERT_NO_FATAL_FAILURE(
+      LoadExtensionWithRulesets(rulesets, "test_extension", {} /* hosts */));
+
+  auto bfcache_rfh_delete_observer = NavigateForBackForwardCache();
+  const ExtensionId extension_id = last_loaded_extension_id();
+
+  // Enable |ruleset_2|.
+  ASSERT_NO_FATAL_FAILURE(
+      UpdateEnabledRulesets(last_loaded_extension_id(), {}, {"ruleset_2"}));
+
+  // Expect that |rfh_a| is destroyed as the cache would get cleared due to
+  // addition of new ruleset.
+  bfcache_rfh_delete_observer->WaitUntilDeleted();
+}
+
+// Ensure that Back Forward is cleared on new extension.
+IN_PROC_BROWSER_TEST_P(DeclarativeNetRequestBackForwardCacheBrowserTest,
+                       BackForwardCacheClearedOnAddExtension) {
+  set_config_flags(ConfigFlag::kConfig_HasBackgroundScript);
+
+  auto bfcache_rfh_delete_observer = NavigateForBackForwardCache();
+
+  // Now block requests to script.js.
+  TestRule rule = CreateGenericRule();
+  rule.condition->url_filter = std::string("script.js");
+  ASSERT_NO_FATAL_FAILURE(LoadExtensionWithRules({rule}));
+
+  // Expect that |rfh_a| is destroyed as the cache would get cleared due to
+  // addition of new rule.
+  bfcache_rfh_delete_observer->WaitUntilDeleted();
+}
+
 INSTANTIATE_TEST_SUITE_P(All,
                          DeclarativeNetRequestBrowserTest,
                          ::testing::Values(ExtensionLoadType::PACKED,
@@ -6106,6 +6231,11 @@
                          ::testing::Values(ExtensionLoadType::PACKED,
                                            ExtensionLoadType::UNPACKED));
 
+INSTANTIATE_TEST_SUITE_P(All,
+                         DeclarativeNetRequestBackForwardCacheBrowserTest,
+                         ::testing::Values(ExtensionLoadType::PACKED,
+                                           ExtensionLoadType::UNPACKED));
+
 }  // namespace
 }  // namespace declarative_net_request
 }  // namespace extensions
diff --git a/chrome/browser/extensions/api/tabs/tabs_api.cc b/chrome/browser/extensions/api/tabs/tabs_api.cc
index 6e67ad0..332a4edf 100644
--- a/chrome/browser/extensions/api/tabs/tabs_api.cc
+++ b/chrome/browser/extensions/api/tabs/tabs_api.cc
@@ -1896,6 +1896,9 @@
   return true;
 }
 
+// static
+bool TabsCaptureVisibleTabFunction::disable_throttling_for_test_ = false;
+
 TabsCaptureVisibleTabFunction::TabsCaptureVisibleTabFunction()
     : chrome_details_(this) {
 }
@@ -1972,6 +1975,21 @@
   return RespondNow(Error(CaptureResultToErrorMessage(capture_result)));
 }
 
+void TabsCaptureVisibleTabFunction::GetQuotaLimitHeuristics(
+    QuotaLimitHeuristics* heuristics) const {
+  constexpr base::TimeDelta kSecond = base::TimeDelta::FromSeconds(1);
+  QuotaLimitHeuristic::Config limit = {
+      tabs::MAX_CAPTURE_VISIBLE_TAB_CALLS_PER_SECOND, kSecond};
+
+  heuristics->push_back(std::make_unique<QuotaService::TimedLimit>(
+      limit, std::make_unique<QuotaLimitHeuristic::SingletonBucketMapper>(),
+      "MAX_CAPTURE_VISIBLE_TAB_CALLS_PER_SECOND"));
+}
+
+bool TabsCaptureVisibleTabFunction::ShouldSkipQuotaLimiting() const {
+  return disable_throttling_for_test_;
+}
+
 void TabsCaptureVisibleTabFunction::OnCaptureSuccess(const SkBitmap& bitmap) {
   base::ThreadPool::PostTask(
       FROM_HERE, {base::TaskPriority::USER_VISIBLE},
diff --git a/chrome/browser/extensions/api/tabs/tabs_api.h b/chrome/browser/extensions/api/tabs/tabs_api.h
index 72b5b75..2000882 100644
--- a/chrome/browser/extensions/api/tabs/tabs_api.h
+++ b/chrome/browser/extensions/api/tabs/tabs_api.h
@@ -240,8 +240,15 @@
   TabsCaptureVisibleTabFunction();
   static void RegisterProfilePrefs(user_prefs::PrefRegistrySyncable* registry);
 
+  static void set_disable_throttling_for_tests(
+      bool disable_throttling_for_test) {
+    disable_throttling_for_test_ = disable_throttling_for_test;
+  }
+
   // ExtensionFunction implementation.
   ResponseAction Run() override;
+  void GetQuotaLimitHeuristics(QuotaLimitHeuristics* heuristics) const override;
+  bool ShouldSkipQuotaLimiting() const override;
 
  protected:
   ~TabsCaptureVisibleTabFunction() override {}
@@ -268,6 +275,8 @@
 
   static std::string CaptureResultToErrorMessage(CaptureResult result);
 
+  static bool disable_throttling_for_test_;
+
   DISALLOW_COPY_AND_ASSIGN(TabsCaptureVisibleTabFunction);
 };
 
diff --git a/chrome/browser/extensions/back_forward_cache_browsertest.cc b/chrome/browser/extensions/back_forward_cache_browsertest.cc
index b9142d4..c1d1369 100644
--- a/chrome/browser/extensions/back_forward_cache_browsertest.cc
+++ b/chrome/browser/extensions/back_forward_cache_browsertest.cc
@@ -18,7 +18,8 @@
     feature_list_.InitWithFeaturesAndParameters(
         {{features::kBackForwardCache,
           {{"content_injection_supported",
-            allow_content_scripts ? "true" : "false"}}}},
+            allow_content_scripts ? "true" : "false"},
+           {"TimeToLiveInBackForwardCacheInSeconds", "3600"}}}},
         {features::kBackForwardCacheMemoryControls});
   }
 
@@ -136,4 +137,69 @@
             content::RenderFrameHost::LifecycleState::kInBackForwardCache);
 }
 
+IN_PROC_BROWSER_TEST_F(ExtensionBackForwardCacheBrowserTest,
+                       UnloadExtensionFlushCache) {
+  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"));
+
+  // Load the extension so we can unload it later.
+  const Extension* extension =
+      LoadExtension(test_data_dir_.AppendASCII("back_forward_cache")
+                        .AppendASCII("content_css"));
+  ASSERT_TRUE(extension);
+
+  // 1) Navigate to A.
+  content::RenderFrameHost* rfh_a =
+      ui_test_utils::NavigateToURL(browser(), url_a);
+  content::RenderFrameDeletedObserver delete_observer_rfh_a(rfh_a);
+
+  // 2) Navigate to B.
+  content::RenderFrameHost* rfh_b =
+      ui_test_utils::NavigateToURL(browser(), url_b);
+  content::RenderFrameDeletedObserver delete_observer_rfh_b(rfh_b);
+
+  // 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(),
+            content::RenderFrameHost::LifecycleState::kInBackForwardCache);
+
+  // 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.
+  delete_observer_rfh_a.WaitUntilDeleted();
+}
+
+IN_PROC_BROWSER_TEST_F(ExtensionBackForwardCacheBrowserTest,
+                       LoadExtensionFlushCache) {
+  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"));
+
+  // 1) Navigate to A.
+  content::RenderFrameHost* rfh_a =
+      ui_test_utils::NavigateToURL(browser(), url_a);
+  content::RenderFrameDeletedObserver delete_observer_rfh_a(rfh_a);
+
+  // 2) Navigate to B.
+  content::RenderFrameHost* rfh_b =
+      ui_test_utils::NavigateToURL(browser(), url_b);
+  content::RenderFrameDeletedObserver delete_observer_rfh_b(rfh_b);
+
+  // 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(),
+            content::RenderFrameHost::LifecycleState::kInBackForwardCache);
+
+  // Now load the extension after something is in the cache.
+  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.
+  delete_observer_rfh_a.WaitUntilDeleted();
+}
+
 }  // namespace extensions
diff --git a/chrome/browser/extensions/chrome_extensions_browser_client.cc b/chrome/browser/extensions/chrome_extensions_browser_client.cc
index 7319225cf..fc6ed4a8 100644
--- a/chrome/browser/extensions/chrome_extensions_browser_client.cc
+++ b/chrome/browser/extensions/chrome_extensions_browser_client.cc
@@ -415,6 +415,10 @@
       MenuItem::ExtensionKey("", embedder_process_id, view_instance_id));
 }
 
+void ChromeExtensionsBrowserClient::ClearBackForwardCache() {
+  ExtensionTabUtil::ClearBackForwardCache();
+}
+
 void ChromeExtensionsBrowserClient::AttachExtensionTaskManagerTag(
     content::WebContents* web_contents,
     mojom::ViewType view_type) {
diff --git a/chrome/browser/extensions/chrome_extensions_browser_client.h b/chrome/browser/extensions/chrome_extensions_browser_client.h
index e3ba2950..31792c0 100644
--- a/chrome/browser/extensions/chrome_extensions_browser_client.h
+++ b/chrome/browser/extensions/chrome_extensions_browser_client.h
@@ -131,6 +131,7 @@
   void CleanUpWebView(content::BrowserContext* browser_context,
                       int embedder_process_id,
                       int view_instance_id) override;
+  void ClearBackForwardCache() override;
   void AttachExtensionTaskManagerTag(content::WebContents* web_contents,
                                      mojom::ViewType view_type) override;
   scoped_refptr<update_client::UpdateClient> CreateUpdateClient(
diff --git a/chrome/browser/extensions/extension_tab_util.cc b/chrome/browser/extensions/extension_tab_util.cc
index abf6362c..f46d826c4 100644
--- a/chrome/browser/extensions/extension_tab_util.cc
+++ b/chrome/browser/extensions/extension_tab_util.cc
@@ -42,6 +42,7 @@
 #include "components/sessions/content/session_tab_helper.h"
 #include "components/tab_groups/tab_group_id.h"
 #include "components/url_formatter/url_fixer.h"
+#include "content/public/browser/back_forward_cache.h"
 #include "content/public/browser/favicon_status.h"
 #include "content/public/browser/navigation_controller.h"
 #include "content/public/browser/navigation_entry.h"
@@ -973,4 +974,10 @@
   return api::tabs::TAB_STATUS_COMPLETE;
 }
 
+void ExtensionTabUtil::ClearBackForwardCache() {
+  ForEachTab(base::BindRepeating([](WebContents* web_contents) {
+    web_contents->GetController().GetBackForwardCache().Flush();
+  }));
+}
+
 }  // namespace extensions
diff --git a/chrome/browser/extensions/extension_tab_util.h b/chrome/browser/extensions/extension_tab_util.h
index d2a8faa..b2843a02 100644
--- a/chrome/browser/extensions/extension_tab_util.h
+++ b/chrome/browser/extensions/extension_tab_util.h
@@ -253,6 +253,10 @@
   // some non-const member functions of |contents|, but actually leaves it
   // unmodified.
   static api::tabs::TabStatus GetLoadingStatus(content::WebContents* contents);
+
+  // Clears the back-forward cache for all active tabs across all browser
+  // contexts.
+  static void ClearBackForwardCache();
 };
 
 }  // namespace extensions
diff --git a/chrome/browser/extensions/extension_tabs_apitest.cc b/chrome/browser/extensions/extension_tabs_apitest.cc
index bc724e4..4194696 100644
--- a/chrome/browser/extensions/extension_tabs_apitest.cc
+++ b/chrome/browser/extensions/extension_tabs_apitest.cc
@@ -7,6 +7,7 @@
 #include "base/strings/stringprintf.h"
 #include "build/build_config.h"
 #include "build/chromeos_buildflags.h"
+#include "chrome/browser/extensions/api/tabs/tabs_api.h"
 #include "chrome/browser/extensions/extension_tab_util.h"
 #include "chrome/browser/prefs/incognito_mode_prefs.h"
 #include "chrome/browser/profiles/profile.h"
@@ -183,6 +184,8 @@
   ExtensionApiCaptureTest() {}
 
   void SetUp() override {
+    extensions::TabsCaptureVisibleTabFunction::set_disable_throttling_for_tests(
+        true);
     EnablePixelOutput();
     ExtensionApiTabTest::SetUp();
   }
diff --git a/chrome/browser/extensions/tab_helper.cc b/chrome/browser/extensions/tab_helper.cc
index 7106cc7..bb6309a 100644
--- a/chrome/browser/extensions/tab_helper.cc
+++ b/chrome/browser/extensions/tab_helper.cc
@@ -34,6 +34,7 @@
 #include "chrome/common/extensions/manifest_handlers/app_launch_info.h"
 #include "chrome/common/url_constants.h"
 #include "components/sessions/content/session_tab_helper.h"
+#include "content/public/browser/back_forward_cache.h"
 #include "content/public/browser/invalidate_type.h"
 #include "content/public/browser/navigation_controller.h"
 #include "content/public/browser/navigation_entry.h"
@@ -180,6 +181,9 @@
   ExtensionWebContentsObserver::GetForWebContents(web_contents)->dispatcher()->
       set_delegate(this);
 
+  registry_observation_.Observe(
+      ExtensionRegistry::Get(web_contents->GetBrowserContext()));
+
   BookmarkManagerPrivateDragEventRouter::CreateForWebContents(web_contents);
 }
 
@@ -194,13 +198,6 @@
   }
   extension_app_ = extension;
 
-  if (extension_app_) {
-    registry_observation_.Observe(
-        ExtensionRegistry::Get(web_contents()->GetBrowserContext()));
-  } else {
-    registry_observation_.Reset();
-  }
-
   UpdateExtensionAppIcon(extension_app_);
 
 #if BUILDFLAG(ENABLE_SESSION_SERVICE)
@@ -386,10 +383,21 @@
   return web_contents();
 }
 
+void TabHelper::OnExtensionLoaded(content::BrowserContext* browser_context,
+                                  const Extension* extension) {
+  // Clear the back forward cache for the associated tab to accommodate for any
+  // side effects of loading/unloading the extension.
+  web_contents()->GetController().GetBackForwardCache().Flush();
+}
+
 void TabHelper::OnExtensionUnloaded(content::BrowserContext* browser_context,
                                     const Extension* extension,
                                     UnloadedExtensionReason reason) {
-  DCHECK(extension_app_);
+  // Clear the back forward cache for the associated tab to accommodate for any
+  // side effects of loading/unloading the extension.
+  web_contents()->GetController().GetBackForwardCache().Flush();
+  if (!extension_app_)
+    return;
   if (extension == extension_app_)
     SetExtensionApp(nullptr);
 }
diff --git a/chrome/browser/extensions/tab_helper.h b/chrome/browser/extensions/tab_helper.h
index 2786909f..61ac854a 100644
--- a/chrome/browser/extensions/tab_helper.h
+++ b/chrome/browser/extensions/tab_helper.h
@@ -116,6 +116,8 @@
   content::WebContents* GetAssociatedWebContents() const override;
 
   // ExtensionRegistryObserver:
+  void OnExtensionLoaded(content::BrowserContext* browser_context,
+                         const Extension* extension) override;
   void OnExtensionUnloaded(content::BrowserContext* browser_context,
                            const Extension* extension,
                            UnloadedExtensionReason reason) override;
diff --git a/chrome/browser/feed/android/java/src/org/chromium/chrome/browser/feed/webfeed/WebFeedBridge.java b/chrome/browser/feed/android/java/src/org/chromium/chrome/browser/feed/webfeed/WebFeedBridge.java
index 77a32eb..daf74b6 100644
--- a/chrome/browser/feed/android/java/src/org/chromium/chrome/browser/feed/webfeed/WebFeedBridge.java
+++ b/chrome/browser/feed/android/java/src/org/chromium/chrome/browser/feed/webfeed/WebFeedBridge.java
@@ -24,6 +24,13 @@
  */
 @JNINamespace("feed")
 public class WebFeedBridge {
+    // Access to JNI test hooks for other libraries. This can go away once more Feed code is
+    // migrated to chrome/browser/feed.
+    public static org.chromium.base.JniStaticTestMocker<WebFeedBridge.Natives>
+    getTestHooksForTesting() {
+        return WebFeedBridgeJni.TEST_HOOKS;
+    }
+
     // TODO(crbug/1152592): remove members needed only for returning mock results.
     private static Random sRandom = new Random();
     private static int sCounter;
@@ -222,6 +229,11 @@
         return sRandom.nextBoolean();
     }
 
+    /** Returns whether the user subscribes to at least one Web Feed. */
+    public static boolean isWebFeedSubscriber() {
+        return WebFeedBridgeJni.get().isWebFeedSubscriber();
+    }
+
     /** This is deprecated, do not use. */
     @Deprecated
     public static class FollowedIds {
@@ -262,8 +274,9 @@
         }
     }
 
+    @VisibleForTesting
     @NativeMethods
-    interface Natives {
+    public interface Natives {
         void followWebFeed(WebFeedPageInformation pageInfo, Callback<FollowResults> callback);
         void followWebFeedById(byte[] webFeedId, Callback<FollowResults> callback);
         void unfollowWebFeed(byte[] webFeedId, Callback<UnfollowResults> callback);
@@ -273,5 +286,6 @@
         void getAllSubscriptions(Callback<Object[]> callback);
         void refreshSubscriptions(Callback<Boolean> callback);
         void getRecentVisitCountsToHost(GURL url, Callback<int[]> callback);
+        boolean isWebFeedSubscriber();
     }
 }
diff --git a/chrome/browser/password_manager/android/BUILD.gn b/chrome/browser/password_manager/android/BUILD.gn
index 252a4e2d..176293b 100644
--- a/chrome/browser/password_manager/android/BUILD.gn
+++ b/chrome/browser/password_manager/android/BUILD.gn
@@ -13,11 +13,13 @@
     "//components/password_manager/core/browser:password_manager_java_enums",
     "//third_party/androidx:androidx_annotation_annotation_java",
     "//third_party/androidx:androidx_appcompat_appcompat_java",
+    "//third_party/androidx:androidx_core_core_java",
     "//third_party/androidx:androidx_fragment_fragment_java",
     "//ui/android:ui_no_recycler_view_java",
     "//url:gurl_java",
   ]
   sources = [
+    "java/src/org/chromium/chrome/browser/password_manager/BiometricAuthenticatorBridge.java",
     "java/src/org/chromium/chrome/browser/password_manager/ConfirmationDialogHelper.java",
     "java/src/org/chromium/chrome/browser/password_manager/PasswordManagerHelper.java",
     "java/src/org/chromium/chrome/browser/password_manager/PasswordScriptsFetcherBridge.java",
@@ -35,6 +37,7 @@
 generate_jni("jni_headers") {
   visibility = [ "//chrome/browser" ]
   sources = [
+    "java/src/org/chromium/chrome/browser/password_manager/BiometricAuthenticatorBridge.java",
     "java/src/org/chromium/chrome/browser/password_manager/PasswordScriptsFetcherBridge.java",
     "java/src/org/chromium/chrome/browser/password_manager/PasswordStoreBridge.java",
     "java/src/org/chromium/chrome/browser/password_manager/PasswordStoreCredential.java",
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/password_manager/BiometricAuthenticatorBridge.java b/chrome/browser/password_manager/android/java/src/org/chromium/chrome/browser/password_manager/BiometricAuthenticatorBridge.java
similarity index 100%
rename from chrome/android/java/src/org/chromium/chrome/browser/password_manager/BiometricAuthenticatorBridge.java
rename to chrome/browser/password_manager/android/java/src/org/chromium/chrome/browser/password_manager/BiometricAuthenticatorBridge.java
diff --git a/chrome/browser/password_manager/biometric_authenticator_android.cc b/chrome/browser/password_manager/biometric_authenticator_android.cc
index 71995da..0bb835b 100644
--- a/chrome/browser/password_manager/biometric_authenticator_android.cc
+++ b/chrome/browser/password_manager/biometric_authenticator_android.cc
@@ -12,7 +12,7 @@
 #include "base/callback.h"
 #include "base/feature_list.h"
 #include "base/location.h"
-#include "chrome/android/chrome_jni_headers/BiometricAuthenticatorBridge_jni.h"
+#include "chrome/browser/password_manager/android/jni_headers/BiometricAuthenticatorBridge_jni.h"
 #include "components/autofill/core/common/autofill_features.h"
 #include "components/password_manager/core/browser/biometric_authenticator.h"
 #include "components/password_manager/core/browser/origin_credential_store.h"
diff --git a/chrome/browser/privacy_sandbox/privacy_sandbox_settings.cc b/chrome/browser/privacy_sandbox/privacy_sandbox_settings.cc
index af8b071..9125e80 100644
--- a/chrome/browser/privacy_sandbox/privacy_sandbox_settings.cc
+++ b/chrome/browser/privacy_sandbox/privacy_sandbox_settings.cc
@@ -168,14 +168,7 @@
 PrivacySandboxSettings::~PrivacySandboxSettings() = default;
 
 /*static*/ bool PrivacySandboxSettings::PrivacySandboxSettingsFunctional() {
-  // The order in which the features are checked matters here. Preventing
-  // checking for the PrivacySandboxSettings if all the APIs are disabled
-  // avoids polluting rollout data, as it stops clients reporting as active
-  // while the feature is enabled but not accessible.
-  return (base::FeatureList::IsEnabled(
-              blink::features::kInterestCohortAPIOriginTrial) ||
-          base::FeatureList::IsEnabled(features::kConversionMeasurement)) &&
-         base::FeatureList::IsEnabled(features::kPrivacySandboxSettings);
+  return base::FeatureList::IsEnabled(features::kPrivacySandboxSettings);
 }
 
 bool PrivacySandboxSettings::IsFlocAllowed(
diff --git a/chrome/browser/privacy_sandbox/privacy_sandbox_settings.h b/chrome/browser/privacy_sandbox/privacy_sandbox_settings.h
index 3f8d01f..056647b 100644
--- a/chrome/browser/privacy_sandbox/privacy_sandbox_settings.h
+++ b/chrome/browser/privacy_sandbox/privacy_sandbox_settings.h
@@ -52,10 +52,9 @@
                          signin::IdentityManager* identity_manager);
   ~PrivacySandboxSettings() override;
 
-  // Returns true when the privacy sandbox settings feature is enabled, and at
-  // least one of the privacy sandbox APIs is also enabled. This function,
-  // rather than direct inspection of the feature itself, should be used to
-  // determine if the privacy sandbox is available to users.
+  // Returns true when the privacy sandbox settings feature is enabled. This
+  // function, rather than direct inspection of the feature itself, should be
+  // used to determine if the privacy sandbox is available to users.
   // TODO(crbug.com/1174572) Remove this when one API is fully launched.
   static bool PrivacySandboxSettingsFunctional();
 
diff --git a/chrome/browser/privacy_sandbox/privacy_sandbox_settings_unittest.cc b/chrome/browser/privacy_sandbox/privacy_sandbox_settings_unittest.cc
index 0b02e925..3579df2 100644
--- a/chrome/browser/privacy_sandbox/privacy_sandbox_settings_unittest.cc
+++ b/chrome/browser/privacy_sandbox/privacy_sandbox_settings_unittest.cc
@@ -163,32 +163,18 @@
 };
 
 TEST_F(PrivacySandboxSettingsTest, PrivacySandboxSettingsFunctional) {
-  // Check that the settings are only reported as functional when at least one
-  // privacy sandbox API is enabled.
-  feature_list()->InitWithFeatures(
-      {features::kPrivacySandboxSettings, features::kConversionMeasurement},
-      {});
-  EXPECT_TRUE(privacy_sandbox_settings()->PrivacySandboxSettingsFunctional());
-  feature_list()->Reset();
-
-  feature_list()->InitWithFeatures(
-      {features::kPrivacySandboxSettings,
-       blink::features::kInterestCohortAPIOriginTrial},
-      {features::kConversionMeasurement});
-  EXPECT_TRUE(privacy_sandbox_settings()->PrivacySandboxSettingsFunctional());
-  feature_list()->Reset();
-
   feature_list()->InitWithFeatures(
       {features::kConversionMeasurement,
        blink::features::kInterestCohortAPIOriginTrial},
-      {features::kConversionMeasurement});
+      {features::kPrivacySandboxSettings});
   EXPECT_FALSE(privacy_sandbox_settings()->PrivacySandboxSettingsFunctional());
   feature_list()->Reset();
 
-  feature_list()->InitWithFeatures({features::kPrivacySandboxSettings},
-                                   {features::kConversionMeasurement});
-  EXPECT_FALSE(privacy_sandbox_settings()->PrivacySandboxSettingsFunctional());
-  feature_list()->Reset();
+  feature_list()->InitWithFeatures(
+      {features::kPrivacySandboxSettings},
+      {features::kConversionMeasurement,
+       blink::features::kInterestCohortAPIOriginTrial});
+  EXPECT_TRUE(privacy_sandbox_settings()->PrivacySandboxSettingsFunctional());
 }
 
 TEST_F(PrivacySandboxSettingsTest, CookieSettingAppliesWhenUiDisabled) {
diff --git a/chrome/browser/resources/new_tab_page/app.js b/chrome/browser/resources/new_tab_page/app.js
index 2a1106b7..9ee086b3 100644
--- a/chrome/browser/resources/new_tab_page/app.js
+++ b/chrome/browser/resources/new_tab_page/app.js
@@ -405,8 +405,8 @@
   async onLazyRendered_() {
     // Instantiate modules even if |modulesEnabled| is false to counterfactually
     // trigger a HaTS survey in a potential control group.
-    if (!loadTimeData.getBoolean('modulesLoadEnabled') &&
-        !loadTimeData.getBoolean('modulesEnabled')) {
+    if (!loadTimeData.getBoolean('modulesLoadEnabled') ||
+        loadTimeData.getBoolean('modulesEnabled')) {
       return;
     }
     const modules = await ModuleRegistry.getInstance().initializeModules(
diff --git a/chrome/browser/resources/pdf/viewport.js b/chrome/browser/resources/pdf/viewport.js
index ae56851..4fa1fa66 100644
--- a/chrome/browser/resources/pdf/viewport.js
+++ b/chrome/browser/resources/pdf/viewport.js
@@ -341,6 +341,16 @@
    */
   getViewportRect_() {
     const zoom = this.getZoom();
+    // Zoom can be 0 in the case of a PDF that is in a hidden iframe. Avoid
+    // returning undefined values in this case. See https://crbug.com/1202725.
+    if (zoom === 0) {
+      return {
+        x: 0,
+        y: 0,
+        width: 0,
+        height: 0,
+      };
+    }
     return {
       x: this.position.x / zoom,
       y: this.position.y / zoom,
diff --git a/chrome/browser/resources/welcome/shared/onboarding_background.html b/chrome/browser/resources/welcome/shared/onboarding_background.html
index 62198a8..6b9f2f1f 100644
--- a/chrome/browser/resources/welcome/shared/onboarding_background.html
+++ b/chrome/browser/resources/welcome/shared/onboarding_background.html
@@ -252,17 +252,17 @@
 
   #green-triangle {
     -webkit-mask: url(../images/background_svgs/triangle.svg);
-    animation: green-triangle 2s infinite linear;
+    animation: green-triangle 3s infinite cubic-bezier(.63,.03,.41,.98);
     background-color: var(--welcome-green);
     left: 1813px;
-    top: 151px;
-    transform: translate(-50%, -50%) rotate(0deg);
-    transform-origin: 250px 250px;
+    top: 0;
+    transform: translate(-50%, -50%) rotate(-10deg);
+    transform-origin: 200px 300px;
   }
 
   @keyframes green-triangle {
     to {
-      transform: translate(-50%, -50%) rotate(360deg);
+      transform: translate(-50%, -50%) rotate(350deg);
     }
   }
 
@@ -288,7 +288,7 @@
   #green-connectagon {
     --connectagon-shape-color: var(--welcome-green);
     --connectagon-connector-color: var(--welcome-green-tinted);
-    animation: green-connectagon 8s alternate infinite linear;
+    animation: green-connectagon 6s alternate infinite linear;
     left: 851px;
     top: 766px;
     transform: translate(-50%, -50%) rotate(139deg);
diff --git a/chrome/browser/service_sandbox_type.h b/chrome/browser/service_sandbox_type.h
index 336d9983..d801122 100644
--- a/chrome/browser/service_sandbox_type.h
+++ b/chrome/browser/service_sandbox_type.h
@@ -162,7 +162,7 @@
 template <>
 inline sandbox::policy::SandboxType
 content::GetServiceSandboxType<sharing::mojom::Sharing>() {
-  return sandbox::policy::SandboxType::kSharingService;
+  return sandbox::policy::SandboxType::kService;
 }
 #endif  // !defined(OS_MAC)
 
diff --git a/chrome/browser/ui/BUILD.gn b/chrome/browser/ui/BUILD.gn
index aa3e892..b9602700b 100644
--- a/chrome/browser/ui/BUILD.gn
+++ b/chrome/browser/ui/BUILD.gn
@@ -2800,6 +2800,7 @@
         ]
       }
       deps += [
+        "//chromeos/components/demo_mode_app_ui",
         "//chromeos/components/file_manager:file_manager_ui",
         "//chromeos/components/sample_system_web_app_ui",
         "//chromeos/components/telemetry_extension_ui",
diff --git a/chrome/browser/ui/android/strings/android_chrome_strings.grd b/chrome/browser/ui/android/strings/android_chrome_strings.grd
index fd8ff9c..c6b3247f 100644
--- a/chrome/browser/ui/android/strings/android_chrome_strings.grd
+++ b/chrome/browser/ui/android/strings/android_chrome_strings.grd
@@ -221,7 +221,7 @@
       <message name="IDS_NOTIFICATION_CATEGORY_GROUP_SITES" desc='Subheading for "Sites" section of a list of notification categories. [CHAR-LIMIT=32]'>
         Sites
       </message>
-      <message name="IDS_NOTIFICATION_CATEGORY_INCOGNITO" desc="Label for notification that indicates incognito mode is active, within a list of notification categories. [CHAR-LIMIT=32]">
+      <message name="IDS_NOTIFICATION_CATEGORY_INCOGNITO" desc="Label for notification that indicates Incognito mode is active, within a list of notification categories. [CHAR-LIMIT=32]">
         Incognito
       </message>
       <message name="IDS_NOTIFICATION_CATEGORY_SHARING" desc="Label for sharing-related notifications, within a list of notification categories. [CHAR-LIMIT=32]">
@@ -1947,14 +1947,14 @@
       <message name="IDS_ACCESSIBILITY_TAB_SWITCHER_STANDARD_STACK" desc="Content description for the standard stack selection 'button'.">
         Standard tabs
       </message>
-      <message name="IDS_ACCESSIBILITY_TAB_SWITCHER_INCOGNITO_STACK" desc="Content description for the incognito stack selection 'button'.">
+      <message name="IDS_ACCESSIBILITY_TAB_SWITCHER_INCOGNITO_STACK" desc="Content description for the Incognito stack selection 'button'.">
         Incognito tabs
       </message>
       <message name="IDS_ACCESSIBILITY_TAB_SWITCHER_STANDARD_STACK_SELECTED" desc="Announcement when the standard tab stack is selected.">
         Switched to standard tabs
       </message>
-      <message name="IDS_ACCESSIBILITY_TAB_SWITCHER_INCOGNITO_STACK_SELECTED" desc="Announcement when the incognito tab stack is selected.">
-        Switched to incognito tabs
+      <message name="IDS_ACCESSIBILITY_TAB_SWITCHER_INCOGNITO_STACK_SELECTED" desc="Announcement when the Incognito tab stack is selected.">
+        Switched to Incognito tabs
       </message>
       <message name="IDS_ACCESSIBILITY_TAB_SWITCHER_UNDO_TAB_CLOSED" desc="Content description for the undo tab closed 'button.">
         Reopen closed tab
@@ -2136,8 +2136,8 @@
       <message name="IDS_CONTEXTMENU_OPEN_IN_NEW_TAB_GROUP" desc="Context sensitive menu item to open the selected link in a new tab in a new group. [CHAR-LIMIT=30]">
         Open in new tab in group
       </message>
-      <message name="IDS_CONTEXTMENU_OPEN_IN_INCOGNITO_TAB" desc="Context sensitive menu item to open the selected link in a new incognito tab. [CHAR-LIMIT=30]">
-        Open in incognito tab
+      <message name="IDS_CONTEXTMENU_OPEN_IN_INCOGNITO_TAB" desc="Context sensitive menu item to open the selected link in a new Incognito tab. [CHAR-LIMIT=30]">
+        Open in Incognito tab
       </message>
       <message name="IDS_CONTEXTMENU_CALL" desc="Context sensitive menu for calling phone number. [CHAR-LIMIT=30]">
         Call
@@ -2211,8 +2211,8 @@
       <message name="IDS_CONTEXTMENU_OPEN_IN_NEW_CHROME_TAB" desc="Context sensitive menu item to open the selected link in a new Chrome tab from Chrome Custom Tab. [CHAR-LIMIT=30]">
         Open in new Chrome tab
       </message>
-      <message name="IDS_CONTEXTMENU_OPEN_IN_CHROME_INCOGNITO_TAB" desc="Context sensitive menu item to open the selected link in a Chrome incognito tab from Chrome Custom Tab. [CHAR-LIMIT=30]">
-        Open in incognito tab
+      <message name="IDS_CONTEXTMENU_OPEN_IN_CHROME_INCOGNITO_TAB" desc="Context sensitive menu item to open the selected link in a Chrome Incognito tab from Chrome Custom Tab. [CHAR-LIMIT=30]">
+        Open in Incognito tab
       </message>
       <message name="IDS_CONTEXTMENU_OPEN_IN_EPHEMERAL_TAB" desc="Context-sensitive menu item to open a quick preview of the selected linked page.   Note that 'preview' is a verb, not a noun. We're also labeling it *New* to draw attention to it when first released. The selected link will open in an overlay panel on top of the current tab which will go away easily too. [CHAR-LIMIT=30]">
         Preview page <ph name="BEGIN_NEW">&lt;new&gt;</ph>New<ph name="END_NEW">&lt;/new&gt;</ph>
@@ -2355,7 +2355,7 @@
       <message name="IDS_DOWNLOAD_NOTIFICATION_PENDING_ANOTHER_DOWNLOAD" desc="Download notification to be displayed when a download has been scheduled but has not started being fetched from the network because another download is currently being downloaded.">
         Waiting for another download…
       </message>
-      <message name="IDS_DOWNLOAD_NOTIFICATION_INCOGNITO_SUBTEXT" desc="Displayed in incognito mode download notification as subtext.">
+      <message name="IDS_DOWNLOAD_NOTIFICATION_INCOGNITO_SUBTEXT" desc="Displayed in Incognito mode download notification as subtext.">
         Incognito tab
       </message>
       <message name="IDS_DOWNLOAD_CANT_OPEN_FILE" desc="Toast that appears when a downloaded file can't be opened.">
@@ -2574,11 +2574,11 @@
       </message>
 
       <!-- Close Incognito tabs notification messages -->
-      <message name="IDS_CLOSE_ALL_INCOGNITO_NOTIFICATION_TITLE" desc="Title on the notification that closes all incognito tabs on Android N+.">
+      <message name="IDS_CLOSE_ALL_INCOGNITO_NOTIFICATION_TITLE" desc="Title on the notification that closes all Incognito tabs on Android N+.">
         Incognito Tabs
       </message>
-      <message name="IDS_CLOSE_ALL_INCOGNITO_NOTIFICATION" desc="Message on the notification that closes all incognito tabs.">
-        Close all incognito tabs
+      <message name="IDS_CLOSE_ALL_INCOGNITO_NOTIFICATION" desc="Message on the notification that closes all Incognito tabs.">
+        Close all Incognito tabs
       </message>
 
       <!-- Autofill/Wallet integration prompt -->
@@ -2628,12 +2628,12 @@
       <message name="IDS_SIGNIN_ADD_ACCOUNT_TO_DEVICE" desc="Text for adding another Google Account to device in the web sign-in flow and account picker dialog, users will be asked to add an account to his device when this text is tapped">
         Add account to device
       </message>
-      <message name="IDS_SIGNIN_INCOGNITO_BUTTON" desc="Button text to open the incognito interstitial in the web sign-in account picker bottom sheet">
+      <message name="IDS_SIGNIN_INCOGNITO_BUTTON" desc="Button text to open the Incognito interstitial in the web sign-in account picker bottom sheet">
         Open an Incognito tab
       </message>
 
       <!-- Strings for Incognito Interstitial. -->
-      <message name="IDS_INCOGNITO_INTERSTITIAL_TITLE" desc="The title of the incognito interstitial bottom sheet.">
+      <message name="IDS_INCOGNITO_INTERSTITIAL_TITLE" desc="The title of the Incognito interstitial bottom sheet.">
         Use Incognito to sign in
       </message>
       <message name="IDS_INCOGNITO_INTERSTITIAL_MESSAGE" desc="The content of Incognito interstitial which is shown when a user is in the sign-in flow and they want to go Incognito mode to continue signing in. Before they go Incognito mode they would be presented with an Incognito interstitial and this message is part of that interstitial which would inform how activity in Incognito may be visbile to others and how data from the Incognito session can be cleared.">
@@ -2963,8 +2963,8 @@
       <message name="IDS_BUTTON_NEW_TAB" desc="Button name for opening a new tab.">
         New tab
       </message>
-      <message name="IDS_BUTTON_NEW_INCOGNITO_TAB" desc="Tooltip for the button to open a new incognito browser tab.">
-        New incognito tab
+      <message name="IDS_BUTTON_NEW_INCOGNITO_TAB" desc="Tooltip for the button to open a new Incognito browser tab.">
+        New Incognito tab
       </message>
       <message name="IDS_CUSTOM_TAB_AMP_PUBLISHER_URL" desc="Text shown in a Custom Tab toolbar for a web page that is hosted by the Google content delivery network but was originally published by someone else.">
         From <ph name="PUBLISHER_ORIGIN">&lt;pub&gt;%1$s&lt;/pub&gt;<ex>example.com</ex></ph> – <ph name="BEGIN_DEEMPHASIZED">&lt;bg&gt;</ph>delivered by Google<ph name="END_DEEMPHASIZED">&lt;/bg&gt;</ph>
@@ -3019,8 +3019,8 @@
       <message name="IDS_MENU_NEW_TAB" desc="Menu item for opening a new tab. [CHAR-LIMIT=27]">
         New tab
       </message>
-      <message name="IDS_MENU_NEW_INCOGNITO_TAB" desc="Menu item for opening a new incognito tab that facilitates pseudononymous browsing. [CHAR-LIMIT=27]" meaning="Android menu">
-        New incognito tab
+      <message name="IDS_MENU_NEW_INCOGNITO_TAB" desc="Menu item for opening a new Incognito tab that facilitates pseudononymous browsing. [CHAR-LIMIT=27]" meaning="Android menu">
+        New Incognito tab
       </message>
       <message name="IDS_MENU_BOOKMARKS" desc="Menu item for opening the bookmarks page that contains all the user's bookmarks. [CHAR-LIMIT=27]">
         Bookmarks
@@ -3070,8 +3070,8 @@
       <message name="IDS_MENU_CLOSE_ALL_TABS" desc="Menu item for closing all open tabs. [CHAR-LIMIT=27]">
         Close all tabs
       </message>
-      <message name="IDS_MENU_CLOSE_ALL_INCOGNITO_TABS" desc="Menu item for closing all open incognito tabs. [CHAR-LIMIT=27]">
-        Close incognito tabs
+      <message name="IDS_MENU_CLOSE_ALL_INCOGNITO_TABS" desc="Menu item for closing all open Incognito tabs. [CHAR-LIMIT=27]">
+        Close Incognito tabs
       </message>
       <message name="IDS_MENU_GROUP_TABS" desc="Menu item for grouping tabs. [CHAR-LIMIT=27]">
         Group tabs
@@ -3341,14 +3341,14 @@
       <message name="IDS_ACCESSIBILITY_NEW_TAB_PAGE" desc="Accessibility text to read aloud when the user focuses the new tab view.">
         New tab
       </message>
-      <message name="IDS_ACCESSIBILITY_NEW_INCOGNITO_TAB_PAGE" desc="Accessibility text to read aloud when the user focuses the new incognito tab view.">
-        New incognito tab
+      <message name="IDS_ACCESSIBILITY_NEW_INCOGNITO_TAB_PAGE" desc="Accessibility text to read aloud when the user focuses the new Incognito tab view.">
+        New Incognito tab
       </message>
       <message name="IDS_ACCESSIBILITY_TOOLBAR_BTN_NEW_TAB" desc="Content description for the new tab button.">
         New tab
       </message>
-      <message name="IDS_ACCESSIBILITY_TOOLBAR_BTN_NEW_INCOGNITO_TAB" desc="Content description for the new incognito tab button.">
-        New incognito tab
+      <message name="IDS_ACCESSIBILITY_TOOLBAR_BTN_NEW_INCOGNITO_TAB" desc="Content description for the new Incognito tab button.">
+        New Incognito tab
       </message>
       <message name="IDS_ACCESSIBILITY_INCOGNITO_BADGE" desc="Content description for the badge indicating that the user is in Incognito mode.">
         Incognito mode
@@ -3383,11 +3383,11 @@
       <message name="IDS_ACCESSIBILITY_TABSTRIP_BTN_EMPTY_NEW_TAB" desc="Content description for the new tab button.">
         New tab
       </message>
-      <message name="IDS_ACCESSIBILITY_TABSTRIP_BTN_INCOGNITO_TOGGLE_STANDARD" desc="Content description for the incognito tabs toggle button when viewing standard tabs.">
-        Enter incognito mode
+      <message name="IDS_ACCESSIBILITY_TABSTRIP_BTN_INCOGNITO_TOGGLE_STANDARD" desc="Content description for the Incognito tabs toggle button when viewing standard tabs.">
+        Enter Incognito mode
       </message>
-      <message name="IDS_ACCESSIBILITY_TABSTRIP_BTN_INCOGNITO_TOGGLE_INCOGNITO" desc="Content description for the incognito tabs toggle button when viewing incognito tabs.">
-        Leave incognito mode
+      <message name="IDS_ACCESSIBILITY_TABSTRIP_BTN_INCOGNITO_TOGGLE_INCOGNITO" desc="Content description for the Incognito tabs toggle button when viewing Incognito tabs.">
+        Leave Incognito mode
       </message>
       <message name="IDS_ACCESSIBILITY_TABSTRIP_IDENTIFIER" desc="Content description for tabs that are not selected">
         Tab
@@ -3395,10 +3395,10 @@
       <message name="IDS_ACCESSIBILITY_TABSTRIP_IDENTIFIER_SELECTED" desc="Content description for the tab that is selected">
         Selected Tab
       </message>
-      <message name="IDS_ACCESSIBILITY_TABSTRIP_INCOGNITO_IDENTIFIER" desc="Content description for tabs that are not selected in incognito mode">
+      <message name="IDS_ACCESSIBILITY_TABSTRIP_INCOGNITO_IDENTIFIER" desc="Content description for tabs that are not selected in Incognito mode">
         Incognito Tab
       </message>
-      <message name="IDS_ACCESSIBILITY_TABSTRIP_INCOGNITO_IDENTIFIER_SELECTED" desc="Content description for the tab that is selected in incognito mode">
+      <message name="IDS_ACCESSIBILITY_TABSTRIP_INCOGNITO_IDENTIFIER_SELECTED" desc="Content description for the tab that is selected in Incognito mode">
         Selected Incognito Tab
       </message>
       <message name="IDS_ACCESSIBILITY_OMNIBOX_BTN_REFINE" desc="Content description for the omnibox refine button followed with the text or URL that will be shown in the Omnibox if the user clicks the refine button, eg. User input 'The' may offer a suggestion 'The meaning of life' or 'thewirecutter.com'.">
@@ -3803,7 +3803,7 @@
       <message name="IDS_KEYBOARD_SHORTCUT_REOPEN_NEW_TAB" desc="A text label that appears next to the keyboard shortcut for reopening the last tab in the chrome app. The shortcut description is shown in a system dialog along with all other supported shortcuts. [CHAR-LIMIT=55]">
         Reopen the last closed tab
       </message>
-      <message name="IDS_KEYBOARD_SHORTCUT_NEW_INCOGNITO_TAB" desc="A text label that appears next to a keyboard shortcut to open a new tab in incognito mode in Chrome. The shortcut description is shown in a system dialog along with all other supported shortcuts. [CHAR-LIMIT=55]">
+      <message name="IDS_KEYBOARD_SHORTCUT_NEW_INCOGNITO_TAB" desc="A text label that appears next to a keyboard shortcut to open a new tab in Incognito mode in Chrome. The shortcut description is shown in a system dialog along with all other supported shortcuts. [CHAR-LIMIT=55]">
         Open a new tab in Incognito mode
       </message>
       <message name="IDS_KEYBOARD_SHORTCUT_OPEN_MENU" desc="A text label that appears next to a keyboard shortcut that opens the overflow menu in Chrome. The shortcut description is shown in a system dialog along with all other supported shortcuts. [CHAR-LIMIT=55]">
@@ -3993,7 +3993,7 @@
       </message>
 
       <!-- Launcher Shortcuts -->
-      <message name="IDS_DISABLED_INCOGNITO_LAUNCHER_SHORTCUT_MESSAGE" desc="Text for a toast displayed prompting the user to remove the disabled 'New incognito tab' app shortcut and recreate it.">
+      <message name="IDS_DISABLED_INCOGNITO_LAUNCHER_SHORTCUT_MESSAGE" desc="Text for a toast displayed prompting the user to remove the disabled 'New Incognito tab' app shortcut and recreate it.">
         Remove and recreate this shortcut
       </message>
 
diff --git a/chrome/browser/ui/ash/assistant/assistant_client_impl.cc b/chrome/browser/ui/ash/assistant/assistant_client_impl.cc
index f89b391..5e29ee0 100644
--- a/chrome/browser/ui/ash/assistant/assistant_client_impl.cc
+++ b/chrome/browser/ui/ash/assistant/assistant_client_impl.cc
@@ -188,9 +188,9 @@
 }
 
 void AssistantClientImpl::OnUserProfileLoaded(const AccountId& account_id) {
-  if (!assistant_state_observer_.IsObservingSources() && !initialized_ &&
+  if (!assistant_state_observation_.IsObserving() && !initialized_ &&
       ash::AssistantState::Get()) {
-    assistant_state_observer_.Add(ash::AssistantState::Get());
+    assistant_state_observation_.Observe(ash::AssistantState::Get());
   }
 }
 
diff --git a/chrome/browser/ui/ash/assistant/assistant_client_impl.h b/chrome/browser/ui/ash/assistant/assistant_client_impl.h
index 7f30c27b..e255d727 100644
--- a/chrome/browser/ui/ash/assistant/assistant_client_impl.h
+++ b/chrome/browser/ui/ash/assistant/assistant_client_impl.h
@@ -11,7 +11,7 @@
 #include "ash/public/cpp/assistant/assistant_client.h"
 #include "ash/public/cpp/assistant/assistant_state.h"
 #include "base/macros.h"
-#include "base/scoped_observer.h"
+#include "base/scoped_observation.h"
 #include "chrome/browser/ui/ash/assistant/device_actions.h"
 #include "chromeos/assistant/buildflags.h"
 #include "chromeos/services/assistant/public/cpp/assistant_client.h"
@@ -116,8 +116,8 @@
   Profile* profile_ = nullptr;
   signin::IdentityManager* identity_manager_ = nullptr;
 
-  ScopedObserver<ash::AssistantStateBase, ash::AssistantStateObserver>
-      assistant_state_observer_{this};
+  base::ScopedObservation<ash::AssistantStateBase, ash::AssistantStateObserver>
+      assistant_state_observation_{this};
 
   DISALLOW_COPY_AND_ASSIGN(AssistantClientImpl);
 };
diff --git a/chrome/browser/ui/ash/assistant/assistant_timers_browsertest.cc b/chrome/browser/ui/ash/assistant/assistant_timers_browsertest.cc
index 9b9b97b..8eebf65 100644
--- a/chrome/browser/ui/ash/assistant/assistant_timers_browsertest.cc
+++ b/chrome/browser/ui/ash/assistant/assistant_timers_browsertest.cc
@@ -13,7 +13,7 @@
 #include "ash/system/message_center/unified_message_center_view.h"
 #include "ash/system/status_area_widget.h"
 #include "ash/system/unified/unified_system_tray.h"
-#include "base/scoped_observer.h"
+#include "base/scoped_observation.h"
 #include "base/strings/string_util.h"
 #include "base/test/bind.h"
 #include "base/test/icu_test_util.h"
@@ -52,8 +52,9 @@
       return;                                                                 \
     }                                                                         \
     MockMessageCenterObserver mock;                                           \
-    ScopedObserver<MessageCenter, MessageCenterObserver> observer_{&mock};    \
-    observer_.Add(MessageCenter::Get());                                      \
+    base::ScopedObservation<MessageCenter, MessageCenterObserver>             \
+        observation_{&mock};                                                  \
+    observation_.Observe(MessageCenter::Get());                               \
                                                                               \
     base::RunLoop run_loop;                                                   \
     EXPECT_CALL(mock, OnNotificationAdded)                                    \
@@ -290,7 +291,7 @@
   // Observe notifications.
   MockMessageCenterObserver mock;
   ScopedObserver<MessageCenter, MessageCenterObserver> scoped_observer{&mock};
-  scoped_observer.Add(MessageCenter::Get());
+  scoped_observer.Observe(MessageCenter::Get());
 
   // Show Assistant UI (once ready).
   tester()->StartAssistantAndWaitForReady();
diff --git a/chrome/browser/ui/ash/assistant/assistant_web_view_impl_browsertest.cc b/chrome/browser/ui/ash/assistant/assistant_web_view_impl_browsertest.cc
index c424a35..84e8856e 100644
--- a/chrome/browser/ui/ash/assistant/assistant_web_view_impl_browsertest.cc
+++ b/chrome/browser/ui/ash/assistant/assistant_web_view_impl_browsertest.cc
@@ -10,7 +10,7 @@
 #include "ash/public/cpp/assistant/assistant_web_view.h"
 #include "ash/public/cpp/assistant/assistant_web_view_factory.h"
 #include "base/run_loop.h"
-#include "base/scoped_observer.h"
+#include "base/scoped_observation.h"
 #include "base/strings/stringprintf.h"
 #include "chrome/browser/ui/ash/assistant/assistant_test_mixin.h"
 #include "chrome/test/base/mixin_based_in_process_browser_test.h"
@@ -41,7 +41,7 @@
   {                                                                      \
     MockViewObserver mock;                                               \
     ScopedObserver<views::View, views::ViewObserver> observer{&mock};    \
-    observer.Add(static_cast<views::View*>(web_view_));                  \
+    observer.Observe(static_cast<views::View*>(web_view_));              \
                                                                          \
     base::RunLoop run_loop;                                              \
     EXPECT_CALL(mock, OnViewPreferredSizeChanged)                        \
@@ -56,7 +56,7 @@
   {                                                                            \
     MockAssistantWebViewObserver mock;                                         \
     ScopedObserver<AssistantWebView, AssistantWebView::Observer> obs{&mock};   \
-    obs.Add(web_view_);                                                        \
+    obs.Observe(web_view_);                                                    \
                                                                                \
     base::RunLoop run_loop;                                                    \
     EXPECT_CALL(mock, DidStopLoading).WillOnce(testing::Invoke([&run_loop]() { \
@@ -71,7 +71,7 @@
   {                                                                          \
     MockAssistantWebViewObserver mock;                                       \
     ScopedObserver<AssistantWebView, AssistantWebView::Observer> obs{&mock}; \
-    obs.Add(web_view_);                                                      \
+    obs.Observe(web_view_);                                                  \
                                                                              \
     base::RunLoop run_loop;                                                  \
     EXPECT_CALL(mock, DidSuppressNavigation)                                 \
@@ -90,7 +90,7 @@
   {                                                                          \
     MockAssistantWebViewObserver mock;                                       \
     ScopedObserver<AssistantWebView, AssistantWebView::Observer> obs{&mock}; \
-    obs.Add(web_view_);                                                      \
+    obs.Observe(web_view_);                                                  \
                                                                              \
     base::RunLoop run_loop;                                                  \
     EXPECT_CALL(mock, DidChangeCanGoBack)                                    \
diff --git a/chrome/browser/ui/ash/assistant/device_actions.cc b/chrome/browser/ui/ash/assistant/device_actions.cc
index 23e8bb1..5cb48fe 100644
--- a/chrome/browser/ui/ash/assistant/device_actions.cc
+++ b/chrome/browser/ui/ash/assistant/device_actions.cc
@@ -218,8 +218,8 @@
 
   app_list_subscribers_.AddObserver(subscriber);
 
-  if (prefs && !scoped_prefs_observer_.IsObserving(prefs))
-    scoped_prefs_observer_.Add(prefs);
+  if (prefs && !scoped_prefs_observations_.IsObservingSource(prefs))
+    scoped_prefs_observations_.AddObservation(prefs);
 }
 
 void DeviceActions::RemoveAppListEventSubscriber(
diff --git a/chrome/browser/ui/ash/assistant/device_actions.h b/chrome/browser/ui/ash/assistant/device_actions.h
index 73d1e57..4cc685c 100644
--- a/chrome/browser/ui/ash/assistant/device_actions.h
+++ b/chrome/browser/ui/ash/assistant/device_actions.h
@@ -10,7 +10,7 @@
 #include <vector>
 
 #include "ash/public/cpp/android_intent_helper.h"
-#include "base/scoped_observer.h"
+#include "base/scoped_multi_source_observation.h"
 #include "chrome/browser/ui/app_list/arc/arc_app_list_prefs.h"
 #include "chrome/browser/ui/ash/assistant/device_actions_delegate.h"
 #include "chromeos/services/assistant/public/cpp/assistant_service.h"
@@ -57,8 +57,8 @@
 
   std::unique_ptr<DeviceActionsDelegate> delegate_;
 
-  ScopedObserver<ArcAppListPrefs, ArcAppListPrefs::Observer>
-      scoped_prefs_observer_{this};
+  base::ScopedMultiSourceObservation<ArcAppListPrefs, ArcAppListPrefs::Observer>
+      scoped_prefs_observations_{this};
   base::ObserverList<chromeos::assistant::AppListEventSubscriber>
       app_list_subscribers_;
   DISALLOW_COPY_AND_ASSIGN(DeviceActions);
diff --git a/chrome/browser/ui/ash/holding_space/holding_space_browsertest_base.cc b/chrome/browser/ui/ash/holding_space/holding_space_browsertest_base.cc
index a94257f..f993d5b9 100644
--- a/chrome/browser/ui/ash/holding_space/holding_space_browsertest_base.cc
+++ b/chrome/browser/ui/ash/holding_space/holding_space_browsertest_base.cc
@@ -14,7 +14,7 @@
 #include "ash/public/cpp/holding_space/holding_space_test_api.h"
 #include "base/callback_helpers.h"
 #include "base/files/file_util.h"
-#include "base/scoped_observer.h"
+#include "base/scoped_observation.h"
 #include "base/strings/stringprintf.h"
 #include "base/test/bind.h"
 #include "base/unguessable_token.h"
@@ -87,7 +87,8 @@
 class SessionStateWaiter : public session_manager::SessionManagerObserver {
  public:
   SessionStateWaiter() {
-    session_manager_observer_.Add(session_manager::SessionManager::Get());
+    session_manager_observation_.Observe(
+        session_manager::SessionManager::Get());
   }
 
   void WaitFor(session_manager::SessionState state) {
@@ -115,9 +116,9 @@
   session_manager::SessionState state_ = session_manager::SessionState::UNKNOWN;
   std::unique_ptr<base::RunLoop> wait_loop_;
 
-  ScopedObserver<session_manager::SessionManager,
-                 session_manager::SessionManagerObserver>
-      session_manager_observer_{this};
+  base::ScopedObservation<session_manager::SessionManager,
+                          session_manager::SessionManagerObserver>
+      session_manager_observation_{this};
 };
 
 }  // namespace
diff --git a/chrome/browser/ui/ash/holding_space/holding_space_downloads_delegate.cc b/chrome/browser/ui/ash/holding_space/holding_space_downloads_delegate.cc
index df0d35e8..1c0f57789 100644
--- a/chrome/browser/ui/ash/holding_space/holding_space_downloads_delegate.cc
+++ b/chrome/browser/ui/ash/holding_space/holding_space_downloads_delegate.cc
@@ -35,7 +35,7 @@
 }
 
 void HoldingSpaceDownloadsDelegate::Init() {
-  download_manager_observer_.Add(
+  download_manager_observation_.Observe(
       download_manager_for_testing
           ? download_manager_for_testing
           : content::BrowserContext::GetDownloadManager(profile()));
@@ -68,7 +68,7 @@
   for (auto* download : downloads) {
     switch (download->GetState()) {
       case download::DownloadItem::IN_PROGRESS:
-        download_item_observer_.Add(download);
+        download_item_observations_.AddObservation(download);
         break;
       case download::DownloadItem::COMPLETE:
       case download::DownloadItem::CANCELLED:
@@ -90,7 +90,7 @@
   // Ignore `OnDownloadCreated()` events prior to `manager` initialization. For
   // those events we bind any observers necessary in `OnManagerInitialized()`.
   if (!is_restoring_persistence() && manager->IsManagerInitialized())
-    download_item_observer_.Add(item);
+    download_item_observations_.AddObservation(item);
 }
 
 void HoldingSpaceDownloadsDelegate::OnDownloadUpdated(
@@ -101,7 +101,7 @@
       FALLTHROUGH;
     case download::DownloadItem::CANCELLED:
     case download::DownloadItem::INTERRUPTED:
-      download_item_observer_.Remove(item);
+      download_item_observations_.RemoveObservation(item);
       break;
     case download::DownloadItem::IN_PROGRESS:
     case download::DownloadItem::MAX_DOWNLOAD_STATE:
@@ -116,8 +116,8 @@
 }
 
 void HoldingSpaceDownloadsDelegate::RemoveObservers() {
-  download_manager_observer_.RemoveAll();
-  download_item_observer_.RemoveAll();
+  download_manager_observation_.Reset();
+  download_item_observations_.RemoveAllObservations();
 }
 
 }  // namespace ash
diff --git a/chrome/browser/ui/ash/holding_space/holding_space_downloads_delegate.h b/chrome/browser/ui/ash/holding_space/holding_space_downloads_delegate.h
index b23ea756a..279b61e3 100644
--- a/chrome/browser/ui/ash/holding_space/holding_space_downloads_delegate.h
+++ b/chrome/browser/ui/ash/holding_space/holding_space_downloads_delegate.h
@@ -6,7 +6,8 @@
 #define CHROME_BROWSER_UI_ASH_HOLDING_SPACE_HOLDING_SPACE_DOWNLOADS_DELEGATE_H_
 
 #include "base/callback.h"
-#include "base/scoped_observer.h"
+#include "base/scoped_multi_source_observation.h"
+#include "base/scoped_observation.h"
 #include "chrome/browser/ui/ash/holding_space/holding_space_keyed_service_delegate.h"
 #include "components/download/public/common/download_item.h"
 #include "content/public/browser/download_manager.h"
@@ -65,11 +66,13 @@
   // Callback to invoke when a download is completed.
   ItemDownloadedCallback item_downloaded_callback_;
 
-  ScopedObserver<content::DownloadManager, content::DownloadManager::Observer>
-      download_manager_observer_{this};
+  base::ScopedObservation<content::DownloadManager,
+                          content::DownloadManager::Observer>
+      download_manager_observation_{this};
 
-  ScopedObserver<download::DownloadItem, download::DownloadItem::Observer>
-      download_item_observer_{this};
+  base::ScopedMultiSourceObservation<download::DownloadItem,
+                                     download::DownloadItem::Observer>
+      download_item_observations_{this};
 
   base::WeakPtrFactory<HoldingSpaceDownloadsDelegate> weak_factory_{this};
 };
diff --git a/chrome/browser/ui/ash/holding_space/holding_space_keyed_service_delegate.cc b/chrome/browser/ui/ash/holding_space/holding_space_keyed_service_delegate.cc
index f6ecb8d..0831ff2e 100644
--- a/chrome/browser/ui/ash/holding_space/holding_space_keyed_service_delegate.cc
+++ b/chrome/browser/ui/ash/holding_space/holding_space_keyed_service_delegate.cc
@@ -31,7 +31,7 @@
     : profile_(profile), model_(model) {
   // It is expected that `profile` already be ready prior to delegate creation.
   DCHECK(GetProfileManager()->IsValidProfile(profile));
-  holding_space_model_observer_.Add(model);
+  holding_space_model_observation_.Observe(model);
 }
 
 void HoldingSpaceKeyedServiceDelegate::OnHoldingSpaceItemsAdded(
diff --git a/chrome/browser/ui/ash/holding_space/holding_space_keyed_service_delegate.h b/chrome/browser/ui/ash/holding_space/holding_space_keyed_service_delegate.h
index 6983486..fa23cae1 100644
--- a/chrome/browser/ui/ash/holding_space/holding_space_keyed_service_delegate.h
+++ b/chrome/browser/ui/ash/holding_space/holding_space_keyed_service_delegate.h
@@ -9,7 +9,7 @@
 
 #include "ash/public/cpp/holding_space/holding_space_model.h"
 #include "ash/public/cpp/holding_space/holding_space_model_observer.h"
-#include "base/scoped_observer.h"
+#include "base/scoped_observation.h"
 
 class Profile;
 
@@ -60,8 +60,8 @@
   // If persistence is being restored.
   bool is_restoring_persistence_ = true;
 
-  ScopedObserver<HoldingSpaceModel, HoldingSpaceModelObserver>
-      holding_space_model_observer_{this};
+  base::ScopedObservation<HoldingSpaceModel, HoldingSpaceModelObserver>
+      holding_space_model_observation_{this};
 };
 
 }  // namespace ash
diff --git a/chrome/browser/ui/ash/holding_space/holding_space_keyed_service_unittest.cc b/chrome/browser/ui/ash/holding_space/holding_space_keyed_service_unittest.cc
index 52a92da0..336e853 100644
--- a/chrome/browser/ui/ash/holding_space/holding_space_keyed_service_unittest.cc
+++ b/chrome/browser/ui/ash/holding_space/holding_space_keyed_service_unittest.cc
@@ -96,7 +96,8 @@
  public:
   explicit HoldingSpaceModelAttachedWaiter(Profile* profile)
       : profile_(profile) {
-    holding_space_controller_observer_.Add(HoldingSpaceController::Get());
+    holding_space_controller_observation_.Observe(
+        HoldingSpaceController::Get());
   }
 
   void Wait() {
@@ -125,8 +126,9 @@
   }
 
   Profile* const profile_;
-  ScopedObserver<HoldingSpaceController, HoldingSpaceControllerObserver>
-      holding_space_controller_observer_{this};
+  base::ScopedObservation<HoldingSpaceController,
+                          HoldingSpaceControllerObserver>
+      holding_space_controller_observation_{this};
   std::unique_ptr<base::RunLoop> wait_loop_;
 };
 
diff --git a/chrome/browser/ui/ash/holding_space/holding_space_ui_browsertest.cc b/chrome/browser/ui/ash/holding_space/holding_space_ui_browsertest.cc
index 4974410a..2910e3a 100644
--- a/chrome/browser/ui/ash/holding_space/holding_space_ui_browsertest.cc
+++ b/chrome/browser/ui/ash/holding_space/holding_space_ui_browsertest.cc
@@ -21,7 +21,6 @@
 #include "ash/public/cpp/holding_space/holding_space_test_api.h"
 #include "base/callback_helpers.h"
 #include "base/scoped_observation.h"
-#include "base/scoped_observer.h"
 #include "base/test/bind.h"
 #include "chrome/browser/ash/profiles/profile_helper.h"
 #include "chrome/browser/profiles/profile.h"
@@ -908,8 +907,9 @@
   // Observe the `activation_client` so we can detect windows becoming active as
   // a result of opening holding space items.
   testing::NiceMock<MockActivationChangeObserver> mock;
-  ScopedObserver<wm::ActivationClient, wm::ActivationChangeObserver> obs{&mock};
-  obs.Add(activation_client);
+  base::ScopedObservation<wm::ActivationClient, wm::ActivationChangeObserver>
+      obs{&mock};
+  obs.Observe(activation_client);
 
   // Create a holding space item.
   AddScreenshotFile();
@@ -1297,8 +1297,9 @@
 
   // Bind an observer to watch for updates to the holding space model.
   testing::NiceMock<MockHoldingSpaceModelObserver> mock;
-  ScopedObserver<HoldingSpaceModel, HoldingSpaceModelObserver> observer{&mock};
-  observer.Add(HoldingSpaceController::Get()->model());
+  base::ScopedObservation<HoldingSpaceModel, HoldingSpaceModelObserver>
+      observer{&mock};
+  observer.Observe(HoldingSpaceController::Get()->model());
 
   // Expect and wait for a screenshot item to be added to holding space.
   base::RunLoop run_loop;
@@ -1352,8 +1353,9 @@
 
   // Bind an observer to watch for updates to the holding space model.
   testing::NiceMock<MockHoldingSpaceModelObserver> mock;
-  ScopedObserver<HoldingSpaceModel, HoldingSpaceModelObserver> observer{&mock};
-  observer.Add(HoldingSpaceController::Get()->model());
+  base::ScopedObservation<HoldingSpaceModel, HoldingSpaceModelObserver>
+      observer{&mock};
+  observer.Observe(HoldingSpaceController::Get()->model());
 
   base::RunLoop wait_for_item;
   // Expect and wait for a screen recording item to be added to holding space.
diff --git a/chrome/browser/ui/autofill/chrome_autofill_client.cc b/chrome/browser/ui/autofill/chrome_autofill_client.cc
index 588b3cc..b4db391 100644
--- a/chrome/browser/ui/autofill/chrome_autofill_client.cc
+++ b/chrome/browser/ui/autofill/chrome_autofill_client.cc
@@ -534,9 +534,8 @@
     const AutofillProfile* original_profile,
     AddressProfileSavePromptCallback callback) {
 #if defined(OS_ANDROID)
-  // TODO(crbug.com/1167061): Implement update.
-  save_address_profile_flow_manager_.OfferSave(web_contents(), profile,
-                                               std::move(callback));
+  save_address_profile_flow_manager_.OfferSave(
+      web_contents(), profile, original_profile, std::move(callback));
 #else
   SaveUpdateAddressProfileBubbleControllerImpl::CreateForWebContents(
       web_contents());
diff --git a/chrome/browser/ui/extensions/extension_action_view_controller.cc b/chrome/browser/ui/extensions/extension_action_view_controller.cc
index 2af8c5a..62418ea 100644
--- a/chrome/browser/ui/extensions/extension_action_view_controller.cc
+++ b/chrome/browser/ui/extensions/extension_action_view_controller.cc
@@ -385,7 +385,7 @@
     return false;
 
   popup_host_ = host.get();
-  popup_host_observer_.Add(popup_host_);
+  popup_host_observation_.Observe(popup_host_);
   extensions_container_->SetPopupOwner(this);
 
   extensions_container_->CloseOverflowMenuIfOpen();
@@ -412,7 +412,8 @@
 }
 
 void ExtensionActionViewController::OnPopupClosed() {
-  popup_host_observer_.Remove(popup_host_);
+  DCHECK(popup_host_observation_.IsObservingSource(popup_host_));
+  popup_host_observation_.Reset();
   popup_host_ = nullptr;
   extensions_container_->SetPopupOwner(nullptr);
   if (extensions_container_->GetPoppedOutAction() == this)
diff --git a/chrome/browser/ui/extensions/extension_action_view_controller.h b/chrome/browser/ui/extensions/extension_action_view_controller.h
index b707276e..d32ec0b 100644
--- a/chrome/browser/ui/extensions/extension_action_view_controller.h
+++ b/chrome/browser/ui/extensions/extension_action_view_controller.h
@@ -7,7 +7,7 @@
 
 #include "base/macros.h"
 #include "base/memory/weak_ptr.h"
-#include "base/scoped_observer.h"
+#include "base/scoped_observation.h"
 #include "chrome/browser/extensions/extension_action_icon_factory.h"
 #include "chrome/browser/extensions/extension_context_menu_model.h"
 #include "chrome/browser/ui/toolbar/toolbar_action_view_controller.h"
@@ -203,8 +203,9 @@
   // The associated ExtensionRegistry; cached for quick checking.
   extensions::ExtensionRegistry* extension_registry_;
 
-  ScopedObserver<extensions::ExtensionHost, extensions::ExtensionHostObserver>
-      popup_host_observer_{this};
+  base::ScopedObservation<extensions::ExtensionHost,
+                          extensions::ExtensionHostObserver>
+      popup_host_observation_{this};
 
   base::WeakPtrFactory<ExtensionActionViewController> weak_factory_{this};
 
diff --git a/chrome/browser/ui/extensions/extension_enable_flow.cc b/chrome/browser/ui/extensions/extension_enable_flow.cc
index 9073172..1a3c320 100644
--- a/chrome/browser/ui/extensions/extension_enable_flow.cc
+++ b/chrome/browser/ui/extensions/extension_enable_flow.cc
@@ -194,13 +194,13 @@
 #endif  // BUILDFLAG(ENABLE_SUPERVISED_USERS)
 
 void ExtensionEnableFlow::StartObserving() {
-  extension_registry_observer_.Add(
+  extension_registry_observation_.Observe(
       extensions::ExtensionRegistry::Get(profile_));
   load_error_observation_.Observe(extensions::LoadErrorReporter::GetInstance());
 }
 
 void ExtensionEnableFlow::StopObserving() {
-  extension_registry_observer_.RemoveAll();
+  extension_registry_observation_.Reset();
   load_error_observation_.Reset();
 }
 
diff --git a/chrome/browser/ui/extensions/extension_enable_flow.h b/chrome/browser/ui/extensions/extension_enable_flow.h
index 1478b9f1..2c2f88bf 100644
--- a/chrome/browser/ui/extensions/extension_enable_flow.h
+++ b/chrome/browser/ui/extensions/extension_enable_flow.h
@@ -13,7 +13,6 @@
 #include "base/macros.h"
 #include "base/memory/weak_ptr.h"
 #include "base/scoped_observation.h"
-#include "base/scoped_observer.h"
 #include "chrome/browser/extensions/extension_install_prompt.h"
 #include "chrome/browser/extensions/load_error_reporter.h"
 #include "chrome/common/buildflags.h"
@@ -120,9 +119,9 @@
   std::unique_ptr<ExtensionInstallPrompt> prompt_;
 
   // Listen to extension load notification.
-  ScopedObserver<extensions::ExtensionRegistry,
-                 extensions::ExtensionRegistryObserver>
-      extension_registry_observer_{this};
+  base::ScopedObservation<extensions::ExtensionRegistry,
+                          extensions::ExtensionRegistryObserver>
+      extension_registry_observation_{this};
 
   base::ScopedObservation<extensions::LoadErrorReporter,
                           extensions::LoadErrorReporter::Observer>
diff --git a/chrome/browser/ui/extensions/extension_installed_waiter.cc b/chrome/browser/ui/extensions/extension_installed_waiter.cc
index 98ccdb7..6e11b903 100644
--- a/chrome/browser/ui/extensions/extension_installed_waiter.cc
+++ b/chrome/browser/ui/extensions/extension_installed_waiter.cc
@@ -38,7 +38,7 @@
     : extension_(extension),
       browser_(browser),
       done_callback_(std::move(done_callback)) {
-  extension_registry_observer_.Add(
+  extension_registry_observation_.Observe(
       extensions::ExtensionRegistry::Get(browser->profile()));
   removal_watcher_ = std::make_unique<ExtensionRemovalWatcher>(
       browser, extension,
diff --git a/chrome/browser/ui/extensions/extension_installed_waiter.h b/chrome/browser/ui/extensions/extension_installed_waiter.h
index 3dbe58b..1bf449d2b 100644
--- a/chrome/browser/ui/extensions/extension_installed_waiter.h
+++ b/chrome/browser/ui/extensions/extension_installed_waiter.h
@@ -7,7 +7,7 @@
 
 #include "base/callback.h"
 #include "base/memory/weak_ptr.h"
-#include "base/scoped_observer.h"
+#include "base/scoped_observation.h"
 #include "base/time/time.h"
 #include "chrome/browser/ui/extensions/extension_removal_watcher.h"
 #include "extensions/browser/extension_registry.h"
@@ -64,9 +64,9 @@
   const Browser* const browser_;
   base::OnceClosure done_callback_;
 
-  ScopedObserver<extensions::ExtensionRegistry,
-                 extensions::ExtensionRegistryObserver>
-      extension_registry_observer_{this};
+  base::ScopedObservation<extensions::ExtensionRegistry,
+                          extensions::ExtensionRegistryObserver>
+      extension_registry_observation_{this};
 
   std::unique_ptr<ExtensionRemovalWatcher> removal_watcher_;
 
diff --git a/chrome/browser/ui/extensions/extension_removal_watcher.cc b/chrome/browser/ui/extensions/extension_removal_watcher.cc
index 2d1ed12a..4ef1045c 100644
--- a/chrome/browser/ui/extensions/extension_removal_watcher.cc
+++ b/chrome/browser/ui/extensions/extension_removal_watcher.cc
@@ -13,7 +13,7 @@
     scoped_refptr<const extensions::Extension> extension,
     base::OnceClosure callback)
     : browser_(browser), extension_(extension), callback_(std::move(callback)) {
-  extension_registry_observer_.Add(
+  extension_registry_observation_.Observe(
       extensions::ExtensionRegistry::Get(browser->profile()));
   BrowserList::AddObserver(this);
 }
diff --git a/chrome/browser/ui/extensions/extension_removal_watcher.h b/chrome/browser/ui/extensions/extension_removal_watcher.h
index ed6ef45c..75e8337d 100644
--- a/chrome/browser/ui/extensions/extension_removal_watcher.h
+++ b/chrome/browser/ui/extensions/extension_removal_watcher.h
@@ -7,7 +7,7 @@
 
 #include "base/callback.h"
 #include "base/memory/weak_ptr.h"
-#include "base/scoped_observer.h"
+#include "base/scoped_observation.h"
 #include "chrome/browser/ui/browser_list_observer.h"
 #include "extensions/browser/extension_registry.h"
 #include "extensions/browser/extension_registry_observer.h"
@@ -37,9 +37,9 @@
   const scoped_refptr<const extensions::Extension> extension_;
   base::OnceClosure callback_;
 
-  ScopedObserver<extensions::ExtensionRegistry,
-                 extensions::ExtensionRegistryObserver>
-      extension_registry_observer_{this};
+  base::ScopedObservation<extensions::ExtensionRegistry,
+                          extensions::ExtensionRegistryObserver>
+      extension_registry_observation_{this};
 };
 
 #endif  // CHROME_BROWSER_UI_EXTENSIONS_EXTENSION_REMOVAL_WATCHER_H_
diff --git a/chrome/browser/ui/hid/hid_chooser_controller.cc b/chrome/browser/ui/hid/hid_chooser_controller.cc
index 2d52bc91..290ed565 100644
--- a/chrome/browser/ui/hid/hid_chooser_controller.cc
+++ b/chrome/browser/ui/hid/hid_chooser_controller.cc
@@ -210,11 +210,11 @@
 }
 
 void HidChooserController::OnHidManagerConnectionError() {
-  observer_.RemoveAll();
+  observation_.Reset();
 }
 
 void HidChooserController::OnHidChooserContextShutdown() {
-  observer_.RemoveAll();
+  observation_.Reset();
 }
 
 void HidChooserController::OnGotDevices(
@@ -227,7 +227,7 @@
   // Listen to HidChooserContext for OnDeviceAdded/Removed events after the
   // enumeration.
   if (chooser_context_)
-    observer_.Add(chooser_context_.get());
+    observation_.Observe(chooser_context_.get());
 
   if (view())
     view()->OnOptionsInitialized();
diff --git a/chrome/browser/ui/hid/hid_chooser_controller.h b/chrome/browser/ui/hid/hid_chooser_controller.h
index bf6eade..406832c4 100644
--- a/chrome/browser/ui/hid/hid_chooser_controller.h
+++ b/chrome/browser/ui/hid/hid_chooser_controller.h
@@ -10,7 +10,7 @@
 #include <vector>
 
 #include "base/memory/weak_ptr.h"
-#include "base/scoped_observer.h"
+#include "base/scoped_observation.h"
 #include "chrome/browser/chooser_controller/chooser_controller.h"
 #include "chrome/browser/hid/hid_chooser_context.h"
 #include "content/public/browser/hid_chooser.h"
@@ -104,11 +104,11 @@
   // in the chooser.
   std::vector<std::string> items_;
 
-  ScopedObserver<HidChooserContext,
-                 HidChooserContext::DeviceObserver,
-                 &HidChooserContext::AddDeviceObserver,
-                 &HidChooserContext::RemoveDeviceObserver>
-      observer_{this};
+  base::ScopedObservation<HidChooserContext,
+                          HidChooserContext::DeviceObserver,
+                          &HidChooserContext::AddDeviceObserver,
+                          &HidChooserContext::RemoveDeviceObserver>
+      observation_{this};
 
   base::WeakPtrFactory<HidChooserController> weak_factory_{this};
 };
diff --git a/chrome/browser/ui/search_engines/search_engine_tab_helper.cc b/chrome/browser/ui/search_engines/search_engine_tab_helper.cc
index 3dbb17e..8c4e4c9 100644
--- a/chrome/browser/ui/search_engines/search_engine_tab_helper.cc
+++ b/chrome/browser/ui/search_engines/search_engine_tab_helper.cc
@@ -78,7 +78,7 @@
 }
 
 void SearchEngineTabHelper::WebContentsDestroyed() {
-  favicon_driver_observer_.RemoveAll();
+  favicon_driver_observation_.Reset();
 }
 
 SearchEngineTabHelper::SearchEngineTabHelper(WebContents* web_contents)
@@ -87,7 +87,7 @@
   DCHECK(web_contents);
 
   favicon::CreateContentFaviconDriverForWebContents(web_contents);
-  favicon_driver_observer_.Add(
+  favicon_driver_observation_.Observe(
       favicon::ContentFaviconDriver::FromWebContents(web_contents));
 }
 
diff --git a/chrome/browser/ui/search_engines/search_engine_tab_helper.h b/chrome/browser/ui/search_engines/search_engine_tab_helper.h
index 4e8a25c6..b3fc4ada 100644
--- a/chrome/browser/ui/search_engines/search_engine_tab_helper.h
+++ b/chrome/browser/ui/search_engines/search_engine_tab_helper.h
@@ -6,7 +6,7 @@
 #define CHROME_BROWSER_UI_SEARCH_ENGINES_SEARCH_ENGINE_TAB_HELPER_H_
 
 #include "base/macros.h"
-#include "base/scoped_observer.h"
+#include "base/scoped_observation.h"
 #include "chrome/browser/ui/find_bar/find_bar_controller.h"
 #include "chrome/common/open_search_description_document_handler.mojom.h"
 #include "components/favicon/core/favicon_driver.h"
@@ -52,8 +52,9 @@
       chrome::mojom::OpenSearchDescriptionDocumentHandler>
       osdd_handler_receivers_;
 
-  ScopedObserver<favicon::FaviconDriver, favicon::FaviconDriverObserver>
-      favicon_driver_observer_{this};
+  base::ScopedObservation<favicon::FaviconDriver,
+                          favicon::FaviconDriverObserver>
+      favicon_driver_observation_{this};
 
   WEB_CONTENTS_USER_DATA_KEY_DECL();
 
diff --git a/chrome/browser/ui/startup/OWNERS b/chrome/browser/ui/startup/OWNERS
index b5a58d9..d64944d 100644
--- a/chrome/browser/ui/startup/OWNERS
+++ b/chrome/browser/ui/startup/OWNERS
@@ -1,3 +1,4 @@
 tmartino@chromium.org
+grt@chromium.org
 
 per-file credential_provider*=file://chrome/credential_provider/OWNERS
\ No newline at end of file
diff --git a/chrome/browser/ui/startup/startup_browser_creator.cc b/chrome/browser/ui/startup/startup_browser_creator.cc
index 545156d..2d4dfbb8be 100644
--- a/chrome/browser/ui/startup/startup_browser_creator.cc
+++ b/chrome/browser/ui/startup/startup_browser_creator.cc
@@ -24,7 +24,7 @@
 #include "base/metrics/histogram_macros.h"
 #include "base/metrics/statistics_recorder.h"
 #include "base/optional.h"
-#include "base/scoped_observer.h"
+#include "base/scoped_multi_source_observation.h"
 #include "base/strings/string_tokenizer.h"
 #include "base/task/thread_pool.h"
 #include "base/threading/scoped_blocking_call.h"
@@ -153,7 +153,7 @@
 
   // ProfileObserver:
   void OnProfileWillBeDestroyed(Profile* profile) override {
-    observed_profiles_.Remove(profile);
+    observed_profiles_.RemoveObservation(profile);
     launched_profiles_.erase(profile);
     opened_profiles_.erase(profile);
     if (profile == profile_to_activate_)
@@ -168,8 +168,8 @@
   }
 
   void AddLaunched(Profile* profile) {
-    if (!observed_profiles_.IsObserving(profile))
-      observed_profiles_.Add(profile);
+    if (!observed_profiles_.IsObservingSource(profile))
+      observed_profiles_.AddObservation(profile);
     launched_profiles_.insert(profile);
     if (chrome::FindBrowserWithProfile(profile)) {
       // A browser may get opened before we get initialized (e.g., in tests),
@@ -186,8 +186,8 @@
   bool activated_profile() { return activated_profile_; }
 
   void set_profile_to_activate(Profile* profile) {
-    if (!observed_profiles_.IsObserving(profile))
-      observed_profiles_.Add(profile);
+    if (!observed_profiles_.IsObservingSource(profile))
+      observed_profiles_.AddObservation(profile);
     profile_to_activate_ = profile;
     MaybeActivateProfile();
   }
@@ -210,7 +210,7 @@
         FROM_HERE, base::BindOnce(&ProfileLaunchObserver::ActivateProfile,
                                   base::Unretained(this)));
     // Avoid posting more than once before ActivateProfile gets called.
-    observed_profiles_.RemoveAll();
+    observed_profiles_.RemoveAllObservations();
     BrowserList::RemoveObserver(this);
   }
 
@@ -245,7 +245,8 @@
   Profile* profile_to_activate_ = nullptr;
   // Set once we attempted to activate a profile. We only get one shot at this.
   bool activated_profile_ = false;
-  ScopedObserver<Profile, ProfileObserver> observed_profiles_{this};
+  base::ScopedMultiSourceObservation<Profile, ProfileObserver>
+      observed_profiles_{this};
 };
 
 base::LazyInstance<ProfileLaunchObserver>::DestructorAtExit
diff --git a/chrome/browser/ui/views/status_bubble_views.cc b/chrome/browser/ui/views/status_bubble_views.cc
index f587ac0..066461a 100644
--- a/chrome/browser/ui/views/status_bubble_views.cc
+++ b/chrome/browser/ui/views/status_bubble_views.cc
@@ -31,6 +31,7 @@
 #include "ui/gfx/font_list.h"
 #include "ui/gfx/geometry/point.h"
 #include "ui/gfx/geometry/rect.h"
+#include "ui/gfx/rrect_f.h"
 #include "ui/gfx/scoped_canvas.h"
 #include "ui/gfx/skia_util.h"
 #include "ui/gfx/text_elider.h"
@@ -434,6 +435,11 @@
   const float radius = kBubbleCornerRadius * scale;
 
   SkScalar rad[8] = {};
+  auto round_corner = [&rad, radius](gfx::RRectF::Corner c) {
+    int index = base::to_underlying(c);
+    rad[2 * index] = radius;
+    rad[2 * index + 1] = radius;
+  };
 
   // Top Edges - if the bubble is in its bottom position (sticking downwards),
   // then we square the top edges. Otherwise, we square the edges based on the
@@ -442,14 +448,9 @@
   if (style_ != BubbleStyle::kBottom) {
     if (base::i18n::IsRTL() != (style_ == BubbleStyle::kStandardRight)) {
       // The text is RtL or the bubble is on the right side (but not both).
-
-      // Top Left corner.
-      rad[0] = radius;
-      rad[1] = radius;
+      round_corner(gfx::RRectF::Corner::kUpperLeft);
     } else {
-      // Top Right corner.
-      rad[2] = radius;
-      rad[3] = radius;
+      round_corner(gfx::RRectF::Corner::kUpperRight);
     }
   }
 
@@ -457,13 +458,20 @@
   // position (sticking upward).
   if (style_ != BubbleStyle::kStandard &&
       style_ != BubbleStyle::kStandardRight) {
-    // Bottom Right Corner.
-    rad[4] = radius;
-    rad[5] = radius;
-
-    // Bottom Left Corner.
-    rad[6] = radius;
-    rad[7] = radius;
+    round_corner(gfx::RRectF::Corner::kLowerRight);
+    round_corner(gfx::RRectF::Corner::kLowerLeft);
+  } else {
+#if defined(OS_MAC)
+    // Mac's window has rounded corners, but the corner radius might be
+    // different on different versions. Status bubble will use its own round
+    // corner on Mac when there is no download shelf beneath.
+    if (!status_bubble_->download_shelf_is_visible_) {
+      if (base::i18n::IsRTL() != (style_ == BubbleStyle::kStandard))
+        round_corner(gfx::RRectF::Corner::kLowerLeft);
+      else
+        round_corner(gfx::RRectF::Corner::kLowerRight);
+    }
+#endif
   }
 
   // Snap to pixels to avoid shadow blurriness.
diff --git a/chrome/browser/ui/webui/chrome_web_ui_controller_factory.cc b/chrome/browser/ui/webui/chrome_web_ui_controller_factory.cc
index 85b9066..0fe1d99e 100644
--- a/chrome/browser/ui/webui/chrome_web_ui_controller_factory.cc
+++ b/chrome/browser/ui/webui/chrome_web_ui_controller_factory.cc
@@ -242,6 +242,8 @@
 #if BUILDFLAG(IS_CHROMEOS_ASH) && !defined(OFFICIAL_BUILD)
 #include "chrome/browser/ash/web_applications/chrome_file_manager_ui_delegate.h"
 #include "chrome/browser/ui/webui/chromeos/emulator/device_emulator_ui.h"
+#include "chromeos/components/demo_mode_app_ui/demo_mode_app_ui.h"
+#include "chromeos/components/demo_mode_app_ui/url_constants.h"
 #include "chromeos/components/file_manager/file_manager_ui.h"
 #include "chromeos/components/file_manager/url_constants.h"
 #include "chromeos/components/sample_system_web_app_ui/sample_system_web_app_ui.h"
@@ -867,6 +869,11 @@
       return &NewWebUI<DeviceEmulatorUI>;
   }
 #endif  // !defined(USE_REAL_DBUS_CLIENTS)
+  if (url.host_piece() == chromeos::kChromeUIDemoModeAppHost) {
+    if (chromeos::features::IsDemoModeSWAEnabled()) {
+      return &NewWebUI<chromeos::DemoModeAppUI>;
+    }
+  }
   if (url.host_piece() == chromeos::file_manager::kChromeUIFileManagerHost) {
     return &NewComponentUI<chromeos::file_manager::FileManagerUI,
                            ChromeFileManagerUIDelegate>;
diff --git a/chrome/browser/web_applications/system_web_apps/system_web_app_data.proto b/chrome/browser/web_applications/system_web_apps/system_web_app_data.proto
index c09cad1..604602b 100644
--- a/chrome/browser/web_applications/system_web_apps/system_web_app_data.proto
+++ b/chrome/browser/web_applications/system_web_apps/system_web_app_data.proto
@@ -28,6 +28,7 @@
     PERSONALIZATION = 15;
     SHORTCUT_CUSTOMIZATION = 16;
     SHIMLESS_RMA = 17;
+    DEMO_MODE = 18;
   };
 
   optional SystemAppType system_app_type = 1;
diff --git a/chrome/browser/web_applications/system_web_apps/system_web_app_manager.cc b/chrome/browser/web_applications/system_web_apps/system_web_app_manager.cc
index 990afe98..d83be9a 100644
--- a/chrome/browser/web_applications/system_web_apps/system_web_app_manager.cc
+++ b/chrome/browser/web_applications/system_web_apps/system_web_app_manager.cc
@@ -83,6 +83,7 @@
 #include "chromeos/strings/grit/chromeos_strings.h"  // nogncheck
 #include "extensions/common/constants.h"
 #if !defined(OFFICIAL_BUILD)
+#include "chrome/browser/ash/web_applications/demo_mode_web_app_info.h"
 #include "chrome/browser/ash/web_applications/file_manager_web_app_info.h"
 #include "chrome/browser/ash/web_applications/sample_system_web_app_info.h"
 #include "chrome/browser/ash/web_applications/telemetry_extension_web_app_info.h"
@@ -314,6 +315,14 @@
     infos.at(SystemAppType::FILE_MANAGER).single_window = false;
   }
 
+  if (SystemWebAppManager::IsAppEnabled(SystemAppType::DEMO_MODE)) {
+    infos.emplace(
+        SystemAppType::DEMO_MODE,
+        SystemAppInfo("DemoMode", GURL("chrome://demo-mode-app"),
+                      base::BindRepeating(&CreateWebAppInfoForDemoModeApp)));
+    infos.at(SystemAppType::DEMO_MODE).capture_navigations = true;
+  }
+
   infos.emplace(
       SystemAppType::SAMPLE,
       SystemAppInfo(
@@ -442,6 +451,8 @@
       return chromeos::features::IsWallpaperWebUIEnabled();
     case SystemAppType::SHORTCUT_CUSTOMIZATION:
       return features::IsShortcutCustomizationAppEnabled();
+    case SystemAppType::DEMO_MODE:
+      return chromeos::features::IsDemoModeSWAEnabled();
   }
 #else
   return false;
diff --git a/chrome/browser/web_applications/system_web_apps/system_web_app_types.h b/chrome/browser/web_applications/system_web_apps/system_web_app_types.h
index 08a4df6..9b711af9 100644
--- a/chrome/browser/web_applications/system_web_apps/system_web_app_types.h
+++ b/chrome/browser/web_applications/system_web_apps/system_web_app_types.h
@@ -52,6 +52,17 @@
   // Contact: cros-peripherals@google.com
   SHIMLESS_RMA = 17,
 
+  // A System Web App that launches on Demo Mode startup, to display animated
+  // content that highlights various features of ChromeOS
+  //
+  // Currently this SWA is only enabled in unofficial builds while still under
+  // development. Prefer to file bugs to the internal Demo Mode component:
+  // b/components/812312
+  //
+  // Source: //chromeos/components/demo_mode_app_ui/
+  // Contact: jacksontadie@google.com, drcrash@chromium.org
+  DEMO_MODE = 18,
+
   // When adding a new System App, remember to:
   //
   // 1. Add a corresponding histogram suffix in WebAppSystemAppInternalName
diff --git a/chrome/browser/window_placement/DEPS b/chrome/browser/window_placement/DEPS
new file mode 100644
index 0000000..abf767e
--- /dev/null
+++ b/chrome/browser/window_placement/DEPS
@@ -0,0 +1,5 @@
+specific_include_rules = {
+  "window_placement_browsertest\.cc": [
+    "+ash/shell.h",
+  ],
+}
diff --git a/chrome/browser/window_placement/window_placement_browsertest.cc b/chrome/browser/window_placement/window_placement_browsertest.cc
new file mode 100644
index 0000000..ae2a976e
--- /dev/null
+++ b/chrome/browser/window_placement/window_placement_browsertest.cc
@@ -0,0 +1,178 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/command_line.h"
+#include "build/build_config.h"
+#include "build/chromeos_buildflags.h"
+#include "chrome/browser/ui/browser.h"
+#include "chrome/browser/ui/browser_window.h"
+#include "chrome/test/base/in_process_browser_test.h"
+#include "chrome/test/base/ui_test_utils.h"
+#include "components/permissions/permission_request_manager.h"
+#include "content/public/browser/web_contents.h"
+#include "content/public/common/content_switches.h"
+#include "content/public/test/browser_test.h"
+#include "net/test/embedded_test_server/embedded_test_server.h"
+#include "third_party/blink/public/common/switches.h"
+#include "ui/display/screen_base.h"
+#include "ui/display/test/scoped_screen_override.h"
+#include "ui/display/test/test_screen.h"
+
+#if BUILDFLAG(IS_CHROMEOS_ASH)
+#include "ash/shell.h"
+#include "ui/display/test/display_manager_test_api.h"
+#endif  // BUILDFLAG(IS_CHROMEOS_ASH)
+
+class WindowPlacementTest : public InProcessBrowserTest {
+ public:
+  void SetUpCommandLine(base::CommandLine* command_line) override {
+    base::CommandLine::ForCurrentProcess()->AppendSwitchASCII(
+        switches::kEnableBlinkFeatures, "WindowPlacement");
+    InProcessBrowserTest::SetUpCommandLine(command_line);
+  }
+};
+
+// TODO(crbug.com/1183791): Disabled on non-ChromeOS because of races with
+// SetScreenInstance and observers not being notified.
+// TODO(crbug.com/1194700): Disabled on Mac because of GetScreenInfos staleness.
+#if !BUILDFLAG(IS_CHROMEOS_ASH)
+#define MAYBE_OnScreensChangeEvent DISABLED_OnScreensChangeEvent
+#else
+#define MAYBE_OnScreensChangeEvent OnScreensChangeEvent
+#endif
+IN_PROC_BROWSER_TEST_F(WindowPlacementTest, MAYBE_OnScreensChangeEvent) {
+  // Updates the display configuration to add a secondary display.
+#if BUILDFLAG(IS_CHROMEOS_ASH)
+  display::test::DisplayManagerTestApi(ash::Shell::Get()->display_manager())
+      .UpdateDisplay("100+1-801x802");
+#else
+  display::ScreenBase screen;
+  screen.display_list().AddDisplay({1, gfx::Rect(100, 1, 801, 802)},
+                                   display::DisplayList::Type::PRIMARY);
+  display::test::ScopedScreenOverride screen_override(&screen);
+#endif  // BUILDFLAG(IS_CHROMEOS_ASH)
+  ASSERT_EQ(1, display::Screen::GetScreen()->GetNumDisplays());
+
+  ASSERT_TRUE(embedded_test_server()->Start());
+  const GURL url(embedded_test_server()->GetURL("/simple.html"));
+  EXPECT_TRUE(ui_test_utils::NavigateToURL(browser(), url));
+  auto* tab = browser()->tab_strip_model()->GetActiveWebContents();
+
+  // TODO(crbug.com/1119974): this test could be in content_browsertests
+  // and not browser_tests if permission controls were supported.
+
+  // Auto-accept the Window Placement permission request.
+  permissions::PermissionRequestManager* permission_request_manager =
+      permissions::PermissionRequestManager::FromWebContents(tab);
+  permission_request_manager->set_auto_response_for_test(
+      permissions::PermissionRequestManager::ACCEPT_ALL);
+
+  auto initial_result = std::vector<base::Value>();
+  initial_result.emplace_back(801);
+  ASSERT_EQ(initial_result, EvalJs(tab, R"(
+      var screensInterface;
+      var promiseForEvent = (target, evt) => {
+        return new Promise((resolve) => {
+          const handler = (e) => {
+            target.removeEventListener(evt, handler);
+            resolve(e);
+          };
+          target.addEventListener(evt, handler);
+        });
+      }
+      var makeScreensChangePromise = () => {
+        return promiseForEvent(screensInterface, 'change');
+      };
+      var getScreenWidths = () => {
+        return screensInterface.screens.map((d) => d.width).sort();
+      };
+
+      (async () => {
+          screensInterface = await self.getScreens();
+          return getScreenWidths();
+      })();
+  )"));
+
+  // Add a second display.
+  EXPECT_TRUE(
+      ExecJs(tab, R"(var screensChange = makeScreensChangePromise();)"));
+#if BUILDFLAG(IS_CHROMEOS_ASH)
+  display::test::DisplayManagerTestApi(ash::Shell::Get()->display_manager())
+      .UpdateDisplay("100+100-801x802,901+100-802x802");
+#else
+  screen.display_list().AddDisplay({2, gfx::Rect(901, 100, 802, 802)},
+                                   display::DisplayList::Type::PRIMARY);
+#endif  // BUILDFLAG(IS_CHROMEOS_ASH)
+  ASSERT_EQ(2, display::Screen::GetScreen()->GetNumDisplays());
+
+  {
+    auto result = std::vector<base::Value>();
+    result.emplace_back(801);
+    result.emplace_back(802);
+
+    EXPECT_EQ(result, EvalJs(tab, R"(
+      (async () => {
+          await screensChange;
+          return getScreenWidths();
+      })();
+    )"));
+  }
+
+  // Remove the first display.
+  EXPECT_TRUE(
+      ExecJs(tab, R"(var screensChange = makeScreensChangePromise();)"));
+#if BUILDFLAG(IS_CHROMEOS_ASH)
+  display::test::DisplayManagerTestApi(ash::Shell::Get()->display_manager())
+      .UpdateDisplay("901+100-802x802");
+#else
+  // Make the second display primary so we can remove the first.
+  EXPECT_EQ(screen.display_list().displays().size(), 2u);
+  screen.display_list().RemoveDisplay(1);
+  EXPECT_EQ(screen.display_list().displays().size(), 1u);
+#endif  // BUILDFLAG(IS_CHROMEOS_ASH)
+  ASSERT_EQ(1, display::Screen::GetScreen()->GetNumDisplays());
+
+  {
+    auto result = std::vector<base::Value>();
+    result.emplace_back(802);
+
+    EXPECT_EQ(result, EvalJs(tab, R"(
+      (async () => {
+          await screensChange;
+          return getScreenWidths();
+      })();
+    )"));
+  }
+
+  // Remove one display, add two displays.
+  // TODO(enne): we add two displays here because DisplayManagerTestApi
+  // would consider remove+add to just be an update (with the same id).
+  // An alternative is to modify DisplayManagerTestApi to let us set ids.
+  EXPECT_TRUE(
+      ExecJs(tab, R"(var screensChange = makeScreensChangePromise();)"));
+#if BUILDFLAG(IS_CHROMEOS_ASH)
+  display::test::DisplayManagerTestApi(ash::Shell::Get()->display_manager())
+      .UpdateDisplay("0+0-803x600,1000+0-804x600");
+#else
+  screen.display_list().RemoveDisplay(2);
+  screen.display_list().AddDisplay({3, gfx::Rect(0, 4, 803, 600)},
+                                   display::DisplayList::Type::PRIMARY);
+  screen.display_list().AddDisplay({4, gfx::Rect(0, 4, 804, 600)},
+                                   display::DisplayList::Type::NOT_PRIMARY);
+#endif  // BUILDFLAG(IS_CHROMEOS_ASH)
+  ASSERT_EQ(2, display::Screen::GetScreen()->GetNumDisplays());
+
+  {
+    auto result = std::vector<base::Value>();
+    result.emplace_back(803);
+    result.emplace_back(804);
+
+    EXPECT_EQ(result, EvalJs(tab, R"(
+      (async () => {
+          await screensChange;
+          return getScreenWidths();
+      })();
+    )"));
+  }
+}
diff --git a/chrome/build/win32.pgo.txt b/chrome/build/win32.pgo.txt
index bfb030c..979d981 100644
--- a/chrome/build/win32.pgo.txt
+++ b/chrome/build/win32.pgo.txt
@@ -1 +1 @@
-chrome-win32-master-1619611159-814ff4ad2e5da8294a969d90d950ddf4421bb520.profdata
+chrome-win32-master-1619621900-8d394bc4ef01627c429b71cef8df49097a385661.profdata
diff --git a/chrome/build/win64.pgo.txt b/chrome/build/win64.pgo.txt
index 0043fec..2b48cba 100644
--- a/chrome/build/win64.pgo.txt
+++ b/chrome/build/win64.pgo.txt
@@ -1 +1 @@
-chrome-win64-master-1619588955-1f6be541ab143ad5fe4b61119aebe87e57216bd3.profdata
+chrome-win64-master-1619621900-b500fbfb23f00e485ee02981949b64bc088db137.profdata
diff --git a/chrome/chrome_paks.gni b/chrome/chrome_paks.gni
index 0770f5a..5e8b4e3 100644
--- a/chrome/chrome_paks.gni
+++ b/chrome/chrome_paks.gni
@@ -256,11 +256,13 @@
 
       if (!is_official_build) {
         sources += [
+          "$root_gen_dir/chromeos/chromeos_demo_mode_app_resources.pak",
           "$root_gen_dir/chromeos/chromeos_file_manager_resources.pak",
           "$root_gen_dir/chromeos/chromeos_sample_system_web_app_resources.pak",
           "$root_gen_dir/chromeos/chromeos_telemetry_extension_resources.pak",
         ]
         deps += [
+          "//chromeos/resources:demo_mode_app_resources",
           "//chromeos/resources:file_manager_resources",
           "//chromeos/resources:sample_system_web_app_resources",
           "//chromeos/resources:telemetry_extension_resources",
diff --git a/chrome/common/extensions/api/tabs.json b/chrome/common/extensions/api/tabs.json
index 1f69e1c9..a4de1fc 100644
--- a/chrome/common/extensions/api/tabs.json
+++ b/chrome/common/extensions/api/tabs.json
@@ -141,6 +141,10 @@
       }
     ],
     "properties": {
+      "MAX_CAPTURE_VISIBLE_TAB_CALLS_PER_SECOND": {
+        "value": 1,
+        "description": "The maximum number of times that $(ref:captureVisibleTab) can be called per second. $(ref:captureVisibleTab) is expensive and should not be called too often."
+      },
       "TAB_ID_NONE": {
         "value": -1,
         "description": "An ID that represents the absence of a browser tab."
diff --git a/chrome/common/media/cdm_registration.cc b/chrome/common/media/cdm_registration.cc
index b575456..ea0c793 100644
--- a/chrome/common/media/cdm_registration.cc
+++ b/chrome/common/media/cdm_registration.cc
@@ -10,6 +10,7 @@
 #include "base/logging.h"
 #include "base/path_service.h"
 #include "build/build_config.h"
+#include "media/cdm/cdm_capability.h"
 #include "third_party/widevine/cdm/buildflags.h"
 
 #if BUILDFLAG(ENABLE_LIBRARY_CDMS)
@@ -52,7 +53,7 @@
 std::unique_ptr<content::CdmInfo> CreateWidevineCdmInfo(
     const base::Version& version,
     const base::FilePath& cdm_library_path,
-    content::CdmCapability capability) {
+    media::CdmCapability capability) {
   return std::make_unique<content::CdmInfo>(
       kWidevineKeySystem, Robustness::kSoftwareSecure, std::move(capability),
       /*supports_sub_key_systems=*/false, kWidevineCdmDisplayName,
@@ -76,7 +77,7 @@
   // Manifest should be at the top level.
   auto manifest_path = cdm_base_path.Append(FILE_PATH_LITERAL("manifest.json"));
   base::Version version;
-  content::CdmCapability capability;
+  media::CdmCapability capability;
   if (!ParseCdmManifestFromPath(manifest_path, &version, &capability))
     return nullptr;
 
@@ -108,7 +109,7 @@
 
   // As there is no manifest, set |capability| as if it came from one. These
   // values must match the CDM that is being bundled with Chrome.
-  content::CdmCapability capability;
+  media::CdmCapability capability;
 
   // Add the supported codecs as if they came from the component manifest.
   capability.video_codecs.push_back(media::VideoCodec::kCodecVP8);
@@ -226,7 +227,7 @@
 
 void AddHardwareSecureWidevine(std::vector<content::CdmInfo>* cdms) {
 #if BUILDFLAG(USE_CHROMEOS_PROTECTED_MEDIA)
-  content::CdmCapability capability;
+  media::CdmCapability capability;
 
   // We currently support VP9, H264 and HEVC.
   capability.video_codecs.push_back(media::VideoCodec::kCodecVP9);
@@ -294,7 +295,7 @@
       "org.chromium.externalclearkey.differentguid";
 
   // Supported codecs are hard-coded in ExternalClearKeyProperties.
-  content::CdmCapability capability(
+  media::CdmCapability capability(
       {}, {media::EncryptionScheme::kCenc, media::EncryptionScheme::kCbcs},
       {media::CdmSessionType::kTemporary,
        media::CdmSessionType::kPersistentLicense});
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn
index c8022e4..927c8633 100644
--- a/chrome/test/BUILD.gn
+++ b/chrome/test/BUILD.gn
@@ -994,6 +994,8 @@
       "../browser/apps/platform_apps/event_page_browsertest.cc",
       "../browser/apps/platform_apps/platform_app_navigation_redirector_browsertest.cc",
       "../browser/apps/platform_apps/service_worker_browsertest.cc",
+      "../browser/attribution_reporting/conversions_usage_restriction_trial_browsertest.cc",
+      "../browser/attribution_reporting/conversions_usecounter_browsertest.cc",
       "../browser/autocomplete/autocomplete_browsertest.cc",
       "../browser/autofill/autofill_autocomplete_browsertest.cc",
       "../browser/autofill/autofill_browsertest.cc",
@@ -1048,8 +1050,6 @@
       "../browser/component_updater/component_patcher_operation_browsertest.cc",
       "../browser/content_index/content_index_browsertest.cc",
       "../browser/content_settings/content_settings_browsertest.cc",
-      "../browser/conversions/conversions_usage_restriction_trial_browsertest.cc",
-      "../browser/conversions/conversions_usecounter_browsertest.cc",
       "../browser/crash_recovery_browsertest.cc",
       "../browser/custom_handlers/protocol_handler_registry_browsertest.cc",
       "../browser/data_reduction_proxy/data_reduction_proxy_browsertest.cc",
@@ -1598,6 +1598,7 @@
       "../browser/usb/usb_browsertest.cc",
       "../browser/wake_lock/wake_lock_browsertest.cc",
       "../browser/webauthn/chrome_webauthn_browsertest.cc",
+      "../browser/window_placement/window_placement_browsertest.cc",
       "../browser/window_placement/window_placement_permission_context_browsertest.cc",
       "../common/time_format_browsertest.cc",
       "../renderer/autofill/autofill_renderer_browsertest.cc",
@@ -1841,6 +1842,7 @@
 
         if (!is_official_build) {
           deps += [
+            "//chromeos/components/demo_mode_app_ui:browser_tests_js",
             "//chromeos/components/sample_system_web_app_ui:browser_tests_js",
             "//chromeos/components/telemetry_extension_ui/test:browser_tests_js",
           ]
@@ -2923,6 +2925,7 @@
       data += [ "../browser/resources/chromeos/accessibility/switch_access/test_support.js" ]
       if (!is_official_build) {
         sources += [
+          "../browser/ash/web_applications/demo_mode_app_integration_browsertest.cc",
           "../browser/ash/web_applications/sample_system_web_app_integration_browsertest.cc",
           "../browser/ash/web_applications/telemetry_extension_integration_browsertest.cc",
         ]
diff --git a/chrome/test/android/javatests/src/org/chromium/chrome/test/pagecontroller/tests/webapk/MapsGoFirstRunTest.java b/chrome/test/android/javatests/src/org/chromium/chrome/test/pagecontroller/tests/webapk/MapsGoFirstRunTest.java
index d7f8455..381981e75 100644
--- a/chrome/test/android/javatests/src/org/chromium/chrome/test/pagecontroller/tests/webapk/MapsGoFirstRunTest.java
+++ b/chrome/test/android/javatests/src/org/chromium/chrome/test/pagecontroller/tests/webapk/MapsGoFirstRunTest.java
@@ -27,6 +27,7 @@
 import org.chromium.base.test.util.CallbackHelper;
 import org.chromium.base.test.util.CommandLineFlags;
 import org.chromium.base.test.util.CriteriaHelper;
+import org.chromium.base.test.util.DisabledTest;
 import org.chromium.chrome.browser.firstrun.FirstRunActivity;
 import org.chromium.chrome.browser.firstrun.FirstRunStatus;
 import org.chromium.chrome.browser.firstrun.LightweightFirstRunActivity;
@@ -151,6 +152,7 @@
 
     @Test
     @CommandLineFlags.Add({FLAG_POLICY_TOS_DIALOG_BEHAVIOR_STANDARD})
+    @DisabledTest(message = "https://crbug.com/1184149")
     public void testTosNotSkippedByPolicy() {
         LightweightFirstRunActivity.setSupportSkippingTos(true);
         FirstRunStatus.setLightweightFirstRunFlowComplete(false);
diff --git a/chrome/test/data/pdf/viewport_test.js b/chrome/test/data/pdf/viewport_test.js
index af6d16b..66e94fd 100644
--- a/chrome/test/data/pdf/viewport_test.js
+++ b/chrome/test/data/pdf/viewport_test.js
@@ -135,6 +135,25 @@
     chrome.test.succeed();
   },
 
+  // Regression test for https://crbug.com/1202725.
+  function testGetMostVisiblePageZeroSize() {
+    // This happens when the PDF is in a hidden iframe.
+    const mockWindow = new MockElement(0, 0, null);
+    const viewport = getZoomableViewport(mockWindow, new MockSizer(), 0, 1);
+
+    const documentDimensions = new MockDocumentDimensions(100, 100);
+    documentDimensions.addPage(50, 100);
+    documentDimensions.addPage(100, 100);
+    documentDimensions.addPage(100, 200);
+    viewport.setDocumentDimensions(documentDimensions);
+
+    // Zoom is computed as 0.
+    chrome.test.assertEq(0, viewport.getZoom());
+    // This call should not crash.
+    chrome.test.assertEq(0, viewport.getMostVisiblePage());
+    chrome.test.succeed();
+  },
+
   function testGetMostVisiblePage() {
     const mockWindow = new MockElement(100, 100, null);
     const viewport = getZoomableViewport(mockWindow, new MockSizer(), 0, 1);
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 26d5d53..996d2062 100644
--- a/chrome/test/data/webui/chromeos/diagnostics/network_list_test.js
+++ b/chrome/test/data/webui/chromeos/diagnostics/network_list_test.js
@@ -34,12 +34,20 @@
     return flushTasks();
   }
 
-  test('NetworkListPopulated', () => {
-    return initializeNetworkList().then(() => {
-      // TODO(michaelcheco): Remove when network-info elements are created
-      // and used in network-list.
-      dx_utils.assertElementContainsText(
-          networkListElement.$$('#networkListContainer'), 'Network list');
-    });
+  /**
+   * Returns the connectivity-card element.
+   * @return {!ConnectivityCardElement}
+   */
+  function getConnectivityCard() {
+    const connectivityCard =
+        /** @type {!ConnectivityCardElement} */ (
+            networkListElement.$$('connectivity-card'));
+    assertTrue(!!connectivityCard);
+    return connectivityCard;
+  }
+
+  test('ConnectivityCardInitialized', () => {
+    return initializeNetworkList().then(
+        () => assertTrue(!!getConnectivityCard()));
   });
 }
\ No newline at end of file
diff --git a/chrome/test/data/webui/cr_components/chromeos/cellular_setup/BUILD.gn b/chrome/test/data/webui/cr_components/chromeos/cellular_setup/BUILD.gn
index 0616e4c7..8a2799bb 100644
--- a/chrome/test/data/webui/cr_components/chromeos/cellular_setup/BUILD.gn
+++ b/chrome/test/data/webui/cr_components/chromeos/cellular_setup/BUILD.gn
@@ -34,5 +34,7 @@
                          "cellular_setup.FakeMediaDevices|FakeMediaDevices",
                          "cellular_setup.FakeCanvasContext|FakeCanvasContext",
                          "cellular_setup.MockMetricsPrivate|MockMetricsPrivate",
+                         "test_util.eventToPromise|eventToPromise",
+                         "test_util.flushTasks|flushTasks",
                        ]
 }
diff --git a/chrome/test/data/webui/cr_components/chromeos/cellular_setup/activation_code_page_test.js b/chrome/test/data/webui/cr_components/chromeos/cellular_setup/activation_code_page_test.js
index 169744d..c9d41b44 100644
--- a/chrome/test/data/webui/cr_components/chromeos/cellular_setup/activation_code_page_test.js
+++ b/chrome/test/data/webui/cr_components/chromeos/cellular_setup/activation_code_page_test.js
@@ -10,6 +10,7 @@
 // #import {assertTrue} from '../../../chai_assert.js';
 // #import {FakeMediaDevices} from './fake_media_devices.m.js';
 // #import {FakeBarcodeDetector, FakeImageCapture} from './fake_barcode_detector.m.js';
+// #import {eventToPromise, flushTasks} from 'chrome://test/test_util.m.js';
 // clang-format on
 
 suite('CrComponentsActivationCodePageTest', function() {
@@ -127,12 +128,17 @@
     assertTrue(scanFinishContainer.hidden);
     assertTrue(switchCameraButton.hidden);
 
+    const focusNextButtonPromise =
+        test_util.eventToPromise('focus-default-button', activationCodePage);
+
     // Scan a new code.
     await intervalFunction();
     await flushAsync();
 
     // The scanFinishContainer and scanSuccessContainer should now be visible,
-    // video, start scanning UI and scanFailureContainer hidden.
+    // video, start scanning UI, scanFailureContainer hidden and nextbutton
+    // is focused.
+    await Promise.all([focusNextButtonPromise, test_util.flushTasks()]);
     assertFalse(scanFinishContainer.hidden);
     assertTrue(startScanningContainer.hidden);
     assertTrue(video.hidden);
diff --git a/chrome/updater/BUILD.gn b/chrome/updater/BUILD.gn
index 066d692..628757a 100644
--- a/chrome/updater/BUILD.gn
+++ b/chrome/updater/BUILD.gn
@@ -492,6 +492,7 @@
       ":lib",
       ":updater_test_sources",
       "//base",
+      "//base/test:test_support",
       "//url",
     ]
   }
diff --git a/chrome/updater/test/integration_tests_helper.cc b/chrome/updater/test/integration_tests_helper.cc
index 6785b02..660ce5e 100644
--- a/chrome/updater/test/integration_tests_helper.cc
+++ b/chrome/updater/test/integration_tests_helper.cc
@@ -2,9 +2,12 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#include <map>
 #include <string>
+#include <utility>
 
 #include "base/at_exit.h"
+#include "base/callback.h"
 #include "base/command_line.h"
 #include "base/logging.h"
 #include "base/strings/string_number_conversions.h"
@@ -12,6 +15,7 @@
 #include "base/task/single_thread_task_executor.h"
 #include "base/task/thread_pool.h"
 #include "base/task/thread_pool/thread_pool_instance.h"
+#include "base/test/bind.h"
 #include "base/threading/thread_restrictions.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "build/build_config.h"
@@ -25,15 +29,109 @@
 namespace test {
 namespace {
 
-constexpr char kAppId[] = "app_id";
 constexpr int kSuccess = 0;
 constexpr int kUnknownSwitch = 101;
-constexpr int kMissingAppIdSwitch = 102;
-constexpr int kMissingUrlSwitch = 103;
-constexpr int kMissingExitCodeSwitch = 104;
-constexpr int kBadExitCodeSwitch = 105;
-constexpr int kMissingPathSwitch = 106;
-constexpr int kMissingVersionParameter = 107;
+constexpr int kBadCommand = 102;
+
+template <typename... Args>
+base::RepeatingCallback<bool(Args...)> WithSwitch(
+    const std::string& flag,
+    base::RepeatingCallback<bool(const std::string&, Args...)> callback) {
+  return base::BindLambdaForTesting([=](Args... args) {
+    const base::CommandLine* command_line =
+        base::CommandLine::ForCurrentProcess();
+    if (command_line->HasSwitch(flag)) {
+      return callback.Run(command_line->GetSwitchValueASCII(flag),
+                          std::move(args)...);
+    }
+    LOG(ERROR) << "Missing switch: " << flag;
+    return false;
+  });
+}
+
+// Overload for int switches.
+template <typename... Args>
+base::RepeatingCallback<bool(Args...)> WithSwitch(
+    const std::string& flag,
+    base::RepeatingCallback<bool(int, Args...)> callback) {
+  return WithSwitch(
+      flag,
+      base::BindLambdaForTesting([=](const std::string& flag, Args... args) {
+        int flag_int = -1;
+        if (base::StringToInt(flag, &flag_int)) {
+          return callback.Run(flag_int, std::move(args)...);
+        }
+        return false;
+      }));
+}
+
+// Overload for GURL switches.
+template <typename... Args>
+base::RepeatingCallback<bool(Args...)> WithSwitch(
+    const std::string& flag,
+    base::RepeatingCallback<bool(const GURL&, Args...)> callback) {
+  return WithSwitch(
+      flag,
+      base::BindLambdaForTesting([=](const std::string& flag, Args... args) {
+        return callback.Run(GURL(flag), std::move(args)...);
+      }));
+}
+
+// Overload for FilePath switches.
+template <typename... Args>
+base::RepeatingCallback<bool(Args...)> WithSwitch(
+    const std::string& flag,
+    base::RepeatingCallback<bool(const base::FilePath&, Args...)> callback) {
+  return WithSwitch(
+      flag,
+      base::BindLambdaForTesting([=](const std::string& flag, Args... args) {
+        return callback.Run(base::FilePath::FromUTF8Unsafe(flag),
+                            std::move(args)...);
+      }));
+}
+
+template <typename Arg, typename... RemainingArgs>
+base::RepeatingCallback<bool(RemainingArgs...)> WithArg(
+    Arg arg,
+    base::RepeatingCallback<bool(Arg, RemainingArgs...)> callback) {
+  return base::BindRepeating(callback, arg);
+}
+
+// Adapts the input callback to take a shutdown callback as the final parameter.
+template <typename... Args>
+base::RepeatingCallback<bool(Args..., base::OnceCallback<void(int)>)>
+WithShutdown(base::RepeatingCallback<int(Args...)> callback) {
+  return base::BindLambdaForTesting(
+      [=](Args... args, base::OnceCallback<void(int)> shutdown) {
+        std::move(shutdown).Run(callback.Run(args...));
+        return true;
+      });
+}
+
+// Short-named wrapper around BindOnce.
+template <typename... Args, typename... ProvidedArgs>
+base::RepeatingCallback<bool(Args..., base::OnceCallback<void(int)>)> Wrap(
+    int (*func)(Args...),
+    ProvidedArgs... provided_args) {
+  return WithShutdown(base::BindRepeating(func, provided_args...));
+}
+
+// Overload of Wrap for functions that return void. (Returns kSuccess.)
+template <typename... Args>
+base::RepeatingCallback<bool(Args..., base::OnceCallback<void(int)>)> Wrap(
+    void (*func)(Args...)) {
+  return WithShutdown(base::BindLambdaForTesting([=](Args... args) {
+    func(args...);
+    return kSuccess;
+  }));
+}
+
+// Helper to shorten lines below.
+template <typename... Args>
+base::RepeatingCallback<bool(Args...)> WithSystemScope(
+    base::RepeatingCallback<bool(UpdaterScope, Args...)> callback) {
+  return WithArg(UpdaterScope::kSystem, callback);
+}
 
 class AppTestHelper : public App {
  private:
@@ -43,145 +141,58 @@
 };
 
 void AppTestHelper::FirstTaskRun() {
-  base::ScopedAllowBlockingForTesting allow_blocking;
+  std::map<std::string,
+           base::RepeatingCallback<bool(base::OnceCallback<void(int)>)>>
+      commands = {
+        // To add additional commands, first Wrap a pointer to the target
+        // function (which should be declared in integration_tests_impl.h), and
+        // then use the With* helper functions to provide its arguments.
+        {"clean", WithSystemScope(Wrap(&Clean))},
+        {"copy_log", WithSwitch("path", Wrap(&CopyLog))},
+        {"enter_test_mode", WithSwitch("url", Wrap(&EnterTestMode))},
+        {"expect_active_updater", WithSystemScope(Wrap(&ExpectActiveUpdater))},
+        {"expect_app_unregistered_existence_checker_path",
+         WithSwitch("app_id",
+                    Wrap(&ExpectAppUnregisteredExistenceCheckerPath))},
+        {"expect_candidate_uninstalled",
+         WithSystemScope(Wrap(&ExpectCandidateUninstalled))},
+        {"expect_clean", WithSystemScope(Wrap(&ExpectClean))},
+        {"expect_installed", WithSystemScope(Wrap(&ExpectInstalled))},
+        {"expect_version_active",
+         WithSwitch("version", Wrap(&ExpectVersionActive))},
+        {"expect_version_not_active",
+         WithSwitch("version", Wrap(&ExpectVersionNotActive))},
+        {"install", WithSystemScope(Wrap(&Install))},
+        {"print_log", WithSystemScope(Wrap(&PrintLog))},
+        {"run_wake", WithSwitch("exit_code", WithSystemScope(Wrap(&RunWake)))},
+#if defined(OS_MAC)
+        {"register_app", WithSwitch("app_id", Wrap(&RegisterApp))},
+        {"register_test_app", WithSystemScope(Wrap(&RegisterTestApp))},
+#endif  // defined(OS_MAC)
+        {"set_fake_existence_checker_path",
+         WithSwitch("app_id", Wrap(&SetFakeExistenceCheckerPath))},
+        {"setup_fake_updater_higher_version",
+         WithSystemScope(Wrap(&SetupFakeUpdaterHigherVersion))},
+        {"setup_fake_updater_lower_version",
+         WithSystemScope(Wrap(&SetupFakeUpdaterLowerVersion))},
+        {"uninstall", WithSystemScope(Wrap(&Uninstall))},
+      };
+
   const base::CommandLine* command_line =
       base::CommandLine::ForCurrentProcess();
-  scoped_refptr<base::SequencedTaskRunner> task_runner =
-      base::ThreadPool::CreateSequencedTaskRunner({base::MayBlock()});
-
-  if (command_line->HasSwitch("clean")) {
-    task_runner->PostTaskAndReply(
-        FROM_HERE, base::BindOnce(&Clean, UpdaterScope::kSystem),
-        base::BindOnce(&AppTestHelper::Shutdown, this, kSuccess));
-  } else if (command_line->HasSwitch("expect_clean")) {
-    ExpectClean(UpdaterScope::kSystem);
-    Shutdown(kSuccess);
-  } else if (command_line->HasSwitch("install")) {
-    task_runner->PostTaskAndReply(
-        FROM_HERE, base::BindOnce(&Install, UpdaterScope::kSystem),
-        base::BindOnce(&AppTestHelper::Shutdown, this, kSuccess));
-  } else if (command_line->HasSwitch("expect_installed")) {
-    task_runner->PostTaskAndReply(
-        FROM_HERE, base::BindOnce(&ExpectInstalled, UpdaterScope::kSystem),
-        base::BindOnce(&AppTestHelper::Shutdown, this, kSuccess));
-  } else if (command_line->HasSwitch("uninstall")) {
-    task_runner->PostTaskAndReply(
-        FROM_HERE, base::BindOnce(&Uninstall, UpdaterScope::kSystem),
-        base::BindOnce(&AppTestHelper::Shutdown, this, kSuccess));
-  } else if (command_line->HasSwitch("expect_candidate_uninstalled")) {
-    task_runner->PostTaskAndReply(
-        FROM_HERE,
-        base::BindOnce(&ExpectCandidateUninstalled, UpdaterScope::kSystem),
-        base::BindOnce(&AppTestHelper::Shutdown, this, kSuccess));
-  } else if (command_line->HasSwitch("enter_test_mode")) {
-    if (command_line->HasSwitch("url")) {
-      GURL url(command_line->GetSwitchValueASCII("url"));
-      EnterTestMode(url);
-      Shutdown(kSuccess);
-    } else {
-      Shutdown(kMissingUrlSwitch);
-    }
-  } else if (command_line->HasSwitch("expect_active_updater")) {
-    task_runner->PostTaskAndReply(
-        FROM_HERE, base::BindOnce(&ExpectActiveUpdater, UpdaterScope::kSystem),
-        base::BindOnce(&AppTestHelper::Shutdown, this, kSuccess));
-  } else if (command_line->HasSwitch("expect_version_active")) {
-    if (command_line->HasSwitch("version")) {
-      task_runner->PostTaskAndReply(
-          FROM_HERE,
-          base::BindOnce(&ExpectVersionActive,
-                         command_line->GetSwitchValueASCII("version")),
-          base::BindOnce(&AppTestHelper::Shutdown, this, kSuccess));
-    } else {
-      Shutdown(kMissingVersionParameter);
-    }
-  } else if (command_line->HasSwitch("expect_version_not_active")) {
-    if (command_line->HasSwitch("version")) {
-      task_runner->PostTaskAndReply(
-          FROM_HERE,
-          base::BindOnce(&ExpectVersionNotActive,
-                         command_line->GetSwitchValueASCII("version")),
-          base::BindOnce(&AppTestHelper::Shutdown, this, kSuccess));
-    } else {
-      Shutdown(kMissingVersionParameter);
-    }
-  } else if (command_line->HasSwitch("run_wake")) {
-    if (command_line->HasSwitch("exit_code")) {
-      int exit_code = -1;
-      if (base::StringToInt(command_line->GetSwitchValueASCII("exit_code"),
-                            &exit_code)) {
-        task_runner->PostTaskAndReply(
-            FROM_HERE,
-            base::BindOnce(&RunWake, UpdaterScope::kSystem, exit_code),
-            base::BindOnce(&AppTestHelper::Shutdown, this, kSuccess));
-      } else {
-        Shutdown(kBadExitCodeSwitch);
+  for (const auto& entry : commands) {
+    if (command_line->HasSwitch(entry.first)) {
+      base::ScopedAllowBlockingForTesting allow_blocking;
+      if (!entry.second.Run(base::BindOnce(&AppTestHelper::Shutdown, this))) {
+        Shutdown(kBadCommand);
       }
-    } else {
-      Shutdown(kMissingExitCodeSwitch);
-    }
-  } else if (command_line->HasSwitch("setup_fake_updater_higher_version")) {
-    SetupFakeUpdaterHigherVersion(UpdaterScope::kSystem);
-    Shutdown(kSuccess);
-  } else if (command_line->HasSwitch("setup_fake_updater_lower_version")) {
-    SetupFakeUpdaterLowerVersion(UpdaterScope::kSystem);
-    Shutdown(kSuccess);
-  } else if (command_line->HasSwitch("set_fake_existence_checker_path")) {
-    if (command_line->HasSwitch(kAppId)) {
-      SetFakeExistenceCheckerPath(command_line->GetSwitchValueASCII(kAppId));
-      Shutdown(kSuccess);
-    } else {
-      Shutdown(kMissingAppIdSwitch);
-    }
-  } else if (command_line->HasSwitch(
-                 "expect_app_unregistered_existence_checker_path")) {
-    if (command_line->HasSwitch(kAppId)) {
-      ExpectAppUnregisteredExistenceCheckerPath(
-          command_line->GetSwitchValueASCII(kAppId));
-      Shutdown(kSuccess);
-    } else {
-      Shutdown(kMissingAppIdSwitch);
-    }
-  } else if (command_line->HasSwitch("print_log")) {
-    task_runner->PostTaskAndReply(
-        FROM_HERE, base::BindOnce(&PrintLog, UpdaterScope::kSystem),
-        base::BindOnce(&AppTestHelper::Shutdown, this, kSuccess));
-  } else if (command_line->HasSwitch("copy_log")) {
-    if (command_line->HasSwitch("path")) {
-      const std::string path_string = command_line->GetSwitchValueASCII("path");
-      base::FilePath path;
-#if defined(OS_WIN)
-      base::FilePath::StringType str;
-      path = base::UTF8ToWide(path_string.c_str(), path_string.size(), &str)
-                 ? base::FilePath(str)
-                 : base::FilePath();
-#else
-      path = base::FilePath(path_string);
-#endif  // OS_WIN
-      CopyLog(path);
-      Shutdown(kSuccess);
-    } else {
-      Shutdown(kMissingPathSwitch);
+      return;
     }
   }
-#if defined(OS_MAC)
-  else if (command_line->HasSwitch("register_app")) {
-    if (command_line->HasSwitch(kAppId)) {
-      RegisterApp(command_line->GetSwitchValueASCII(kAppId));
-      Shutdown(kSuccess);
-    } else {
-      Shutdown(kMissingAppIdSwitch);
-    }
-  } else if (command_line->HasSwitch("register_test_app")) {
-    RegisterTestApp(UpdaterScope::kSystem);
-    Shutdown(kSuccess);
-  }
-#endif
-  else {
-    VLOG(0) << "No supported switch provided. Command: "
-            << command_line->GetCommandLineString();
-    Shutdown(kUnknownSwitch);
-  }
+
+  LOG(ERROR) << "No supported switch provided. Command: "
+             << command_line->GetCommandLineString();
+  Shutdown(kUnknownSwitch);
 }
 
 void AppTestHelper::InitializeThreadPool() {
diff --git a/chromecast/common/cast_content_client.cc b/chromecast/common/cast_content_client.cc
index ce57ebc..05277ab 100644
--- a/chromecast/common/cast_content_client.cc
+++ b/chromecast/common/cast_content_client.cc
@@ -50,6 +50,7 @@
 #if BUILDFLAG(BUNDLE_WIDEVINE_CDM) && defined(OS_LINUX)
 #include "base/no_destructor.h"
 #include "components/cdm/common/cdm_manifest.h"
+#include "media/cdm/cdm_capability.h"
 #include "third_party/widevine/cdm/widevine_cdm_common.h"  // nogncheck
 // component updated CDM on all desktop platforms and remove this.
 // This file is In SHARED_INTERMEDIATE_DIR.
@@ -97,7 +98,7 @@
 std::unique_ptr<content::CdmInfo> CreateWidevineCdmInfo(
     const base::Version& version,
     const base::FilePath& cdm_library_path,
-    content::CdmCapability capability) {
+    media::CdmCapability capability) {
   return std::make_unique<content::CdmInfo>(
       kWidevineKeySystem, content::CdmInfo::Robustness::kSoftwareSecure,
       std::move(capability), /*supports_sub_key_systems=*/false,
@@ -125,7 +126,7 @@
   // Manifest should be at the top level.
   auto manifest_path = cdm_base_path.Append(FILE_PATH_LITERAL("manifest.json"));
   base::Version version;
-  content::CdmCapability capability;
+  media::CdmCapability capability;
   if (!ParseCdmManifestFromPath(manifest_path, &version, &capability))
     return nullptr;
 
diff --git a/chromeos/components/demo_mode_app_ui/BUILD.gn b/chromeos/components/demo_mode_app_ui/BUILD.gn
new file mode 100644
index 0000000..ae0d1c2
--- /dev/null
+++ b/chromeos/components/demo_mode_app_ui/BUILD.gn
@@ -0,0 +1,44 @@
+# Copyright 2021 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import("//chrome/test/base/js2gtest.gni")
+import("//ui/webui/resources/tools/generate_grd.gni")
+
+assert(is_chromeos, "Demo Mode App is Chrome OS only")
+assert(!is_official_build, "Demo Mode App is only built for unofficial builds")
+
+static_library("demo_mode_app_ui") {
+  sources = [
+    "demo_mode_app_ui.cc",
+    "demo_mode_app_ui.h",
+    "url_constants.cc",
+    "url_constants.h",
+  ]
+
+  deps = [
+    "//chromeos/resources:demo_mode_app_resources",
+    "//content/public/browser",
+    "//ui/webui",
+  ]
+}
+
+js2gtest("browser_tests_js") {
+  test_type = "mojo_lite_webui"
+
+  sources = [ "test/demo_mode_app_ui_browsertest.js" ]
+
+  defines = [ "HAS_OUT_OF_PROC_TEST_RUNNER" ]
+}
+
+generate_grd("build_grd") {
+  input_files_base_dir = rebase_path("resources", "//")
+  input_files = [
+    "app_icon_192.png",
+    "demo_mode_app.js",
+    "demo_mode_app.html",
+  ]
+  manifest_files = []
+  grd_prefix = "chromeos_demo_mode_app"
+  out_grd = "$target_gen_dir/${grd_prefix}_resources.grd"
+}
diff --git a/chromeos/components/demo_mode_app_ui/DEPS b/chromeos/components/demo_mode_app_ui/DEPS
new file mode 100644
index 0000000..e3c2f2c
--- /dev/null
+++ b/chromeos/components/demo_mode_app_ui/DEPS
@@ -0,0 +1,5 @@
+include_rules = [
+  "+chromeos/grit/chromeos_demo_mode_app_resources.h",
+  "+content/public/browser",
+  "+ui/webui",
+]
\ No newline at end of file
diff --git a/chromeos/components/demo_mode_app_ui/OWNERS b/chromeos/components/demo_mode_app_ui/OWNERS
new file mode 100644
index 0000000..db69c0d0
--- /dev/null
+++ b/chromeos/components/demo_mode_app_ui/OWNERS
@@ -0,0 +1,5 @@
+drcrash@chromium.org
+jacksontadie@google.com
+silinliu@google.com
+
+# Internal Component: b/components/812312
\ No newline at end of file
diff --git a/chromeos/components/demo_mode_app_ui/demo_mode_app_ui.cc b/chromeos/components/demo_mode_app_ui/demo_mode_app_ui.cc
new file mode 100644
index 0000000..864d59a
--- /dev/null
+++ b/chromeos/components/demo_mode_app_ui/demo_mode_app_ui.cc
@@ -0,0 +1,36 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chromeos/components/demo_mode_app_ui/demo_mode_app_ui.h"
+#include "chromeos/components/demo_mode_app_ui/url_constants.h"
+#include "chromeos/grit/chromeos_demo_mode_app_resources.h"
+#include "chromeos/grit/chromeos_demo_mode_app_resources_map.h"
+#include "content/public/browser/web_contents.h"
+#include "content/public/browser/web_ui_data_source.h"
+
+namespace chromeos {
+
+DemoModeAppUI::DemoModeAppUI(content::WebUI* web_ui)
+    : ui::MojoWebUIController(web_ui) {
+  content::WebUIDataSource* html_source =
+      content::WebUIDataSource::Create(chromeos::kChromeUIDemoModeAppHost);
+
+  // Add required resources.
+  for (size_t i = 0; i < kChromeosDemoModeAppResourcesSize; ++i) {
+    html_source->AddResourcePath(kChromeosDemoModeAppResources[i].path,
+                                 kChromeosDemoModeAppResources[i].id);
+  }
+
+  html_source->SetDefaultResource(
+      IDR_CHROMEOS_DEMO_MODE_APP_DEMO_MODE_APP_HTML);
+
+  auto* browser_context = web_ui->GetWebContents()->GetBrowserContext();
+  content::WebUIDataSource::Add(browser_context, html_source);
+}
+
+DemoModeAppUI::~DemoModeAppUI() = default;
+
+WEB_UI_CONTROLLER_TYPE_IMPL(DemoModeAppUI)
+
+}  // namespace chromeos
diff --git a/chromeos/components/demo_mode_app_ui/demo_mode_app_ui.h b/chromeos/components/demo_mode_app_ui/demo_mode_app_ui.h
new file mode 100644
index 0000000..9b7a24d
--- /dev/null
+++ b/chromeos/components/demo_mode_app_ui/demo_mode_app_ui.h
@@ -0,0 +1,26 @@
+// 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 CHROMEOS_COMPONENTS_DEMO_MODE_APP_UI_DEMO_MODE_APP_UI_H_
+#define CHROMEOS_COMPONENTS_DEMO_MODE_APP_UI_DEMO_MODE_APP_UI_H_
+
+#include "ui/webui/mojo_web_ui_controller.h"
+
+namespace chromeos {
+// The WebUI for chrome://demo-mode-app
+class DemoModeAppUI : public ui::MojoWebUIController {
+ public:
+  explicit DemoModeAppUI(content::WebUI* web_ui);
+  ~DemoModeAppUI() override;
+
+  DemoModeAppUI(const DemoModeAppUI&) = delete;
+  DemoModeAppUI& operator=(const DemoModeAppUI&) = delete;
+
+ private:
+  WEB_UI_CONTROLLER_TYPE_DECL();
+};
+
+}  // namespace chromeos
+
+#endif  // CHROMEOS_COMPONENTS_DEMO_MODE_APP_UI_DEMO_MODE_APP_UI_H_
diff --git a/chromeos/components/demo_mode_app_ui/resources/app_icon_192.png b/chromeos/components/demo_mode_app_ui/resources/app_icon_192.png
new file mode 100644
index 0000000..1419460f
--- /dev/null
+++ b/chromeos/components/demo_mode_app_ui/resources/app_icon_192.png
Binary files differ
diff --git a/chromeos/components/demo_mode_app_ui/resources/demo_mode_app.html b/chromeos/components/demo_mode_app_ui/resources/demo_mode_app.html
new file mode 100644
index 0000000..28ea452
--- /dev/null
+++ b/chromeos/components/demo_mode_app_ui/resources/demo_mode_app.html
@@ -0,0 +1,16 @@
+<!-- Copyright 2021 The Chromium Authors. All rights reserved.
+     Use of this source code is governed by a BSD-style license that can be
+     found in the LICENSE file. -->
+<!DOCTYPE HTML>
+<html i18n-values="dir:textdirection">
+<head>
+  <meta charset="utf-8">
+  <title>Demo Mode App Title</title>
+</head>
+<body>
+  <h1 id="header">Hello World</h1>
+</body>
+  <script src="demo_mode_app.js" type="module"></script>
+<!-- Below mojo script required to run browser tests -->
+<script src="chrome://resources/mojo/mojo/public/js/mojo_bindings_lite.js"></script>
+</html>
\ No newline at end of file
diff --git a/chromeos/components/demo_mode_app_ui/resources/demo_mode_app.js b/chromeos/components/demo_mode_app_ui/resources/demo_mode_app.js
new file mode 100644
index 0000000..20caad15
--- /dev/null
+++ b/chromeos/components/demo_mode_app_ui/resources/demo_mode_app.js
@@ -0,0 +1,6 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+var header = document.getElementById('header');
+header.textContent = 'Demo Mode App';
diff --git a/chromeos/components/demo_mode_app_ui/test/DEPS b/chromeos/components/demo_mode_app_ui/test/DEPS
new file mode 100644
index 0000000..ac5677f9
--- /dev/null
+++ b/chromeos/components/demo_mode_app_ui/test/DEPS
@@ -0,0 +1,4 @@
+include_rules = [
+  # Tests run in browser_tests, so can access things under chrome/test/base*.
+  "+chrome/test/base",
+]
diff --git a/chromeos/components/demo_mode_app_ui/test/demo_mode_app_ui_browsertest.js b/chromeos/components/demo_mode_app_ui/test/demo_mode_app_ui_browsertest.js
new file mode 100644
index 0000000..ec14a521
--- /dev/null
+++ b/chromeos/components/demo_mode_app_ui/test/demo_mode_app_ui_browsertest.js
@@ -0,0 +1,34 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+GEN('#include "ash/constants/ash_features.h"');
+GEN('#include "content/public/test/browser_test.h"')
+
+const HOST_ORIGIN = 'chrome://demo-mode-app';
+
+var DemoModeAppUIBrowserTest = class extends testing.Test {
+  /** @override */
+  get browsePreload() {
+    return HOST_ORIGIN;
+  }
+
+  /** @override */
+  get runAccessibilityChecks() {
+    return false;
+  }
+
+  /** @override */
+  get featureList() {
+    return {enabled: ['chromeos::features::kDemoModeSWA']};
+  }
+};
+
+// Tests that chrome://demo-mode-app runs js file and that it goes
+// somewhere instead of 404ing or crashing.
+TEST_F('DemoModeAppUIBrowserTest', 'HasChromeSchemeURL', () => {
+  const header = document.querySelector('h1');
+
+  assertEquals(header.innerText, 'Demo Mode App');
+  assertEquals(document.location.origin, HOST_ORIGIN);
+});
diff --git a/chromeos/components/demo_mode_app_ui/url_constants.cc b/chromeos/components/demo_mode_app_ui/url_constants.cc
new file mode 100644
index 0000000..ad7ce51
--- /dev/null
+++ b/chromeos/components/demo_mode_app_ui/url_constants.cc
@@ -0,0 +1,12 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chromeos/components/demo_mode_app_ui/url_constants.h"
+
+namespace chromeos {
+
+const char kChromeUIDemoModeAppHost[] = "demo-mode-app";
+const char kChromeUIDemoModeAppURL[] = "chrome://demo-mode-app/";
+
+}  // namespace chromeos
diff --git a/chromeos/components/demo_mode_app_ui/url_constants.h b/chromeos/components/demo_mode_app_ui/url_constants.h
new file mode 100644
index 0000000..276347db
--- /dev/null
+++ b/chromeos/components/demo_mode_app_ui/url_constants.h
@@ -0,0 +1,15 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROMEOS_COMPONENTS_DEMO_MODE_APP_UI_URL_CONSTANTS_H_
+#define CHROMEOS_COMPONENTS_DEMO_MODE_APP_UI_URL_CONSTANTS_H_
+
+namespace chromeos {
+
+extern const char kChromeUIDemoModeAppHost[];
+extern const char kChromeUIDemoModeAppURL[];
+
+}  // namespace chromeos
+
+#endif  // CHROMEOS_COMPONENTS_DEMO_MODE_APP_UI_URL_CONSTANTS_H_
diff --git a/chromeos/components/diagnostics_ui/resources/BUILD.gn b/chromeos/components/diagnostics_ui/resources/BUILD.gn
index 86222ad3..e62eaa4 100644
--- a/chromeos/components/diagnostics_ui/resources/BUILD.gn
+++ b/chromeos/components/diagnostics_ui/resources/BUILD.gn
@@ -80,7 +80,6 @@
 js_library("diagnostics_app") {
   deps = [
     ":battery_status_card",
-    ":connectivity_card",
     ":cpu_card",
     ":diagnostics_browser_proxy",
     ":diagnostics_card",
@@ -174,6 +173,7 @@
 
 js_library("network_list") {
   deps = [
+    ":connectivity_card",
     ":mojo_interface_provider",
     "//third_party/polymer/v3_0/components-chromium/polymer:polymer_bundled",
   ]
diff --git a/chromeos/components/diagnostics_ui/resources/diagnostics_app.html b/chromeos/components/diagnostics_ui/resources/diagnostics_app.html
index fde9b4a5..628d01c8a 100644
--- a/chromeos/components/diagnostics_ui/resources/diagnostics_app.html
+++ b/chromeos/components/diagnostics_ui/resources/diagnostics_app.html
@@ -85,9 +85,6 @@
     <overview-card id="overviewCard"></overview-card>
   </div>
   <div class="diagonstics-cards-container">
-    <div hidden="[[!isNetworkingEnabled_()]]">
-      <network-list></network-list>
-    </div>
     <template is="dom-if" if="[[showBatteryStatusCard_]]" restamp>
       <div class="card-width">
         <battery-status-card id="batteryStatusCard"
@@ -106,9 +103,7 @@
       </memory-card>
     </div>
     <div class="card-width" hidden="[[!isNetworkingEnabled_()]]">
-      <connectivity-card id="connectivityCard"
-        is-test-running="{{isTestRunning}}">
-      </connectivity-card>
+      <network-list></network-list>
     </div>
   </div>
   <div class="session-log-container">
diff --git a/chromeos/components/diagnostics_ui/resources/diagnostics_app.js b/chromeos/components/diagnostics_ui/resources/diagnostics_app.js
index ab81102c..95af1ee 100644
--- a/chromeos/components/diagnostics_ui/resources/diagnostics_app.js
+++ b/chromeos/components/diagnostics_ui/resources/diagnostics_app.js
@@ -7,7 +7,6 @@
 import 'chrome://resources/cr_elements/icons.m.js';
 import 'chrome://resources/polymer/v3_0/iron-icon/iron-icon.js';
 import './battery_status_card.js';
-import './connectivity_card.js';
 import './cpu_card.js';
 import './diagnostics_fonts_css.js';
 import './diagnostics_shared_css.js';
diff --git a/chromeos/components/diagnostics_ui/resources/network_list.html b/chromeos/components/diagnostics_ui/resources/network_list.html
index 7c262f4f..44e0a4b 100644
--- a/chromeos/components/diagnostics_ui/resources/network_list.html
+++ b/chromeos/components/diagnostics_ui/resources/network_list.html
@@ -1,6 +1,11 @@
 <style include="diagnostics-shared diagnostics-fonts">
+  #connectivityCard {
+    width: 100%;
+  }
 </style>
 
-<!-- TODO(michaelcheco): Remove "Network list" when connectivy and
-network-info elements are added here. -->
-<div id="networkListContainer">Network list</div>
+<div id="networkListContainer">
+  <connectivity-card id="connectivityCard"
+    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 2d083c8..a7a4294 100644
--- a/chromeos/components/diagnostics_ui/resources/network_list.js
+++ b/chromeos/components/diagnostics_ui/resources/network_list.js
@@ -2,6 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+import './connectivity_card.js';
 import './diagnostics_fonts_css.js';
 import './diagnostics_shared_css.js';
 
@@ -17,4 +18,12 @@
 
   _template: html`{__html_template__}`,
 
+  properties: {
+    /** @type {boolean} */
+    isTestRunning: {
+      type: Boolean,
+      value: false,
+      notify: true,
+    },
+  },
 });
diff --git a/chromeos/components/telemetry_extension_ui/diagnostics_service_converters.cc b/chromeos/components/telemetry_extension_ui/diagnostics_service_converters.cc
index 57514a7..38946d0 100644
--- a/chromeos/components/telemetry_extension_ui/diagnostics_service_converters.cc
+++ b/chromeos/components/telemetry_extension_ui/diagnostics_service_converters.cc
@@ -6,6 +6,7 @@
 
 #include "base/notreached.h"
 #include "base/optional.h"
+#include "base/strings/string_piece.h"
 #include "chrome/browser/ash/wilco_dtc_supportd/mojo_utils.h"
 #include "chromeos/components/telemetry_extension_ui/convert_ptr.h"
 #include "chromeos/components/telemetry_extension_ui/mojom/diagnostics_service.mojom.h"
@@ -20,9 +21,8 @@
 
 std::string GetStringFromMojoHandle(mojo::ScopedHandle handle) {
   base::ReadOnlySharedMemoryMapping shared_memory;
-  return MojoUtils::GetStringPieceFromMojoHandle(std::move(handle),
-                                                 &shared_memory)
-      .as_string();
+  return std::string(MojoUtils::GetStringPieceFromMojoHandle(std::move(handle),
+                                                             &shared_memory));
 }
 
 }  // namespace
diff --git a/chromeos/components/telemetry_extension_ui/telemetry_extension_untrusted_ui.cc b/chromeos/components/telemetry_extension_ui/telemetry_extension_untrusted_ui.cc
index dd2ff566..9d342c9 100644
--- a/chromeos/components/telemetry_extension_ui/telemetry_extension_untrusted_ui.cc
+++ b/chromeos/components/telemetry_extension_ui/telemetry_extension_untrusted_ui.cc
@@ -11,6 +11,7 @@
 #include "base/logging.h"
 #include "base/memory/ptr_util.h"
 #include "base/memory/ref_counted_memory.h"
+#include "base/strings/string_piece.h"
 #include "base/task/post_task.h"
 #include "base/task/task_traits.h"
 #include "base/task/thread_pool.h"
@@ -106,7 +107,7 @@
 
 void TelemetryExtensionUntrustedSource::AddResourcePath(base::StringPiece path,
                                                         int resource_id) {
-  path_to_idr_map_[path.as_string()] = resource_id;
+  path_to_idr_map_[std::string(path)] = resource_id;
 }
 
 void TelemetryExtensionUntrustedSource::OverrideContentSecurityPolicy(
diff --git a/chromeos/dbus/concierge_client.cc b/chromeos/dbus/concierge_client.cc
index 9247eb7..c2224a07 100644
--- a/chromeos/dbus/concierge_client.cc
+++ b/chromeos/dbus/concierge_client.cc
@@ -238,6 +238,13 @@
     CallMethod(concierge::kSetVmIdMethod, request, std::move(callback));
   }
 
+  void ReclaimVmMemory(
+      const vm_tools::concierge::ReclaimVmMemoryRequest& request,
+      DBusMethodCallback<vm_tools::concierge::ReclaimVmMemoryResponse> callback)
+      override {
+    CallMethod(concierge::kReclaimVmMemoryMethod, request, std::move(callback));
+  }
+
  protected:
   void Init(dbus::Bus* bus) override {
     concierge_proxy_ = bus->GetObjectProxy(
diff --git a/chromeos/dbus/concierge_client.h b/chromeos/dbus/concierge_client.h
index ec3c943..ac17def 100644
--- a/chromeos/dbus/concierge_client.h
+++ b/chromeos/dbus/concierge_client.h
@@ -258,6 +258,13 @@
       const vm_tools::concierge::SetVmIdRequest& request,
       DBusMethodCallback<vm_tools::concierge::SetVmIdResponse> callback) = 0;
 
+  // Reclaims memory of the given VM.
+  // |callback| is called after the method call finishes.
+  virtual void ReclaimVmMemory(
+      const vm_tools::concierge::ReclaimVmMemoryRequest& request,
+      DBusMethodCallback<vm_tools::concierge::ReclaimVmMemoryResponse>
+          callback) = 0;
+
   // Creates an instance of ConciergeClient.
   static std::unique_ptr<ConciergeClient> Create();
 
diff --git a/chromeos/dbus/fake_concierge_client.cc b/chromeos/dbus/fake_concierge_client.cc
index fe79bb7d..a531c95b 100644
--- a/chromeos/dbus/fake_concierge_client.cc
+++ b/chromeos/dbus/fake_concierge_client.cc
@@ -322,6 +322,15 @@
       FROM_HERE, base::BindOnce(std::move(callback), set_vm_id_response_));
 }
 
+void FakeConciergeClient::ReclaimVmMemory(
+    const vm_tools::concierge::ReclaimVmMemoryRequest& request,
+    DBusMethodCallback<vm_tools::concierge::ReclaimVmMemoryResponse> callback) {
+  reclaim_vm_memory_call_count_++;
+  base::ThreadTaskRunnerHandle::Get()->PostTask(
+      FROM_HERE,
+      base::BindOnce(std::move(callback), reclaim_vm_memory_response_));
+}
+
 void FakeConciergeClient::NotifyVmStarted(
     const vm_tools::concierge::VmStartedSignal& signal) {
   for (auto& observer : vm_observer_list_)
diff --git a/chromeos/dbus/fake_concierge_client.h b/chromeos/dbus/fake_concierge_client.h
index 2a3e275..ca0dd30 100644
--- a/chromeos/dbus/fake_concierge_client.h
+++ b/chromeos/dbus/fake_concierge_client.h
@@ -121,6 +121,11 @@
                DBusMethodCallback<vm_tools::concierge::SetVmIdResponse>
                    callback) override;
 
+  void ReclaimVmMemory(
+      const vm_tools::concierge::ReclaimVmMemoryRequest& request,
+      DBusMethodCallback<vm_tools::concierge::ReclaimVmMemoryResponse> callback)
+      override;
+
   const base::ObserverList<Observer>& observer_list() const {
     return observer_list_;
   }
@@ -170,6 +175,9 @@
   int resize_disk_image_call_count() const {
     return resize_disk_image_call_count_;
   }
+  int reclaim_vm_memory_call_count() const {
+    return reclaim_vm_memory_call_count_;
+  }
 
   void set_vm_started_signal_connected(bool connected) {
     is_vm_started_signal_connected_ = connected;
@@ -271,6 +279,11 @@
       base::Optional<vm_tools::concierge::SetVmIdResponse> set_vm_id_response) {
     set_vm_id_response_ = set_vm_id_response;
   }
+  void set_reclaim_vm_memory_response(
+      base::Optional<vm_tools::concierge::ReclaimVmMemoryResponse>
+          reclaim_vm_memory_response) {
+    reclaim_vm_memory_response_ = reclaim_vm_memory_response;
+  }
 
   void set_send_create_disk_image_response_delay(base::TimeDelta delay) {
     send_create_disk_image_response_delay_ = delay;
@@ -324,6 +337,7 @@
   int start_arc_vm_call_count_ = 0;
   int resize_disk_image_call_count_ = 0;
   int set_vm_id_call_count_ = 0;
+  int reclaim_vm_memory_call_count_ = 0;
 
   bool is_vm_started_signal_connected_ = true;
   bool is_vm_stopped_signal_connected_ = true;
@@ -361,6 +375,8 @@
   base::Optional<vm_tools::concierge::ResizeDiskImageResponse>
       resize_disk_image_response_;
   base::Optional<vm_tools::concierge::SetVmIdResponse> set_vm_id_response_;
+  base::Optional<vm_tools::concierge::ReclaimVmMemoryResponse>
+      reclaim_vm_memory_response_;
 
   base::TimeDelta send_create_disk_image_response_delay_;
   base::TimeDelta send_start_vm_response_delay_;
diff --git a/chromeos/login/auth/challenge_response/cert_utils.cc b/chromeos/login/auth/challenge_response/cert_utils.cc
index b63d7d32f..5fce3f07 100644
--- a/chromeos/login/auth/challenge_response/cert_utils.cc
+++ b/chromeos/login/auth/challenge_response/cert_utils.cc
@@ -25,7 +25,7 @@
           &spki_der_piece)) {
     return false;
   }
-  *spki_der = spki_der_piece.as_string();
+  *spki_der = std::string(spki_der_piece);
   return !spki_der->empty();
 }
 
diff --git a/chromeos/resources/BUILD.gn b/chromeos/resources/BUILD.gn
index ef73bf5..de3a9ba 100644
--- a/chromeos/resources/BUILD.gn
+++ b/chromeos/resources/BUILD.gn
@@ -170,6 +170,21 @@
     ]
     output_dir = "$root_gen_dir/chromeos"
   }
+
+  # Resources used by chrome://demo-mode-app
+  grit("demo_mode_app_resources") {
+    enable_input_discovery_for_gn_analyze = false
+    demo_mode_app_gen_dir = "$root_gen_dir/chromeos/components/demo_mode_app_ui"
+    source = "$demo_mode_app_gen_dir/chromeos_demo_mode_app_resources.grd"
+    deps = [ "//chromeos/components/demo_mode_app_ui:build_grd" ]
+    outputs = [
+      "grit/chromeos_demo_mode_app_resources.h",
+      "grit/chromeos_demo_mode_app_resources_map.cc",
+      "grit/chromeos_demo_mode_app_resources_map.h",
+      "chromeos_demo_mode_app_resources.pak",
+    ]
+    output_dir = "$root_gen_dir/chromeos"
+  }
 }
 
 # Resources used by chrome://telemetry-extension
diff --git a/chromeos/services/device_sync/cryptauth_device_syncer_impl.cc b/chromeos/services/device_sync/cryptauth_device_syncer_impl.cc
index 44bb8208..72930e7 100644
--- a/chromeos/services/device_sync/cryptauth_device_syncer_impl.cc
+++ b/chromeos/services/device_sync/cryptauth_device_syncer_impl.cc
@@ -537,6 +537,11 @@
       CryptAuthKeyBundle::Name::kDeviceSyncBetterTogetherGroupKey);
   DCHECK(group_key);
   if (group_key->private_key().empty()) {
+    PA_LOG(INFO)
+        << "CryptAuthDeviceSyncerImpl::" << __func__
+        << ": Missing group private key needed to decrypt device metadata. "
+        << "Finishing DeviceSync attempt and waiting for GCM message from "
+        << "CryptAuth when the group private key becomes available.";
     CryptAuthDeviceSyncResult::ResultCode result_code =
         did_non_fatal_error_occur_
             ? CryptAuthDeviceSyncResult::ResultCode::kFinishedWithNonFatalErrors
diff --git a/chromeos/tast_control.gni b/chromeos/tast_control.gni
index dadd22f..1c9a223 100644
--- a/chromeos/tast_control.gni
+++ b/chromeos/tast_control.gni
@@ -73,8 +73,4 @@
 
   # crbug.com/1202124
   "health.ProbeNetworkInfo",
-
-  # crbug.com/1202204,
-  "apps.LaunchHelpAppFromLauncher.stable",
-  "launcher.SearchBuiltInApps",
 ]
diff --git a/components/autofill/android/provider/autofill_provider_android.cc b/components/autofill/android/provider/autofill_provider_android.cc
index 433d710..937d71f 100644
--- a/components/autofill/android/provider/autofill_provider_android.cc
+++ b/components/autofill/android/provider/autofill_provider_android.cc
@@ -12,8 +12,8 @@
 #include "base/feature_list.h"
 #include "components/autofill/android/provider/form_data_android.h"
 #include "components/autofill/android/provider/jni_headers/AutofillProvider_jni.h"
+#include "components/autofill/core/browser/android_autofill_manager.h"
 #include "components/autofill/core/browser/autofill_driver.h"
-#include "components/autofill/core/browser/autofill_handler_proxy.h"
 #include "components/autofill/core/common/autofill_constants.h"
 #include "components/autofill/core/common/autofill_features.h"
 #include "content/public/browser/browser_thread.h"
@@ -80,7 +80,7 @@
 }
 
 void AutofillProviderAndroid::OnQueryFormFieldAutofill(
-    AutofillHandlerProxy* handler,
+    AndroidAutofillManager* manager,
     int32_t id,
     const FormData& form,
     const FormFieldData& field,
@@ -94,7 +94,7 @@
 
   // Focus or field value change will also trigger the query, so it should be
   // ignored if the form is same.
-  MaybeStartNewSession(handler, form, field, bounding_box);
+  MaybeStartNewSession(manager, form, field, bounding_box);
 
   JNIEnv* env = AttachCurrentThread();
   ScopedJavaLocalRef<jobject> obj = java_ref_.get(env);
@@ -113,18 +113,18 @@
 }
 
 void AutofillProviderAndroid::MaybeStartNewSession(
-    AutofillHandlerProxy* handler,
+    AndroidAutofillManager* manager,
     const FormData& form,
     const FormFieldData& field,
     const gfx::RectF& bounding_box) {
   // Don't start a new session when the new form is similar to the old form, the
-  // new handler is the same as the current handler, and the coordinates of the
+  // new manager is the same as the current manager, and the coordinates of the
   // relevant form field haven't changed.
   if (form_ && form_->SimilarFormAs(form) &&
-      IsCurrentlyLinkedHandler(handler)) {
+      IsCurrentlyLinkedManager(manager)) {
     size_t index;
     if (form_->GetFieldIndex(field, &index) &&
-        handler->driver()->TransformBoundingBoxToViewportCoordinates(
+        manager->driver()->TransformBoundingBoxToViewportCoordinates(
             form.fields[index].bounds) == form_->form().fields[index].bounds) {
       return;
     }
@@ -138,7 +138,7 @@
   form_ = std::make_unique<FormDataAndroid>(
       form, base::BindRepeating(
                 &AutofillDriver::TransformBoundingBoxToViewportCoordinates,
-                base::Unretained(handler->driver())));
+                base::Unretained(manager->driver())));
   field_id_ = field.global_id();
 
   size_t index;
@@ -149,27 +149,27 @@
 
   FormStructure* form_structure = nullptr;
   AutofillField* autofill_field = nullptr;
-  if (!handler->GetCachedFormAndField(form, field, &form_structure,
+  if (!manager->GetCachedFormAndField(form, field, &form_structure,
                                       &autofill_field)) {
     form_structure = nullptr;
   }
   gfx::RectF transformed_bounding = ToClientAreaBound(bounding_box);
 
   ScopedJavaLocalRef<jobject> form_obj = form_->GetJavaPeer(form_structure);
-  handler_ = handler->GetWeakPtr();
+  manager_ = manager->GetWeakPtr();
   Java_AutofillProvider_startAutofillSession(
       env, obj, form_obj, index, transformed_bounding.x(),
       transformed_bounding.y(), transformed_bounding.width(),
-      transformed_bounding.height(), handler->has_server_prediction());
+      transformed_bounding.height(), manager->has_server_prediction());
 }
 
 void AutofillProviderAndroid::OnAutofillAvailable(JNIEnv* env,
                                                   jobject jcaller,
                                                   jobject formData) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
-  if (handler_ && form_) {
+  if (manager_ && form_) {
     const FormData& form = form_->GetAutofillValues();
-    SendFormDataToRenderer(handler_.get(), id_, form);
+    SendFormDataToRenderer(manager_.get(), id_, form);
   }
 }
 
@@ -177,9 +177,9 @@
                                                          jobject jcaller,
                                                          jstring value) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
-  if (auto* handler = handler_.get()) {
+  if (auto* manager = manager_.get()) {
     RendererShouldAcceptDataListSuggestion(
-        handler, field_id_, ConvertJavaStringToUTF16(env, value));
+        manager, field_id_, ConvertJavaStringToUTF16(env, value));
   }
 }
 
@@ -199,22 +199,22 @@
 }
 
 void AutofillProviderAndroid::OnTextFieldDidChange(
-    AutofillHandlerProxy* handler,
+    AndroidAutofillManager* manager,
     const FormData& form,
     const FormFieldData& field,
     const gfx::RectF& bounding_box,
     const base::TimeTicks timestamp) {
-  FireFormFieldDidChanged(handler, form, field, bounding_box);
+  FireFormFieldDidChanged(manager, form, field, bounding_box);
 }
 
 void AutofillProviderAndroid::OnTextFieldDidScroll(
-    AutofillHandlerProxy* handler,
+    AndroidAutofillManager* manager,
     const FormData& form,
     const FormFieldData& field,
     const gfx::RectF& bounding_box) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
   size_t index;
-  if (!IsCurrentlyLinkedHandler(handler) || !IsCurrentlyLinkedForm(form) ||
+  if (!IsCurrentlyLinkedManager(manager) || !IsCurrentlyLinkedForm(form) ||
       !form_->GetSimilarFieldIndex(field, &index))
     return;
 
@@ -231,12 +231,12 @@
 }
 
 void AutofillProviderAndroid::OnSelectControlDidChange(
-    AutofillHandlerProxy* handler,
+    AndroidAutofillManager* manager,
     const FormData& form,
     const FormFieldData& field,
     const gfx::RectF& bounding_box) {
-  MaybeStartNewSession(handler, form, field, bounding_box);
-  FireFormFieldDidChanged(handler, form, field, bounding_box);
+  MaybeStartNewSession(manager, form, field, bounding_box);
+  FireFormFieldDidChanged(manager, form, field, bounding_box);
 }
 
 void AutofillProviderAndroid::FireSuccessfulSubmission(
@@ -250,12 +250,12 @@
   Reset();
 }
 
-void AutofillProviderAndroid::OnFormSubmitted(AutofillHandlerProxy* handler,
+void AutofillProviderAndroid::OnFormSubmitted(AndroidAutofillManager* manager,
                                               const FormData& form,
                                               bool known_success,
                                               SubmissionSource source) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
-  if (!IsCurrentlyLinkedHandler(handler) || !IsCurrentlyLinkedForm(form))
+  if (!IsCurrentlyLinkedManager(manager) || !IsCurrentlyLinkedForm(form))
     return;
 
   if (known_success || source == SubmissionSource::FORM_SUBMISSION) {
@@ -268,24 +268,24 @@
 }
 
 void AutofillProviderAndroid::OnFocusNoLongerOnForm(
-    AutofillHandlerProxy* handler,
+    AndroidAutofillManager* manager,
     bool had_interacted_form) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
-  if (!IsCurrentlyLinkedHandler(handler))
+  if (!IsCurrentlyLinkedManager(manager))
     return;
 
   OnFocusChanged(false, 0, RectF());
 }
 
 void AutofillProviderAndroid::OnFocusOnFormField(
-    AutofillHandlerProxy* handler,
+    AndroidAutofillManager* manager,
     const FormData& form,
     const FormFieldData& field,
     const gfx::RectF& bounding_box) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
 
   size_t index;
-  if (!IsCurrentlyLinkedHandler(handler) || !IsCurrentlyLinkedForm(form) ||
+  if (!IsCurrentlyLinkedManager(manager) || !IsCurrentlyLinkedForm(form) ||
       !form_->GetSimilarFieldIndex(field, &index))
     return;
 
@@ -311,13 +311,13 @@
 }
 
 void AutofillProviderAndroid::FireFormFieldDidChanged(
-    AutofillHandlerProxy* handler,
+    AndroidAutofillManager* manager,
     const FormData& form,
     const FormFieldData& field,
     const gfx::RectF& bounding_box) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
   size_t index;
-  if (!IsCurrentlyLinkedHandler(handler) || !IsCurrentlyLinkedForm(form) ||
+  if (!IsCurrentlyLinkedManager(manager) || !IsCurrentlyLinkedForm(form) ||
       !form_->GetSimilarFieldIndex(field, &index))
     return;
 
@@ -334,11 +334,11 @@
 }
 
 void AutofillProviderAndroid::OnDidFillAutofillFormData(
-    AutofillHandlerProxy* handler,
+    AndroidAutofillManager* manager,
     const FormData& form,
     base::TimeTicks timestamp) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
-  if (handler != handler_.get() || !IsCurrentlyLinkedForm(form))
+  if (manager != manager_.get() || !IsCurrentlyLinkedForm(form))
     return;
 
   JNIEnv* env = AttachCurrentThread();
@@ -349,12 +349,12 @@
   Java_AutofillProvider_onDidFillAutofillFormData(env, obj);
 }
 
-void AutofillProviderAndroid::OnFormsSeen(AutofillHandlerProxy* handler,
+void AutofillProviderAndroid::OnFormsSeen(AndroidAutofillManager* manager,
                                           const std::vector<FormData>& forms) {}
 
-void AutofillProviderAndroid::OnHidePopup(AutofillHandlerProxy* handler) {
+void AutofillProviderAndroid::OnHidePopup(AndroidAutofillManager* manager) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
-  if (handler == handler_.get()) {
+  if (manager == manager_.get()) {
     JNIEnv* env = AttachCurrentThread();
     ScopedJavaLocalRef<jobject> obj = java_ref_.get(env);
     if (obj.is_null())
@@ -365,13 +365,13 @@
 }
 
 void AutofillProviderAndroid::OnServerPredictionsAvailable(
-    AutofillHandlerProxy* handler) {
+    AndroidAutofillManager* manager) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
-  if (handler != handler_.get() || !form_.get())
+  if (manager != manager_.get() || !form_.get())
     return;
 
   if (auto* form_structure =
-          handler_->FindCachedFormByRendererId(form_->form().global_id())) {
+          manager_->FindCachedFormByRendererId(form_->form().global_id())) {
     form_->UpdateFieldTypes(*form_structure);
 
     JNIEnv* env = AttachCurrentThread();
@@ -384,13 +384,13 @@
 }
 
 void AutofillProviderAndroid::OnServerQueryRequestError(
-    AutofillHandlerProxy* handler,
+    AndroidAutofillManager* manager,
     FormSignature form_signature) {
-  if (!IsCurrentlyLinkedHandler(handler) || !form_.get())
+  if (!IsCurrentlyLinkedManager(manager) || !form_.get())
     return;
 
   if (auto* form_structure =
-          handler_->FindCachedFormByRendererId(form_->form().global_id())) {
+          manager_->FindCachedFormByRendererId(form_->form().global_id())) {
     if (form_structure->form_signature() != form_signature)
       return;
 
@@ -403,9 +403,9 @@
   }
 }
 
-void AutofillProviderAndroid::Reset(AutofillHandlerProxy* handler) {
+void AutofillProviderAndroid::Reset(AndroidAutofillManager* manager) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
-  if (handler == handler_.get()) {
+  if (manager == manager_.get()) {
     // If we previously received a notification from the renderer that the form
     // was likely submitted and no event caused a reset of state in the interim,
     // we consider this navigation to be resulting from the submission.
@@ -423,9 +423,9 @@
   }
 }
 
-bool AutofillProviderAndroid::IsCurrentlyLinkedHandler(
-    AutofillHandlerProxy* handler) {
-  return handler == handler_.get();
+bool AutofillProviderAndroid::IsCurrentlyLinkedManager(
+    AndroidAutofillManager* manager) {
+  return manager == manager_.get();
 }
 
 bool AutofillProviderAndroid::IsCurrentlyLinkedForm(const FormData& form) {
diff --git a/components/autofill/android/provider/autofill_provider_android.h b/components/autofill/android/provider/autofill_provider_android.h
index f2ffedc..7cc893f 100644
--- a/components/autofill/android/provider/autofill_provider_android.h
+++ b/components/autofill/android/provider/autofill_provider_android.h
@@ -34,46 +34,46 @@
 
   // AutofillProvider:
   void OnQueryFormFieldAutofill(
-      AutofillHandlerProxy* handler,
+      AndroidAutofillManager* manager,
       int32_t id,
       const FormData& form,
       const FormFieldData& field,
       const gfx::RectF& bounding_box,
       bool /*unused_autoselect_first_suggestion*/) override;
-  void OnTextFieldDidChange(AutofillHandlerProxy* handler,
+  void OnTextFieldDidChange(AndroidAutofillManager* manager,
                             const FormData& form,
                             const FormFieldData& field,
                             const gfx::RectF& bounding_box,
                             const base::TimeTicks timestamp) override;
-  void OnTextFieldDidScroll(AutofillHandlerProxy* handler,
+  void OnTextFieldDidScroll(AndroidAutofillManager* manager,
                             const FormData& form,
                             const FormFieldData& field,
                             const gfx::RectF& bounding_box) override;
-  void OnSelectControlDidChange(AutofillHandlerProxy* handler,
+  void OnSelectControlDidChange(AndroidAutofillManager* manager,
                                 const FormData& form,
                                 const FormFieldData& field,
                                 const gfx::RectF& bounding_box) override;
-  void OnFormSubmitted(AutofillHandlerProxy* handler,
+  void OnFormSubmitted(AndroidAutofillManager* manager,
                        const FormData& form,
                        bool known_success,
                        mojom::SubmissionSource source) override;
-  void OnFocusNoLongerOnForm(AutofillHandlerProxy* handler,
+  void OnFocusNoLongerOnForm(AndroidAutofillManager* manager,
                              bool had_interacted_form) override;
-  void OnFocusOnFormField(AutofillHandlerProxy* handler,
+  void OnFocusOnFormField(AndroidAutofillManager* manager,
                           const FormData& form,
                           const FormFieldData& field,
                           const gfx::RectF& bounding_box) override;
-  void OnDidFillAutofillFormData(AutofillHandlerProxy* handler,
+  void OnDidFillAutofillFormData(AndroidAutofillManager* manager,
                                  const FormData& form,
                                  base::TimeTicks timestamp) override;
-  void OnFormsSeen(AutofillHandlerProxy* handler,
+  void OnFormsSeen(AndroidAutofillManager* manager,
                    const std::vector<FormData>& forms) override;
-  void OnHidePopup(AutofillHandlerProxy* handler) override;
-  void OnServerPredictionsAvailable(AutofillHandlerProxy* handler) override;
-  void OnServerQueryRequestError(AutofillHandlerProxy* handler,
+  void OnHidePopup(AndroidAutofillManager* manager) override;
+  void OnServerPredictionsAvailable(AndroidAutofillManager* manager) override;
+  void OnServerQueryRequestError(AndroidAutofillManager* manager,
                                  FormSignature form_signature) override;
 
-  void Reset(AutofillHandlerProxy* handler) override;
+  void Reset(AndroidAutofillManager* manager) override;
 
   // Methods called by Java.
   void OnAutofillAvailable(JNIEnv* env, jobject jcaller, jobject form_data);
@@ -92,20 +92,20 @@
   void OnFocusChanged(bool focus_on_form,
                       size_t index,
                       const gfx::RectF& bounding_box);
-  void FireFormFieldDidChanged(AutofillHandlerProxy* handler,
+  void FireFormFieldDidChanged(AndroidAutofillManager* manager,
                                const FormData& form,
                                const FormFieldData& field,
                                const gfx::RectF& bounding_box);
 
-  bool IsCurrentlyLinkedHandler(AutofillHandlerProxy* handler);
+  bool IsCurrentlyLinkedManager(AndroidAutofillManager* manager);
 
   bool IsCurrentlyLinkedForm(const FormData& form);
 
   gfx::RectF ToClientAreaBound(const gfx::RectF& bounding_box);
 
-  // Starts a new session, but only if |form| or |handler| doesn't match the
+  // Starts a new session, but only if |form| or |manager| doesn't match the
   // current session.
-  void MaybeStartNewSession(AutofillHandlerProxy* handler,
+  void MaybeStartNewSession(AndroidAutofillManager* manager,
                             const FormData& form,
                             const FormFieldData& field,
                             const gfx::RectF& bounding_box);
@@ -115,7 +115,7 @@
   int32_t id_;
   std::unique_ptr<FormDataAndroid> form_;
   FieldGlobalId field_id_;
-  base::WeakPtr<AutofillHandlerProxy> handler_;
+  base::WeakPtr<AndroidAutofillManager> manager_;
   JavaObjectWeakGlobalRef java_ref_;
   content::WebContents* web_contents_;
   bool check_submission_;
diff --git a/components/autofill/content/browser/content_autofill_driver.cc b/components/autofill/content/browser/content_autofill_driver.cc
index 9f59e5a..3b4e38c 100644
--- a/components/autofill/content/browser/content_autofill_driver.cc
+++ b/components/autofill/content/browser/content_autofill_driver.cc
@@ -12,8 +12,8 @@
 #include "base/metrics/histogram_macros.h"
 #include "build/build_config.h"
 #include "components/autofill/content/browser/content_autofill_driver_factory.h"
+#include "components/autofill/core/browser/android_autofill_manager.h"
 #include "components/autofill/core/browser/autofill_client.h"
-#include "components/autofill/core/browser/autofill_handler_proxy.h"
 #include "components/autofill/core/browser/browser_autofill_manager.h"
 #include "components/autofill/core/browser/form_structure.h"
 #include "components/autofill/core/browser/payments/payments_service_url.h"
@@ -432,7 +432,7 @@
     AutofillProvider* provider,
     AutofillClient* client,
     AutofillHandler::AutofillDownloadManagerState enable_download_manager) {
-  autofill_handler_ = std::make_unique<AutofillHandlerProxy>(
+  autofill_handler_ = std::make_unique<AndroidAutofillManager>(
       this, client, provider, enable_download_manager);
   GetAutofillAgent()->SetUserGestureRequired(false);
   GetAutofillAgent()->SetSecureContextRequired(true);
diff --git a/components/autofill/core/browser/BUILD.gn b/components/autofill/core/browser/BUILD.gn
index 8f087f2e..ad04261 100644
--- a/components/autofill/core/browser/BUILD.gn
+++ b/components/autofill/core/browser/BUILD.gn
@@ -40,6 +40,8 @@
     "address_profile_save_manager.h",
     "address_rewriter.cc",
     "address_rewriter.h",
+    "android_autofill_manager.cc",
+    "android_autofill_manager.h",
     "autocomplete_history_manager.cc",
     "autocomplete_history_manager.h",
     "autofill_address_policy_handler.cc",
@@ -67,8 +69,6 @@
     "autofill_field.h",
     "autofill_handler.cc",
     "autofill_handler.h",
-    "autofill_handler_proxy.cc",
-    "autofill_handler_proxy.h",
     "autofill_metrics.cc",
     "autofill_metrics.h",
     "autofill_observer.cc",
diff --git a/components/autofill/core/browser/address_rewriter.cc b/components/autofill/core/browser/address_rewriter.cc
index 5f53bc4c..cfe54dd 100644
--- a/components/autofill/core/browser/address_rewriter.cc
+++ b/components/autofill/core/browser/address_rewriter.cc
@@ -10,6 +10,7 @@
 #include "base/i18n/case_conversion.h"
 #include "base/memory/singleton.h"
 #include "base/strings/strcat.h"
+#include "base/strings/string_piece.h"
 #include "base/strings/utf_string_conversions.h"
 #include "components/autofill/core/browser/grit/autofill_address_rewriter_resources_map.h"
 #include "third_party/re2/src/re2/re2.h"
@@ -68,7 +69,7 @@
     data.remove_prefix(token_end + 1);
 
     token_end = data.find('\n');
-    std::string rewrite_string = data.substr(0, token_end).as_string();
+    std::string rewrite_string(data.substr(0, token_end));
     compiled_rules->emplace_back(std::move(pattern), std::move(rewrite_string));
     data.remove_prefix(token_end + 1);
   }
diff --git a/components/autofill/core/browser/autofill_handler_proxy.cc b/components/autofill/core/browser/android_autofill_manager.cc
similarity index 70%
rename from components/autofill/core/browser/autofill_handler_proxy.cc
rename to components/autofill/core/browser/android_autofill_manager.cc
index 8698501..ffbb2e0f 100644
--- a/components/autofill/core/browser/autofill_handler_proxy.cc
+++ b/components/autofill/core/browser/android_autofill_manager.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_handler_proxy.h"
+#include "components/autofill/core/browser/android_autofill_manager.h"
 
 #include "components/autofill/core/browser/autofill_provider.h"
 
@@ -10,7 +10,7 @@
 
 using base::TimeTicks;
 
-AutofillHandlerProxy::AutofillHandlerProxy(
+AndroidAutofillManager::AndroidAutofillManager(
     AutofillDriver* driver,
     AutofillClient* client,
     AutofillProvider* provider,
@@ -21,15 +21,16 @@
                       version_info::Channel::UNKNOWN),
       provider_(provider) {}
 
-AutofillHandlerProxy::~AutofillHandlerProxy() {}
+AndroidAutofillManager::~AndroidAutofillManager() {}
 
-void AutofillHandlerProxy::OnFormSubmittedImpl(const FormData& form,
-                                               bool known_success,
-                                               mojom::SubmissionSource source) {
+void AndroidAutofillManager::OnFormSubmittedImpl(
+    const FormData& form,
+    bool known_success,
+    mojom::SubmissionSource source) {
   provider_->OnFormSubmitted(this, form, known_success, source);
 }
 
-void AutofillHandlerProxy::OnTextFieldDidChangeImpl(
+void AndroidAutofillManager::OnTextFieldDidChangeImpl(
     const FormData& form,
     const FormFieldData& field,
     const gfx::RectF& bounding_box,
@@ -37,14 +38,14 @@
   provider_->OnTextFieldDidChange(this, form, field, bounding_box, timestamp);
 }
 
-void AutofillHandlerProxy::OnTextFieldDidScrollImpl(
+void AndroidAutofillManager::OnTextFieldDidScrollImpl(
     const FormData& form,
     const FormFieldData& field,
     const gfx::RectF& bounding_box) {
   provider_->OnTextFieldDidScroll(this, form, field, bounding_box);
 }
 
-void AutofillHandlerProxy::OnQueryFormFieldAutofillImpl(
+void AndroidAutofillManager::OnQueryFormFieldAutofillImpl(
     int query_id,
     const FormData& form,
     const FormFieldData& field,
@@ -54,21 +55,21 @@
                                       autoselect_first_suggestion);
 }
 
-void AutofillHandlerProxy::OnFocusOnFormFieldImpl(
+void AndroidAutofillManager::OnFocusOnFormFieldImpl(
     const FormData& form,
     const FormFieldData& field,
     const gfx::RectF& bounding_box) {
   provider_->OnFocusOnFormField(this, form, field, bounding_box);
 }
 
-void AutofillHandlerProxy::OnSelectControlDidChangeImpl(
+void AndroidAutofillManager::OnSelectControlDidChangeImpl(
     const FormData& form,
     const FormFieldData& field,
     const gfx::RectF& bounding_box) {
   provider_->OnSelectControlDidChange(this, form, field, bounding_box);
 }
 
-bool AutofillHandlerProxy::ShouldParseForms(
+bool AndroidAutofillManager::ShouldParseForms(
     const std::vector<FormData>& forms) {
   provider_->OnFormsSeen(this, forms);
   // Need to parse the |forms| to FormStructure, so heuristic_type can be
@@ -76,37 +77,38 @@
   return true;
 }
 
-void AutofillHandlerProxy::OnFocusNoLongerOnForm(bool had_interacted_form) {
+void AndroidAutofillManager::OnFocusNoLongerOnForm(bool had_interacted_form) {
   provider_->OnFocusNoLongerOnForm(this, had_interacted_form);
 }
 
-void AutofillHandlerProxy::OnDidFillAutofillFormData(
+void AndroidAutofillManager::OnDidFillAutofillFormData(
     const FormData& form,
     const base::TimeTicks timestamp) {
   provider_->OnDidFillAutofillFormData(this, form, timestamp);
 }
 
-void AutofillHandlerProxy::OnHidePopup() {
+void AndroidAutofillManager::OnHidePopup() {
   provider_->OnHidePopup(this);
 }
 
-void AutofillHandlerProxy::SelectFieldOptionsDidChange(const FormData& form) {}
+void AndroidAutofillManager::SelectFieldOptionsDidChange(const FormData& form) {
+}
 
-void AutofillHandlerProxy::PropagateAutofillPredictions(
+void AndroidAutofillManager::PropagateAutofillPredictions(
     content::RenderFrameHost* rfh,
     const std::vector<FormStructure*>& forms) {
   has_server_prediction_ = true;
   provider_->OnServerPredictionsAvailable(this);
 }
 
-void AutofillHandlerProxy::OnServerRequestError(
+void AndroidAutofillManager::OnServerRequestError(
     FormSignature form_signature,
     AutofillDownloadManager::RequestType request_type,
     int http_error) {
   provider_->OnServerQueryRequestError(this, form_signature);
 }
 
-void AutofillHandlerProxy::Reset() {
+void AndroidAutofillManager::Reset() {
   AutofillHandler::Reset();
   has_server_prediction_ = false;
   provider_->Reset(this);
diff --git a/components/autofill/core/browser/autofill_handler_proxy.h b/components/autofill/core/browser/android_autofill_manager.h
similarity index 86%
rename from components/autofill/core/browser/autofill_handler_proxy.h
rename to components/autofill/core/browser/android_autofill_manager.h
index 9a41754..470bb65 100644
--- a/components/autofill/core/browser/autofill_handler_proxy.h
+++ b/components/autofill/core/browser/android_autofill_manager.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_HANDLER_PROXY_H_
-#define COMPONENTS_AUTOFILL_CORE_BROWSER_AUTOFILL_HANDLER_PROXY_H_
+#ifndef COMPONENTS_AUTOFILL_CORE_BROWSER_ANDROID_AUTOFILL_MANAGER_H_
+#define COMPONENTS_AUTOFILL_CORE_BROWSER_ANDROID_AUTOFILL_MANAGER_H_
 
 #include "base/memory/weak_ptr.h"
 #include "components/autofill/core/browser/autofill_handler.h"
@@ -14,14 +14,14 @@
 class AutofillProvider;
 
 // This class forwards AutofillHandler calls to AutofillProvider.
-class AutofillHandlerProxy : public AutofillHandler {
+class AndroidAutofillManager : public AutofillHandler {
  public:
-  AutofillHandlerProxy(
+  AndroidAutofillManager(
       AutofillDriver* driver,
       AutofillClient* client,
       AutofillProvider* provider,
       AutofillHandler::AutofillDownloadManagerState enable_download_manager);
-  ~AutofillHandlerProxy() override;
+  ~AndroidAutofillManager() override;
 
   void OnFocusNoLongerOnForm(bool had_interacted_form) override;
 
@@ -35,7 +35,7 @@
 
   void Reset() override;
 
-  base::WeakPtr<AutofillHandlerProxy> GetWeakPtr() {
+  base::WeakPtr<AndroidAutofillManager> GetWeakPtr() {
     return weak_ptr_factory_.GetWeakPtr();
   }
 
@@ -90,11 +90,11 @@
  private:
   bool has_server_prediction_ = false;
   AutofillProvider* provider_;
-  base::WeakPtrFactory<AutofillHandlerProxy> weak_ptr_factory_{this};
+  base::WeakPtrFactory<AndroidAutofillManager> weak_ptr_factory_{this};
 
-  DISALLOW_COPY_AND_ASSIGN(AutofillHandlerProxy);
+  DISALLOW_COPY_AND_ASSIGN(AndroidAutofillManager);
 };
 
 }  // namespace autofill
 
-#endif  // COMPONENTS_AUTOFILL_CORE_BROWSER_AUTOFILL_HANDLER_PROXY_H_
+#endif  // COMPONENTS_AUTOFILL_CORE_BROWSER_ANDROID_AUTOFILL_MANAGER_H_
diff --git a/components/autofill/core/browser/autofill_address_util.cc b/components/autofill/core/browser/autofill_address_util.cc
index 6399904..8029e27 100644
--- a/components/autofill/core/browser/autofill_address_util.cc
+++ b/components/autofill/core/browser/autofill_address_util.cc
@@ -17,6 +17,7 @@
 #include "components/autofill/core/browser/geo/autofill_country.h"
 #include "components/autofill/core/browser/ui/country_combobox_model.h"
 #include "components/autofill/core/common/autofill_features.h"
+#include "components/strings/grit/components_strings.h"
 #include "third_party/libaddressinput/src/cpp/include/libaddressinput/address_ui.h"
 #include "third_party/libaddressinput/src/cpp/include/libaddressinput/address_ui_component.h"
 #include "third_party/libaddressinput/src/cpp/include/libaddressinput/localization.h"
@@ -154,4 +155,57 @@
   return base::UTF8ToUTF16(address);
 }
 
+std::u16string GetDescriptionForProfileToSave(
+    const AutofillProfile& profile,
+    const std::string& ui_language_code) {
+  // All user-visible fields.
+  static constexpr ServerFieldType kDetailsFields[] = {
+      NAME_FULL,
+      ADDRESS_HOME_LINE1,
+      ADDRESS_HOME_LINE2,
+      ADDRESS_HOME_DEPENDENT_LOCALITY,
+      ADDRESS_HOME_CITY,
+      ADDRESS_HOME_STATE,
+      ADDRESS_HOME_ZIP,
+      EMAIL_ADDRESS,
+      PHONE_HOME_WHOLE_NUMBER,
+      COMPANY_NAME,
+      ADDRESS_HOME_COUNTRY};
+
+  return profile.ConstructInferredLabel(
+      kDetailsFields, base::size(kDetailsFields),
+      /*num_fields_to_include=*/2, ui_language_code);
+}
+
+std::u16string GetDescriptionForProfileToUpdate(
+    const AutofillProfile& profile,
+    const std::string& ui_language_code) {
+  // All user-visible fields, except for NAME_FULL, which is used as a preceding
+  // label.
+  static constexpr ServerFieldType kDetailsFields[] = {
+      ADDRESS_HOME_LINE1,
+      ADDRESS_HOME_LINE2,
+      ADDRESS_HOME_DEPENDENT_LOCALITY,
+      ADDRESS_HOME_CITY,
+      ADDRESS_HOME_STATE,
+      ADDRESS_HOME_ZIP,
+      EMAIL_ADDRESS,
+      PHONE_HOME_WHOLE_NUMBER,
+      COMPANY_NAME,
+      ADDRESS_HOME_COUNTRY};
+
+  std::vector<std::u16string> description_components;
+  std::u16string label = profile.GetInfo(NAME_FULL, ui_language_code);
+  if (!label.empty())
+    description_components.push_back(label);
+  std::u16string details = profile.ConstructInferredLabel(
+      kDetailsFields, base::size(kDetailsFields),
+      /*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
+  // string.
+  return base::JoinString(description_components, u" — ");
+}
+
 }  // namespace autofill
diff --git a/components/autofill/core/browser/autofill_address_util.h b/components/autofill/core/browser/autofill_address_util.h
index 35a91e216..83a4e0149 100644
--- a/components/autofill/core/browser/autofill_address_util.h
+++ b/components/autofill/core/browser/autofill_address_util.h
@@ -43,6 +43,21 @@
                                        const std::string& ui_language_code,
                                        bool include_country);
 
+// Returns a one-line `profile` description, listing (at max) 2 significant
+// user-visible fields with respect to UI BCP 47 language code in
+// `ui_language_code`.
+std::u16string GetDescriptionForProfileToSave(
+    const AutofillProfile& profile,
+    const std::string& ui_language_code);
+
+// Returns a one-line `profile` description with respect to UI BCP 47
+// language code in `ui_language_code`. It consists of (at max) 2 user-visible
+// fields: the label referring to the profile (full name if exists) and other
+// details, separated by hyphen.
+std::u16string GetDescriptionForProfileToUpdate(
+    const AutofillProfile& profile,
+    const std::string& ui_language_code);
+
 }  // namespace autofill
 
 #endif  // COMPONENTS_AUTOFILL_CORE_BROWSER_AUTOFILL_ADDRESS_UTIL_H_
diff --git a/components/autofill/core/browser/autofill_address_util_unittest.cc b/components/autofill/core/browser/autofill_address_util_unittest.cc
index a2ff8bb..7bd6ead2 100644
--- a/components/autofill/core/browser/autofill_address_util_unittest.cc
+++ b/components/autofill/core/browser/autofill_address_util_unittest.cc
@@ -109,4 +109,28 @@
   EXPECT_EQ(address.find(u"  "), std::string::npos);
 }
 
+TEST(GetDescriptionForProfileToSave, NameAndAddress) {
+  AutofillProfile profile = test::GetFullProfile();
+  std::u16string description = GetDescriptionForProfileToSave(profile, "en-US");
+  // Should contain full name and address line 1.
+  EXPECT_EQ(description, u"John H. Doe, 666 Erebus St.");
+}
+
+TEST(GetDescriptionForProfileToUpdate, LabelAndDetails) {
+  AutofillProfile profile = test::GetFullProfile();
+  std::u16string description =
+      GetDescriptionForProfileToUpdate(profile, "en-US");
+  // Should contain full name as label and address line 1, separated by hyphen.
+  EXPECT_EQ(description, u"John H. Doe — 666 Erebus St.");
+}
+
+TEST(GetDescriptionForProfileToUpdate, NoLabelOnlyDetails) {
+  AutofillProfile profile = test::GetFullProfile();
+  profile.SetInfo(NAME_FULL, u"", "en-US");
+  std::u16string description =
+      GetDescriptionForProfileToUpdate(profile, "en-US");
+  // Should contain no label, but 2 components: address lines 1 & 2.
+  EXPECT_EQ(description, u"666 Erebus St., Apt 8");
+}
+
 }  // namespace autofill
diff --git a/components/autofill/core/browser/autofill_provider.cc b/components/autofill/core/browser/autofill_provider.cc
index 7c87efa..bf99be3 100644
--- a/components/autofill/core/browser/autofill_provider.cc
+++ b/components/autofill/core/browser/autofill_provider.cc
@@ -4,7 +4,7 @@
 
 #include "components/autofill/core/browser/autofill_provider.h"
 
-#include "components/autofill/core/browser/autofill_handler_proxy.h"
+#include "components/autofill/core/browser/android_autofill_manager.h"
 
 namespace autofill {
 namespace {
@@ -24,18 +24,18 @@
 
 AutofillProvider::~AutofillProvider() {}
 
-void AutofillProvider::SendFormDataToRenderer(AutofillHandlerProxy* handler,
+void AutofillProvider::SendFormDataToRenderer(AndroidAutofillManager* manager,
                                               int requestId,
                                               const FormData& formData) {
-  handler->SendFormDataToRenderer(
+  manager->SendFormDataToRenderer(
       requestId, AutofillDriver::FORM_DATA_ACTION_FILL, formData);
 }
 
 void AutofillProvider::RendererShouldAcceptDataListSuggestion(
-    AutofillHandlerProxy* handler,
+    AndroidAutofillManager* manager,
     const FieldGlobalId& field_id,
     const std::u16string& value) {
-  handler->driver()->RendererShouldAcceptDataListSuggestion(field_id, value);
+  manager->driver()->RendererShouldAcceptDataListSuggestion(field_id, value);
 }
 
 }  // namespace autofill
diff --git a/components/autofill/core/browser/autofill_provider.h b/components/autofill/core/browser/autofill_provider.h
index e36889e..991d41ab 100644
--- a/components/autofill/core/browser/autofill_provider.h
+++ b/components/autofill/core/browser/autofill_provider.h
@@ -16,7 +16,7 @@
 
 namespace autofill {
 
-class AutofillHandlerProxy;
+class AndroidAutofillManager;
 
 // This class defines the interface for the autofill implementation other than
 // default BrowserAutofillManager.
@@ -28,65 +28,66 @@
   static bool is_download_manager_disabled_for_testing();
   static void set_is_download_manager_disabled_for_testing();
 
-  virtual void OnQueryFormFieldAutofill(AutofillHandlerProxy* handler,
+  virtual void OnQueryFormFieldAutofill(AndroidAutofillManager* manager,
                                         int32_t id,
                                         const FormData& form,
                                         const FormFieldData& field,
                                         const gfx::RectF& bounding_box,
                                         bool autoselect_first_suggestion) = 0;
 
-  virtual void OnTextFieldDidChange(AutofillHandlerProxy* handler,
+  virtual void OnTextFieldDidChange(AndroidAutofillManager* manager,
                                     const FormData& form,
                                     const FormFieldData& field,
                                     const gfx::RectF& bounding_box,
                                     const base::TimeTicks timestamp) = 0;
 
-  virtual void OnTextFieldDidScroll(AutofillHandlerProxy* handler,
+  virtual void OnTextFieldDidScroll(AndroidAutofillManager* manager,
                                     const FormData& form,
                                     const FormFieldData& field,
                                     const gfx::RectF& bounding_box) = 0;
 
-  virtual void OnSelectControlDidChange(AutofillHandlerProxy* handler,
+  virtual void OnSelectControlDidChange(AndroidAutofillManager* manager,
                                         const FormData& form,
                                         const FormFieldData& field,
                                         const gfx::RectF& bounding_box) = 0;
 
-  virtual void OnFormSubmitted(AutofillHandlerProxy* handler,
+  virtual void OnFormSubmitted(AndroidAutofillManager* manager,
                                const FormData& form,
                                bool known_success,
                                mojom::SubmissionSource source) = 0;
 
-  virtual void OnFocusNoLongerOnForm(AutofillHandlerProxy* handler,
+  virtual void OnFocusNoLongerOnForm(AndroidAutofillManager* manager,
                                      bool had_interacted_form) = 0;
 
-  virtual void OnFocusOnFormField(AutofillHandlerProxy* handler,
+  virtual void OnFocusOnFormField(AndroidAutofillManager* manager,
                                   const FormData& form,
                                   const FormFieldData& field,
                                   const gfx::RectF& bounding_box) = 0;
 
-  virtual void OnDidFillAutofillFormData(AutofillHandlerProxy* handler,
+  virtual void OnDidFillAutofillFormData(AndroidAutofillManager* manager,
                                          const FormData& form,
                                          base::TimeTicks timestamp) = 0;
 
-  virtual void OnFormsSeen(AutofillHandlerProxy* handler,
+  virtual void OnFormsSeen(AndroidAutofillManager* manager,
                            const std::vector<FormData>& forms) = 0;
 
-  virtual void OnHidePopup(AutofillHandlerProxy* handler) = 0;
+  virtual void OnHidePopup(AndroidAutofillManager* manager) = 0;
 
-  virtual void OnServerPredictionsAvailable(AutofillHandlerProxy* handler) = 0;
+  virtual void OnServerPredictionsAvailable(
+      AndroidAutofillManager* manager) = 0;
 
-  virtual void OnServerQueryRequestError(AutofillHandlerProxy* handler,
+  virtual void OnServerQueryRequestError(AndroidAutofillManager* manager,
                                          FormSignature form_signature) = 0;
 
-  virtual void Reset(AutofillHandlerProxy* handler) = 0;
+  virtual void Reset(AndroidAutofillManager* manager) = 0;
 
-  void SendFormDataToRenderer(AutofillHandlerProxy* handler,
+  void SendFormDataToRenderer(AndroidAutofillManager* manager,
                               int requestId,
                               const FormData& formData);
 
   // Notifies the renderer should accept the datalist suggestion given by
   // |value| and fill the input field indified by |field_id|.
-  void RendererShouldAcceptDataListSuggestion(AutofillHandlerProxy* handler,
+  void RendererShouldAcceptDataListSuggestion(AndroidAutofillManager* manager,
                                               const FieldGlobalId& field_id,
                                               const std::u16string& value);
 };
diff --git a/components/autofill/core/browser/autofill_provider_unittest.cc b/components/autofill/core/browser/autofill_provider_unittest.cc
index 567d391e..6486aa4 100644
--- a/components/autofill/core/browser/autofill_provider_unittest.cc
+++ b/components/autofill/core/browser/autofill_provider_unittest.cc
@@ -2,19 +2,19 @@
 // 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_handler_proxy.h"
+#include "components/autofill/core/browser/android_autofill_manager.h"
 #include "components/autofill/core/browser/test_autofill_provider.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace autofill {
 
-class AutofillHandlerProxyTestHelper : public AutofillHandlerProxy {
+class AndroidAutofillManagerTestHelper : public AndroidAutofillManager {
  public:
-  explicit AutofillHandlerProxyTestHelper(AutofillProvider* autofill_provider)
-      : AutofillHandlerProxy(nullptr,
-                             nullptr,
-                             autofill_provider,
-                             DISABLE_AUTOFILL_DOWNLOAD_MANAGER) {}
+  explicit AndroidAutofillManagerTestHelper(AutofillProvider* autofill_provider)
+      : AndroidAutofillManager(nullptr,
+                               nullptr,
+                               autofill_provider,
+                               DISABLE_AUTOFILL_DOWNLOAD_MANAGER) {}
 
   void SimulatePropagateAutofillPredictions() {
     PropagateAutofillPredictions(nullptr, std::vector<FormStructure*>());
@@ -28,22 +28,22 @@
 
 class AutofillProviderTestHelper : public TestAutofillProvider {
  public:
-  bool HasServerPrediction() const { return handler_->has_server_prediction(); }
+  bool HasServerPrediction() const { return manager_->has_server_prediction(); }
 
  private:
   // AutofillProvider
-  void OnQueryFormFieldAutofill(AutofillHandlerProxy* handler,
+  void OnQueryFormFieldAutofill(AndroidAutofillManager* manager,
                                 int32_t id,
                                 const FormData& form,
                                 const FormFieldData& field,
                                 const gfx::RectF& bounding_box,
                                 bool autoselect_first_suggestion) override {
-    handler_ = handler;
+    manager_ = manager;
   }
-  void OnServerQueryRequestError(AutofillHandlerProxy* handler,
+  void OnServerQueryRequestError(AndroidAutofillManager* manager,
                                  FormSignature form_signature) override {}
 
-  AutofillHandlerProxy* handler_;
+  AndroidAutofillManager* manager_;
 };
 
 class AutofillProviderTest : public testing::Test {
@@ -51,8 +51,8 @@
   void SetUp() override {
     autofill_provider_test_helper_ =
         std::make_unique<AutofillProviderTestHelper>();
-    autofill_handler_proxy_test_helper_ =
-        std::make_unique<AutofillHandlerProxyTestHelper>(
+    android_autofill_manager_test_helper_ =
+        std::make_unique<AndroidAutofillManagerTestHelper>(
             autofill_provider_test_helper_.get());
   }
 
@@ -60,32 +60,36 @@
     return autofill_provider_test_helper_.get();
   }
 
-  AutofillHandlerProxyTestHelper* autofill_handler_proxy_test_helper() {
-    return autofill_handler_proxy_test_helper_.get();
+  AndroidAutofillManagerTestHelper* android_autofill_manager_test_helper() {
+    return android_autofill_manager_test_helper_.get();
   }
 
  private:
   std::unique_ptr<AutofillProviderTestHelper> autofill_provider_test_helper_;
-  std::unique_ptr<AutofillHandlerProxyTestHelper>
-      autofill_handler_proxy_test_helper_;
+  std::unique_ptr<AndroidAutofillManagerTestHelper>
+      android_autofill_manager_test_helper_;
 };
 
 TEST_F(AutofillProviderTest, HasServerPredictionAfterQuery) {
   // Simulate the result arrives after starting autofill.
-  autofill_handler_proxy_test_helper()->SimulateOnQueryFormFieldAutofillImpl();
+  android_autofill_manager_test_helper()
+      ->SimulateOnQueryFormFieldAutofillImpl();
   EXPECT_FALSE(autofill_provider_test_helper()->HasServerPrediction());
-  autofill_handler_proxy_test_helper()->SimulatePropagateAutofillPredictions();
+  android_autofill_manager_test_helper()
+      ->SimulatePropagateAutofillPredictions();
   EXPECT_TRUE(autofill_provider_test_helper()->HasServerPrediction());
-  autofill_handler_proxy_test_helper()->Reset();
+  android_autofill_manager_test_helper()->Reset();
   EXPECT_FALSE(autofill_provider_test_helper()->HasServerPrediction());
 }
 
 TEST_F(AutofillProviderTest, HasServerPredictionBeforeQuery) {
   // Simulate the result arrives before starting autofill.
-  autofill_handler_proxy_test_helper()->SimulatePropagateAutofillPredictions();
-  autofill_handler_proxy_test_helper()->SimulateOnQueryFormFieldAutofillImpl();
+  android_autofill_manager_test_helper()
+      ->SimulatePropagateAutofillPredictions();
+  android_autofill_manager_test_helper()
+      ->SimulateOnQueryFormFieldAutofillImpl();
   EXPECT_TRUE(autofill_provider_test_helper()->HasServerPrediction());
-  autofill_handler_proxy_test_helper()->Reset();
+  android_autofill_manager_test_helper()->Reset();
   EXPECT_FALSE(autofill_provider_test_helper()->HasServerPrediction());
 }
 
diff --git a/components/autofill/core/browser/autofill_type.cc b/components/autofill/core/browser/autofill_type.cc
index 9b583bf8..6647aa9 100644
--- a/components/autofill/core/browser/autofill_type.cc
+++ b/components/autofill/core/browser/autofill_type.cc
@@ -477,12 +477,12 @@
   if (server_type_ != UNKNOWN_TYPE)
     return ServerFieldTypeToString(server_type_);
 
-  return FieldTypeToStringPiece(html_type_).as_string();
+  return std::string(FieldTypeToStringPiece(html_type_));
 }
 
 // static
 std::string AutofillType::ServerFieldTypeToString(ServerFieldType type) {
-  return FieldTypeToStringPiece(type).as_string();
+  return std::string(FieldTypeToStringPiece(type));
 }
 
 }  // namespace autofill
diff --git a/components/autofill/core/browser/data_model/credit_card.cc b/components/autofill/core/browser/data_model/credit_card.cc
index e728117..a1b3bd8 100644
--- a/components/autofill/core/browser/data_model/credit_card.cc
+++ b/components/autofill/core/browser/data_model/credit_card.cc
@@ -331,7 +331,7 @@
 
 void CreditCard::SetNetworkForMaskedCard(base::StringPiece network) {
   DCHECK_EQ(MASKED_SERVER_CARD, record_type());
-  network_ = network.as_string();
+  network_ = std::string(network);
 }
 
 void CreditCard::SetServerStatus(ServerStatus status) {
diff --git a/components/autofill/core/browser/randomized_encoder_unittest.cc b/components/autofill/core/browser/randomized_encoder_unittest.cc
index 982fe71..0109d9d 100644
--- a/components/autofill/core/browser/randomized_encoder_unittest.cc
+++ b/components/autofill/core/browser/randomized_encoder_unittest.cc
@@ -3,6 +3,7 @@
 // found in the LICENSE file.
 
 #include "components/autofill/core/browser/randomized_encoder.h"
+#include "base/strings/string_piece.h"
 #include "base/strings/stringprintf.h"
 #include "components/autofill/core/common/signatures.h"
 #include "components/unified_consent/pref_names.h"
@@ -33,7 +34,7 @@
                                 size_t bit_offset,
                                 size_t bit_stride) {
   // Encode all of the bits.
-  std::string all_bits = noise.as_string();
+  std::string all_bits(noise);
   size_t value_length = std::min(value.length(), noise.length());
   for (size_t i = 0; i < value_length; ++i) {
     all_bits[i] = (value[i] & coins[i]) | (all_bits[i] & ~coins[i]);
diff --git a/components/autofill/core/browser/test_autofill_provider.h b/components/autofill/core/browser/test_autofill_provider.h
index 1c498e1..f8dd3d40 100644
--- a/components/autofill/core/browser/test_autofill_provider.h
+++ b/components/autofill/core/browser/test_autofill_provider.h
@@ -14,45 +14,45 @@
   ~TestAutofillProvider() override {}
 
   // AutofillProvider:
-  void OnQueryFormFieldAutofill(AutofillHandlerProxy* handler,
+  void OnQueryFormFieldAutofill(AndroidAutofillManager* manager,
                                 int32_t id,
                                 const FormData& form,
                                 const FormFieldData& field,
                                 const gfx::RectF& bounding_box,
                                 bool autoselect_first_suggestion) override {}
-  void OnTextFieldDidChange(AutofillHandlerProxy* handler,
+  void OnTextFieldDidChange(AndroidAutofillManager* manager,
                             const FormData& form,
                             const FormFieldData& field,
                             const gfx::RectF& bounding_box,
                             const base::TimeTicks timestamp) override {}
-  void OnTextFieldDidScroll(AutofillHandlerProxy* handler,
+  void OnTextFieldDidScroll(AndroidAutofillManager* manager,
                             const FormData& form,
                             const FormFieldData& field,
                             const gfx::RectF& bounding_box) override {}
-  void OnSelectControlDidChange(AutofillHandlerProxy* handler,
+  void OnSelectControlDidChange(AndroidAutofillManager* manager,
                                 const FormData& form,
                                 const FormFieldData& field,
                                 const gfx::RectF& bounding_box) override {}
-  void OnFormSubmitted(AutofillHandlerProxy* handler,
+  void OnFormSubmitted(AndroidAutofillManager* manager,
                        const FormData& form,
                        bool known_success,
                        mojom::SubmissionSource source) override {}
-  void OnFocusNoLongerOnForm(AutofillHandlerProxy* handler,
+  void OnFocusNoLongerOnForm(AndroidAutofillManager* manager,
                              bool had_interacted_form) override {}
-  void OnFocusOnFormField(AutofillHandlerProxy* handler,
+  void OnFocusOnFormField(AndroidAutofillManager* manager,
                           const FormData& form,
                           const FormFieldData& field,
                           const gfx::RectF& bounding_box) override {}
-  void OnDidFillAutofillFormData(AutofillHandlerProxy* handler,
+  void OnDidFillAutofillFormData(AndroidAutofillManager* manager,
                                  const FormData& form,
                                  base::TimeTicks timestamp) override {}
-  void OnFormsSeen(AutofillHandlerProxy* handler,
+  void OnFormsSeen(AndroidAutofillManager* manager,
                    const std::vector<FormData>& forms) override {}
-  void OnHidePopup(AutofillHandlerProxy* handler) override {}
-  void OnServerPredictionsAvailable(AutofillHandlerProxy* handler) override {}
-  void OnServerQueryRequestError(AutofillHandlerProxy* handler,
+  void OnHidePopup(AndroidAutofillManager* manager) override {}
+  void OnServerPredictionsAvailable(AndroidAutofillManager* manager) override {}
+  void OnServerQueryRequestError(AndroidAutofillManager* manager,
                                  FormSignature form_signature) override {}
-  void Reset(AutofillHandlerProxy* handler) override {}
+  void Reset(AndroidAutofillManager* manager) override {}
 };
 
 }  // namespace autofill
diff --git a/components/browser_ui/strings/android/browser_ui_strings.grd b/components/browser_ui/strings/android/browser_ui_strings.grd
index 57a516b..8973ab1 100644
--- a/components/browser_ui/strings/android/browser_ui_strings.grd
+++ b/components/browser_ui/strings/android/browser_ui_strings.grd
@@ -598,13 +598,13 @@
       <message name="IDS_MEDIA_CAPTURE_NOTIFICATION_CONTENT_TEXT" desc="Url of the current tab. The notification will display this text for the user to identify the tab to return to.">
         Tap to return to <ph name="URL_OF_THE_CURRENT_TAB">%1$s<ex>https://apprtc.appspot.com</ex></ph>
       </message>
-      <message name="IDS_MEDIA_CAPTURE_NOTIFICATION_CONTENT_TEXT_INCOGNITO" desc="The notification will display this text for the user to return to the incognito tab which has created the notification.">
+      <message name="IDS_MEDIA_CAPTURE_NOTIFICATION_CONTENT_TEXT_INCOGNITO" desc="The notification will display this text for the user to return to the Incognito tab which has created the notification.">
         Tap to return to the site
       </message>
       <message name="IDS_ACCESSIBILITY_STOP" desc="Accessible name for a button that stops playing or recording media.">
         Stop
       </message>
-      <message name="IDS_NOTIFICATION_INCOGNITO_TAB" desc="Text used as notifications source when the notification is from incognito tabs.">
+      <message name="IDS_NOTIFICATION_INCOGNITO_TAB" desc="Text used as notifications source when the notification is from Incognito tabs.">
         Incognito tab
       </message>
       <message name="IDS_NOTIFICATION_CATEGORY_COMPLETED_DOWNLOADS" desc="Label for completed download notifications, within a list of notification categories. [CHAR-LIMIT=32]">
@@ -646,7 +646,7 @@
       <message name="IDS_ACCESSIBILITY_SEEK_BACKWARD" desc="The seek backward button that seeks media to an earlier position.">
         Seek backward
       </message>
-      <message name="IDS_MEDIA_NOTIFICATION_INCOGNITO" desc="Text used as a placeholder for a media notification about playing media, when notification is shown from incognito tab.">
+      <message name="IDS_MEDIA_NOTIFICATION_INCOGNITO" desc="Text used as a placeholder for a media notification about playing media, when notification is shown from Incognito tab.">
         A site is playing media
       </message>
 
@@ -668,17 +668,17 @@
         Options available near bottom of the screen
       </message>
 
-      <!-- Warning on sharing info with external apps in incognito mode -->
-      <message name="IDS_EXTERNAL_APP_LEAVE_INCOGNITO_WARNING" desc="Alert dialog text warning the user (who is currently in incognito mode) that the site they are currently using is going to share data with an external application." formatter_data="android_java">
-        This site is about to share information with an app outside of incognito mode.
+      <!-- Warning on sharing info with external apps in Incognito mode -->
+      <message name="IDS_EXTERNAL_APP_LEAVE_INCOGNITO_WARNING" desc="Alert dialog text warning the user (who is currently in Incognito mode) that the site they are currently using is going to share data with an external application." formatter_data="android_java">
+        This site is about to share information with an app outside of Incognito mode.
       </message>
-      <message name="IDS_EXTERNAL_APP_LEAVE_INCOGNITO_WARNING_TITLE" desc="Title for dialog asking if the user wants to leave incognito mode. [CHAR-LIMIT=32]" formatter_data="android_java">
-        Leave incognito mode?
+      <message name="IDS_EXTERNAL_APP_LEAVE_INCOGNITO_WARNING_TITLE" desc="Title for dialog asking if the user wants to leave Incognito mode. [CHAR-LIMIT=32]" formatter_data="android_java">
+        Leave Incognito mode?
       </message>
-      <message name="IDS_EXTERNAL_APP_LEAVE_INCOGNITO_STAY" desc="Label for the dialog button to stay in incognito mode. [CHAR-LIMIT=20]" formatter_data="android_java">
+      <message name="IDS_EXTERNAL_APP_LEAVE_INCOGNITO_STAY" desc="Label for the dialog button to stay in Incognito mode. [CHAR-LIMIT=20]" formatter_data="android_java">
         Stay
       </message>
-      <message name="IDS_EXTERNAL_APP_LEAVE_INCOGNITO_LEAVE" desc="Label for the dialog button to leave incognito mode. [CHAR-LIMIT=20]" formatter_data="android_java">
+      <message name="IDS_EXTERNAL_APP_LEAVE_INCOGNITO_LEAVE" desc="Label for the dialog button to leave Incognito mode. [CHAR-LIMIT=20]" formatter_data="android_java">
         Leave
       </message>
 
diff --git a/components/browser_ui/strings/android/site_settings.grdp b/components/browser_ui/strings/android/site_settings.grdp
index 2ffa99d..be7fc785 100644
--- a/components/browser_ui/strings/android/site_settings.grdp
+++ b/components/browser_ui/strings/android/site_settings.grdp
@@ -316,7 +316,7 @@
   <message name="IDS_WEBSITE_SETTINGS_CATEGORY_COOKIE_ALLOW_TITLE" desc="Text used to explain the allow cookies option in settings">
     Allow cookies
   </message>
-  <message name="IDS_WEBSITE_SETTINGS_CATEGORY_COOKIE_BLOCK_THIRD_PARTY_INCOGNITO_TITLE" desc="Text used to explain the block third-party cookies in incognito option in settings">
+  <message name="IDS_WEBSITE_SETTINGS_CATEGORY_COOKIE_BLOCK_THIRD_PARTY_INCOGNITO_TITLE" desc="Text used to explain the block third-party cookies in Incognito option in settings">
     Block third-party cookies in Incognito
   </message>
   <message name="IDS_WEBSITE_SETTINGS_CATEGORY_COOKIE_BLOCK_THIRD_PARTY_TITLE" desc="Text used to explain the block third-party cookies option in settings">
diff --git a/components/browser_watcher/extended_crash_reporting.cc b/components/browser_watcher/extended_crash_reporting.cc
index 42eb535..b79ba4f 100644
--- a/components/browser_watcher/extended_crash_reporting.cc
+++ b/components/browser_watcher/extended_crash_reporting.cc
@@ -11,6 +11,7 @@
 #include "base/debug/activity_tracker.h"
 #include "base/memory/ptr_util.h"
 #include "base/metrics/persistent_memory_allocator.h"
+#include "base/strings/string_piece.h"
 #include "base/win/pe_image.h"
 #include "build/build_config.h"
 #include "components/browser_watcher/activity_data_names.h"
@@ -84,7 +85,7 @@
 
   module.file = "chrome.dll";
   module.debug_file =
-      base::StringPiece(pdb_filename, pdb_filename_length).as_string();
+      std::string(base::StringPiece(pdb_filename, pdb_filename_length));
 
   global_tracker->RecordModuleInfo(module);
 }
diff --git a/components/browsing_data/content/local_shared_objects_container.cc b/components/browsing_data/content/local_shared_objects_container.cc
index 5233e8d..75f833e 100644
--- a/components/browsing_data/content/local_shared_objects_container.cc
+++ b/components/browsing_data/content/local_shared_objects_container.cc
@@ -9,6 +9,7 @@
 #include <utility>
 #include <vector>
 
+#include "base/strings/string_piece.h"
 #include "components/browsing_data/content/appcache_helper.h"
 #include "components/browsing_data/content/cache_storage_helper.h"
 #include "components/browsing_data/content/canonical_cookie_hash.h"
@@ -217,7 +218,7 @@
     if (!domain.empty())
       domains.insert(std::move(domain));
     else
-      domains.insert(host.as_string());
+      domains.insert(std::string(host));
   }
   return domains.size();
 }
diff --git a/components/cast/api_bindings/scoped_api_binding.cc b/components/cast/api_bindings/scoped_api_binding.cc
index 30843b3..00400211 100644
--- a/components/cast/api_bindings/scoped_api_binding.cc
+++ b/components/cast/api_bindings/scoped_api_binding.cc
@@ -8,6 +8,7 @@
 
 #include "base/bind.h"
 #include "base/logging.h"
+#include "base/strings/string_piece.h"
 #include "components/cast/api_bindings/manager.h"
 
 namespace cast_api_bindings {
@@ -58,7 +59,7 @@
     return false;
   }
 
-  if (!message_port_->PostMessage(data_utf8.as_string())) {
+  if (!message_port_->PostMessage(data_utf8)) {
     return false;
   }
 
diff --git a/components/cast/message_port/message_port_fuchsia.cc b/components/cast/message_port/message_port_fuchsia.cc
index e8ce09d..4b74e16 100644
--- a/components/cast/message_port/message_port_fuchsia.cc
+++ b/components/cast/message_port/message_port_fuchsia.cc
@@ -7,6 +7,7 @@
 #include "base/fuchsia/fuchsia_logging.h"
 #include "base/memory/weak_ptr.h"
 #include "base/notreached.h"
+#include "base/strings/string_piece.h"
 #include "fuchsia/base/mem_buffer_util.h"
 #include "fuchsia/fidl/chromium/cast/cpp/fidl.h"
 
@@ -242,8 +243,7 @@
     base::StringPiece message,
     std::vector<std::unique_ptr<MessagePort>> ports) {
   fuchsia::web::WebMessage message_fidl;
-  message_fidl.set_data(cr_fuchsia::MemBufferFromString(message.as_string(),
-                                                        message.as_string()));
+  message_fidl.set_data(cr_fuchsia::MemBufferFromString(message, message));
   if (!ports.empty()) {
     PortType expected_port_type = FromMessagePort(ports[0].get())->port_type_;
     std::vector<fuchsia::web::IncomingTransferable> incoming_transferables;
diff --git a/components/cast/message_port/test_message_port_receiver.cc b/components/cast/message_port/test_message_port_receiver.cc
index 6dcacfd..7b6f68df 100644
--- a/components/cast/message_port/test_message_port_receiver.cc
+++ b/components/cast/message_port/test_message_port_receiver.cc
@@ -5,6 +5,7 @@
 #include "components/cast/message_port/test_message_port_receiver.h"
 
 #include "base/run_loop.h"
+#include "base/strings/string_piece.h"
 
 namespace cast_api_bindings {
 
@@ -33,7 +34,7 @@
 bool TestMessagePortReceiver::OnMessage(
     base::StringPiece message,
     std::vector<std::unique_ptr<MessagePort>> ports) {
-  buffer_.push_back(std::make_pair(message.as_string(), std::move(ports)));
+  buffer_.push_back(std::make_pair(std::string(message), std::move(ports)));
   if (message_count_target_ == buffer_.size()) {
     DCHECK(on_receive_satisfied_);
     std::move(on_receive_satisfied_).Run();
diff --git a/components/cast_certificate/cast_cert_validator.cc b/components/cast_certificate/cast_cert_validator.cc
index 17491d1f..3b6a673b 100644
--- a/components/cast_certificate/cast_cert_validator.cc
+++ b/components/cast_certificate/cast_cert_validator.cc
@@ -14,6 +14,7 @@
 #include "base/containers/contains.h"
 #include "base/logging.h"
 #include "base/no_destructor.h"
+#include "base/strings/string_piece.h"
 #include "base/synchronization/lock.h"
 #include "base/task/task_traits.h"
 #include "base/task/thread_pool.h"
@@ -165,7 +166,7 @@
   // Save a copy of the passed in public key (DER) and common name (text).
   CertVerificationContextImpl(const net::der::Input& spki,
                               const base::StringPiece& common_name)
-      : spki_(spki.AsString()), common_name_(common_name.as_string()) {}
+      : spki_(spki.AsString()), common_name_(common_name) {}
 
   bool VerifySignatureOverData(
       const base::StringPiece& signature,
diff --git a/components/cast_channel/cast_message_util.cc b/components/cast_channel/cast_message_util.cc
index d70a070..fe85e15 100644
--- a/components/cast_channel/cast_message_util.cc
+++ b/components/cast_channel/cast_message_util.cc
@@ -10,6 +10,7 @@
 #include "base/logging.h"
 #include "base/stl_util.h"
 #include "base/strings/string_number_conversions.h"
+#include "base/strings/string_piece.h"
 #include "base/strings/string_util.h"
 #include "build/build_config.h"
 #include "build/chromeos_buildflags.h"
@@ -548,8 +549,7 @@
                                    const std::string& source_id) {
   DCHECK(body.FindKeyOfType("type", Value::Type::STRING) &&
          body.FindKeyOfType("type", Value::Type::STRING)->GetString() ==
-             (EnumToString<V2MessageType, V2MessageType::kSetVolume>())
-                 .as_string());
+             (EnumToString<V2MessageType, V2MessageType::kSetVolume>()));
   Value dict = body.Clone();
   dict.RemoveKey("sessionId");
   dict.SetKey("requestId", Value(request_id));
diff --git a/components/cbor/values.cc b/components/cbor/values.cc
index 7329b58..be88f500 100644
--- a/components/cbor/values.cc
+++ b/components/cbor/values.cc
@@ -10,6 +10,7 @@
 #include "base/check_op.h"
 #include "base/notreached.h"
 #include "base/numerics/safe_conversions.h"
+#include "base/strings/string_piece.h"
 #include "base/strings/string_util.h"
 #include "components/cbor/constants.h"
 
@@ -113,7 +114,7 @@
   switch (type_) {
     case Type::STRING:
       new (&string_value_) std::string();
-      string_value_ = in_string.as_string();
+      string_value_ = std::string(in_string);
       DCHECK(base::IsStringUTF8(string_value_));
       break;
     case Type::BYTE_STRING:
diff --git a/components/cdm/common/cdm_manifest.cc b/components/cdm/common/cdm_manifest.cc
index b8e801d..6efbc85e 100644
--- a/components/cdm/common/cdm_manifest.cc
+++ b/components/cdm/common/cdm_manifest.cc
@@ -23,6 +23,7 @@
 #include "media/base/content_decryption_module.h"
 #include "media/base/decrypt_config.h"
 #include "media/base/video_codecs.h"
+#include "media/cdm/cdm_capability.h"
 #include "media/cdm/supported_cdm_versions.h"
 #include "media/media_buildflags.h"
 
@@ -278,7 +279,7 @@
 }
 
 bool ParseCdmManifest(const base::Value& manifest,
-                      content::CdmCapability* capability) {
+                      media::CdmCapability* capability) {
   DCHECK(manifest.is_dict());
 
   return GetCodecs(manifest, &capability->video_codecs) &&
@@ -288,7 +289,7 @@
 
 bool ParseCdmManifestFromPath(const base::FilePath& manifest_path,
                               base::Version* version,
-                              content::CdmCapability* capability) {
+                              media::CdmCapability* capability) {
   JSONFileValueDeserializer deserializer(manifest_path);
   int error_code;
   std::string error_message;
diff --git a/components/cdm/common/cdm_manifest.h b/components/cdm/common/cdm_manifest.h
index bd8f6d8..4e1b2c7 100644
--- a/components/cdm/common/cdm_manifest.h
+++ b/components/cdm/common/cdm_manifest.h
@@ -11,7 +11,7 @@
 class Version;
 }  // namespace base
 
-namespace content {
+namespace media {
 struct CdmCapability;
 }
 
@@ -26,7 +26,7 @@
 // Returns true on success, false if there are errors in the manifest.
 // If this method returns false, |capability| may or may not be updated.
 bool ParseCdmManifest(const base::Value& manifest,
-                      content::CdmCapability* capability);
+                      media::CdmCapability* capability);
 
 // Reads the file |manifest_path| which is assumed to be a CDM manifest and
 // extracts the necessary information from it to update |version| and
@@ -37,6 +37,6 @@
 // |version| and |capability| may or may not be updated.
 bool ParseCdmManifestFromPath(const base::FilePath& manifest_path,
                               base::Version* version,
-                              content::CdmCapability* capability);
+                              media::CdmCapability* capability);
 
 #endif  // COMPONENTS_CDM_COMMON_CDM_MANIFEST_H_
diff --git a/components/cdm/common/cdm_manifest_unittest.cc b/components/cdm/common/cdm_manifest_unittest.cc
index 461513c..4240d569a 100644
--- a/components/cdm/common/cdm_manifest_unittest.cc
+++ b/components/cdm/common/cdm_manifest_unittest.cc
@@ -20,10 +20,11 @@
 #include "base/version.h"
 #include "content/public/common/cdm_info.h"
 #include "media/cdm/api/content_decryption_module.h"
+#include "media/cdm/cdm_capability.h"
 #include "media/cdm/supported_cdm_versions.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
-using content::CdmCapability;
+using media::CdmCapability;
 
 namespace {
 
diff --git a/components/content_settings/core/common/content_settings_pattern.cc b/components/content_settings/core/common/content_settings_pattern.cc
index 1f0afca..9389cdd0 100644
--- a/components/content_settings/core/common/content_settings_pattern.cc
+++ b/components/content_settings/core/common/content_settings_pattern.cc
@@ -412,7 +412,7 @@
     }
     if (local_url->port_piece().empty()) {
       if (local_url->SchemeIs(url::kHttpsScheme))
-        builder.WithPort(GetDefaultPort(url::kHttpsScheme).as_string());
+        builder.WithPort(std::string(GetDefaultPort(url::kHttpsScheme)));
       else
         builder.WithPortWildcard();
     } else {
@@ -435,7 +435,7 @@
   } else {
     builder.WithScheme(local_url->scheme())->WithHost(local_url->host());
     if (local_url->port_piece().empty()) {
-      builder.WithPort(GetDefaultPort(local_url->scheme_piece()).as_string());
+      builder.WithPort(std::string(GetDefaultPort(local_url->scheme_piece())));
     } else {
       builder.WithPort(local_url->port());
     }
diff --git a/components/content_settings/core/common/content_settings_pattern_parser.cc b/components/content_settings/core/common/content_settings_pattern_parser.cc
index 7064c2b..fa27361 100644
--- a/components/content_settings/core/common/content_settings_pattern_parser.cc
+++ b/components/content_settings/core/common/content_settings_pattern_parser.cc
@@ -111,7 +111,7 @@
     if (scheme_piece == kSchemeWildcard) {
       builder->WithSchemeWildcard();
     } else {
-      builder->WithScheme(scheme_piece.as_string());
+      builder->WithScheme(std::string(scheme_piece));
     }
   } else {
     builder->WithSchemeWildcard();
@@ -142,14 +142,14 @@
 
       host_piece.remove_prefix(kDomainWildcardLength);
       builder->WithDomainWildcard();
-      builder->WithHost(host_piece.as_string());
+      builder->WithHost(std::string(host_piece));
     } else {
       // If the host contains a wildcard symbol then it is invalid.
       if (host_piece.find(kHostWildcard) != std::string::npos) {
         builder->Invalid();
         return;
       }
-      builder->WithHost(host_piece.as_string());
+      builder->WithHost(std::string(host_piece));
     }
   }
 
@@ -171,7 +171,7 @@
         }
       }
       // TODO(markusheintz): Check port range.
-      builder->WithPort(port_piece.as_string());
+      builder->WithPort(std::string(port_piece));
     }
   } else {
     if (!ContentSettingsPattern::IsNonWildcardDomainNonPortScheme(
@@ -184,7 +184,7 @@
     if (path_piece.substr(1) == kPathWildcard)
       builder->WithPathWildcard();
     else
-      builder->WithPath(path_piece.as_string());
+      builder->WithPath(std::string(path_piece));
   }
 }
 
diff --git a/components/crash/core/common/crash_key_breakpad.cc b/components/crash/core/common/crash_key_breakpad.cc
index 986530b..bb37248 100644
--- a/components/crash/core/common/crash_key_breakpad.cc
+++ b/components/crash/core/common/crash_key_breakpad.cc
@@ -62,7 +62,7 @@
 
   // If there is only one slot for the value, then handle it directly.
   if (index_array_count_ == 1) {
-    std::string value_string = value.as_string();
+    std::string value_string(value);
     if (is_set()) {
       storage->SetValueAtIndex(index_array_[0], value_string.c_str());
     } else {
diff --git a/components/data_reduction_proxy/core/browser/data_reduction_proxy_request_options.cc b/components/data_reduction_proxy/core/browser/data_reduction_proxy_request_options.cc
index 3d62fac..663512ad 100644
--- a/components/data_reduction_proxy/core/browser/data_reduction_proxy_request_options.cc
+++ b/components/data_reduction_proxy/core/browser/data_reduction_proxy_request_options.cc
@@ -13,6 +13,7 @@
 #include "base/single_thread_task_runner.h"
 #include "base/strings/safe_sprintf.h"
 #include "base/strings/string_number_conversions.h"
+#include "base/strings/string_piece.h"
 #include "base/strings/string_split.h"
 #include "base/strings/string_tokenizer.h"
 #include "base/strings/string_util.h"
@@ -243,8 +244,8 @@
     // comparing.
     if (base::TrimWhitespaceASCII(kv_pair.first, base::TRIM_ALL) ==
         kSecureSessionHeaderOption) {
-      return base::TrimWhitespaceASCII(kv_pair.second, base::TRIM_ALL)
-          .as_string();
+      return std::string(
+          base::TrimWhitespaceASCII(kv_pair.second, base::TRIM_ALL));
     }
   }
   return base::nullopt;
@@ -265,16 +266,14 @@
         kPageIdOption) {
       uint64_t page_id;
       if (base::StringToUint64(
-              base::TrimWhitespaceASCII(kv_pair.second, base::TRIM_ALL)
-                  .as_string(),
+              base::TrimWhitespaceASCII(kv_pair.second, base::TRIM_ALL),
               &page_id)) {
         return page_id;
       }
 
       // Also attempt parsing the page_id as a hex string.
       if (base::HexStringToUInt64(
-              base::TrimWhitespaceASCII(kv_pair.second, base::TRIM_ALL)
-                  .as_string(),
+              base::TrimWhitespaceASCII(kv_pair.second, base::TRIM_ALL),
               &page_id)) {
         return page_id;
       }
diff --git a/components/data_reduction_proxy/core/browser/data_reduction_proxy_settings_test_utils.cc b/components/data_reduction_proxy/core/browser/data_reduction_proxy_settings_test_utils.cc
index e793d1c..0211314 100644
--- a/components/data_reduction_proxy/core/browser/data_reduction_proxy_settings_test_utils.cc
+++ b/components/data_reduction_proxy/core/browser/data_reduction_proxy_settings_test_utils.cc
@@ -3,6 +3,7 @@
 // found in the LICENSE file.
 
 #include "components/data_reduction_proxy/core/browser/data_reduction_proxy_settings_test_utils.h"
+#include "base/strings/string_piece.h"
 
 #include <stdint.h>
 
@@ -129,7 +130,7 @@
 bool DataReductionProxySettingsTestBase::OnSyntheticFieldTrialRegistration(
     base::StringPiece trial_name,
     base::StringPiece group_name) {
-  synthetic_field_trials_[trial_name.as_string()] = group_name.as_string();
+  synthetic_field_trials_[std::string(trial_name)] = std::string(group_name);
   return true;
 }
 
diff --git a/components/data_reduction_proxy/core/browser/data_reduction_proxy_test_utils.cc b/components/data_reduction_proxy/core/browser/data_reduction_proxy_test_utils.cc
index 4d965c2..98b78a5f 100644
--- a/components/data_reduction_proxy/core/browser/data_reduction_proxy_test_utils.cc
+++ b/components/data_reduction_proxy/core/browser/data_reduction_proxy_test_utils.cc
@@ -3,6 +3,7 @@
 // found in the LICENSE file.
 
 #include "components/data_reduction_proxy/core/browser/data_reduction_proxy_test_utils.h"
+#include "base/strings/string_piece.h"
 
 #include <map>
 #include <memory>
@@ -114,7 +115,7 @@
 
 DataStore::Status TestDataStore::Get(base::StringPiece key,
                                      std::string* value) {
-  auto value_iter = map_.find(key.as_string());
+  auto value_iter = map_.find(std::string(key));
   if (value_iter == map_.end())
     return NOT_FOUND;
 
@@ -131,7 +132,7 @@
 }
 
 DataStore::Status TestDataStore::Delete(base::StringPiece key) {
-  map_.erase(key.as_string());
+  map_.erase(std::string(key));
 
   return OK;
 }
diff --git a/components/download/internal/background_service/in_memory_download.cc b/components/download/internal/background_service/in_memory_download.cc
index f5e369f..dd7445d 100644
--- a/components/download/internal/background_service/in_memory_download.cc
+++ b/components/download/internal/background_service/in_memory_download.cc
@@ -8,6 +8,7 @@
 #include <string>
 
 #include "base/bind.h"
+#include "base/strings/string_piece.h"
 #include "components/download/internal/background_service/blob_task_proxy.h"
 #include "net/base/load_flags.h"
 #include "net/traffic_annotation/network_traffic_annotation.h"
@@ -110,8 +111,8 @@
 
 void InMemoryDownloadImpl::OnDataReceived(base::StringPiece string_piece,
                                           base::OnceClosure resume) {
-  size_t size = string_piece.as_string().size();
-  data_.append(string_piece.as_string().data(), size);
+  size_t size = string_piece.size();
+  data_.append(std::string(string_piece).data(), size);
   bytes_downloaded_ += size;
 
   if (paused_) {
diff --git a/components/drive/drive_notification_manager.cc b/components/drive/drive_notification_manager.cc
index f81924b0..2da63a2d 100644
--- a/components/drive/drive_notification_manager.cc
+++ b/components/drive/drive_notification_manager.cc
@@ -13,6 +13,7 @@
 #include "base/rand_util.h"
 #include "base/strings/char_traits.h"
 #include "base/strings/strcat.h"
+#include "base/strings/string_piece.h"
 #include "base/strings/string_util.h"
 #include "components/drive/drive_notification_observer.h"
 #include "components/invalidation/public/invalidation_service.h"
@@ -317,7 +318,7 @@
   if (!base::StartsWith(topic_name, prefix)) {
     return {};
   }
-  return topic_name.substr(prefix.size()).as_string();
+  return std::string(topic_name.substr(prefix.size()));
 }
 
 }  // namespace drive
diff --git a/components/embedder_support/origin_trials/origin_trial_policy_impl.cc b/components/embedder_support/origin_trials/origin_trial_policy_impl.cc
index 4cdb5c3a..b31d092 100644
--- a/components/embedder_support/origin_trials/origin_trial_policy_impl.cc
+++ b/components/embedder_support/origin_trials/origin_trial_policy_impl.cc
@@ -11,6 +11,7 @@
 #include "base/command_line.h"
 #include "base/feature_list.h"
 #include "base/stl_util.h"
+#include "base/strings/string_piece.h"
 #include "base/strings/string_split.h"
 #include "components/embedder_support/origin_trials/features.h"
 #include "components/embedder_support/switches.h"
@@ -66,12 +67,12 @@
 }
 
 bool OriginTrialPolicyImpl::IsFeatureDisabled(base::StringPiece feature) const {
-  return disabled_features_.count(feature.as_string()) > 0;
+  return disabled_features_.count(std::string(feature)) > 0;
 }
 
 bool OriginTrialPolicyImpl::IsTokenDisabled(
     base::StringPiece token_signature) const {
-  return disabled_tokens_.count(token_signature.as_string()) > 0;
+  return disabled_tokens_.count(std::string(token_signature)) > 0;
 }
 
 // Exclude users in Field trial control group from the corresponding origin
diff --git a/components/feature_engagement/internal/chrome_variations_configuration.cc b/components/feature_engagement/internal/chrome_variations_configuration.cc
index 139f72e6..f8f4171 100644
--- a/components/feature_engagement/internal/chrome_variations_configuration.cc
+++ b/components/feature_engagement/internal/chrome_variations_configuration.cc
@@ -150,7 +150,7 @@
       }
       has_name = true;
 
-      event_config->name = value.as_string();
+      event_config->name = std::string(value);
     } else if (base::LowerCaseEqualsASCII(key, kEventConfigDataComparatorKey)) {
       if (has_comparator) {
         *event_config = EventConfig();
@@ -202,7 +202,7 @@
 bool IsKnownFeature(const base::StringPiece& feature_name,
                     const FeatureVector& features) {
   for (const auto* feature : features) {
-    if (feature->name == feature_name.as_string())
+    if (feature->name == feature_name)
       return true;
   }
   return false;
@@ -254,7 +254,7 @@
               FAILURE_SESSION_RATE_IMPACT_UNKNOWN_FEATURE);
       continue;
     }
-    affected_features.push_back(feature_name.as_string());
+    affected_features.push_back(std::string(feature_name));
   }
 
   if (affected_features.empty())
diff --git a/components/feed/core/common/pref_names.cc b/components/feed/core/common/pref_names.cc
index 4317643..020c73a 100644
--- a/components/feed/core/common/pref_names.cc
+++ b/components/feed/core/common/pref_names.cc
@@ -44,6 +44,7 @@
 const char kEnableWebFeedFollowIntroDebug[] =
     "webfeed_follow_intro_debug.enable";
 const char kReliabilityLoggingIdSalt[] = "feedv2.reliability_logging_id_salt";
+const char kIsWebFeedSubscriber[] = "webfeed.is_subscriber";
 
 }  // namespace prefs
 
@@ -104,6 +105,7 @@
   registry->RegisterBooleanPref(feed::prefs::kEnableWebFeedFollowIntroDebug,
                                 false);
   registry->RegisterUint64Pref(feed::prefs::kReliabilityLoggingIdSalt, 0);
+  registry->RegisterBooleanPref(feed::prefs::kIsWebFeedSubscriber, false);
 
 #if defined(OS_IOS)
   registry->RegisterBooleanPref(feed::prefs::kLastFetchHadLoggingEnabled,
diff --git a/components/feed/core/common/pref_names.h b/components/feed/core/common/pref_names.h
index 2396dfc..27737d47 100644
--- a/components/feed/core/common/pref_names.h
+++ b/components/feed/core/common/pref_names.h
@@ -74,6 +74,8 @@
 extern const char kEnableWebFeedFollowIntroDebug[];
 // Random bytes used in generating reliability logging ID.
 extern const char kReliabilityLoggingIdSalt[];
+// Whether the user has subscribed to a web feed.
+extern const char kIsWebFeedSubscriber[];
 
 }  // namespace prefs
 
diff --git a/components/feed/core/v2/api_test/feed_api_subscriptions_unittest.cc b/components/feed/core/v2/api_test/feed_api_subscriptions_unittest.cc
index e7e12d8..d9ddff1 100644
--- a/components/feed/core/v2/api_test/feed_api_subscriptions_unittest.cc
+++ b/components/feed/core/v2/api_test/feed_api_subscriptions_unittest.cc
@@ -787,6 +787,7 @@
       "{ WebFeedMetadata{ id=id_cats title=Title cats "
       "publisher_url=https://cats.com/ status=kSubscribed } }",
       PrintToString(CheckAllSubscriptions()));
+  EXPECT_TRUE(subscriptions().IsWebFeedSubscriber());
 }
 
 TEST_F(FeedApiSubscriptionsTest, SubscribedWebFeedsAreClearedOnSignOut) {
@@ -812,7 +813,8 @@
   stream_->OnSignedOut();
   WaitForIdleTaskQueue();
   ASSERT_EQ(1, network_.GetListFollowedWebFeedsRequestCount());
-  EXPECT_EQ("{}", PrintToString(CheckRecommendedFeeds()));
+  EXPECT_EQ("{}", PrintToString(CheckAllSubscriptions()));
+  EXPECT_FALSE(subscriptions().IsWebFeedSubscriber());
 }
 
 TEST_F(FeedApiSubscriptionsTest,
diff --git a/components/feed/core/v2/feed_stream.cc b/components/feed/core/v2/feed_stream.cc
index c9a5d90..1add17b 100644
--- a/components/feed/core/v2/feed_stream.cc
+++ b/components/feed/core/v2/feed_stream.cc
@@ -143,7 +143,7 @@
   wire_response_translator_ = &default_translator;
 
   web_feed_subscription_coordinator_ =
-      std::make_unique<WebFeedSubscriptionCoordinator>(this);
+      std::make_unique<WebFeedSubscriptionCoordinator>(profile_prefs, this);
   Stream& stream = GetStream(kForYouStream);
   offline_page_spy_ = std::make_unique<OfflinePageSpy>(
       stream.surface_updater.get(), offline_page_model);
diff --git a/components/feed/core/v2/public/web_feed_subscriptions.h b/components/feed/core/v2/public/web_feed_subscriptions.h
index d84aa928..31de4fa 100644
--- a/components/feed/core/v2/public/web_feed_subscriptions.h
+++ b/components/feed/core/v2/public/web_feed_subscriptions.h
@@ -71,6 +71,11 @@
   // Refresh list of subscribed web feeds from the server.
   virtual void RefreshSubscriptions(
       base::OnceCallback<void(RefreshResult)> callback) = 0;
+
+  // Whether the user has subscribed to at least one web feed.
+  // Because this function returns synchronously, it may return the wrong value.
+  // TODO(crbug.com/1152592): Update this and its callers to use an async call.
+  virtual bool IsWebFeedSubscriber() = 0;
 };
 
 }  // namespace feed
diff --git a/components/feed/core/v2/web_feed_subscription_coordinator.cc b/components/feed/core/v2/web_feed_subscription_coordinator.cc
index bf764c8..6ef7a1d9 100644
--- a/components/feed/core/v2/web_feed_subscription_coordinator.cc
+++ b/components/feed/core/v2/web_feed_subscription_coordinator.cc
@@ -11,6 +11,7 @@
 #include "base/task/post_task.h"
 #include "base/threading/sequenced_task_runner_handle.h"
 #include "base/time/time.h"
+#include "components/feed/core/common/pref_names.h"
 #include "components/feed/core/proto/v2/store.pb.h"
 #include "components/feed/core/v2/config.h"
 #include "components/feed/core/v2/feed_stream.h"
@@ -20,6 +21,7 @@
 #include "components/feed/core/v2/web_feed_subscriptions/subscribe_to_web_feed_task.h"
 #include "components/feed/feed_feature_list.h"
 #include "components/offline_pages/task/closure_task.h"
+#include "components/prefs/pref_service.h"
 
 namespace feed {
 namespace {
@@ -191,8 +193,9 @@
 }  // namespace internal
 
 WebFeedSubscriptionCoordinator::WebFeedSubscriptionCoordinator(
+    PrefService* profile_prefs,
     FeedStream* feed_stream)
-    : feed_stream_(feed_stream) {
+    : feed_stream_(feed_stream), profile_prefs_(profile_prefs) {
   base::TimeDelta delay = GetFeedConfig().fetch_web_feed_info_delay;
   if (IsSignedInAndWebFeedsEnabled() && !delay.is_zero()) {
     base::SequencedTaskRunnerHandle::Get()->PostDelayedTask(
@@ -220,11 +223,15 @@
     const FeedStore::WebFeedStartupData& startup_data) {
   index_.Populate(startup_data.recommended_feed_index);
   index_.Populate(startup_data.subscribed_web_feeds);
+  populated_ = true;
+
+  UpdateIsSubscriberPref();
 }
 
 void WebFeedSubscriptionCoordinator::ClearAllFinished() {
-  index_.Populate(feedstore::RecommendedWebFeedIndex{});
+  index_.Clear();
   model_.reset();
+  UpdateIsSubscriberPref();
   FetchRecommendedWebFeedsIfStale();
   FetchSubscribedWebFeedsIfStale();
 }
@@ -669,6 +676,7 @@
   if (result.status == WebFeedRefreshStatus::kSuccess)
     model_->UpdateSubscribedFeeds(std::move(result.subscribed_web_feeds));
 
+  UpdateIsSubscriberPref();
   CallRefreshCompleteCallbacks(
       RefreshResult{result.status == WebFeedRefreshStatus::kSuccess});
 }
@@ -682,4 +690,22 @@
   }
 }
 
+// Ideally, this function would be async so that we can determine with more
+// certainty that the user is a web feed subscriber. Until the UI can be
+// updated, we need to return an answer right away, so we cache a boolean in
+// prefs.
+bool WebFeedSubscriptionCoordinator::IsWebFeedSubscriber() {
+  if (populated_) {
+    return IsSignedInAndWebFeedsEnabled() && index_.HasSubscriptions();
+  } else {
+    return profile_prefs_->GetBoolean(feed::prefs::kIsWebFeedSubscriber);
+  }
+}
+
+void WebFeedSubscriptionCoordinator::UpdateIsSubscriberPref() {
+  profile_prefs_->SetBoolean(
+      feed::prefs::kIsWebFeedSubscriber,
+      IsSignedInAndWebFeedsEnabled() && index_.HasSubscriptions());
+}
+
 }  // namespace feed
diff --git a/components/feed/core/v2/web_feed_subscription_coordinator.h b/components/feed/core/v2/web_feed_subscription_coordinator.h
index 7183d55..da1f2388 100644
--- a/components/feed/core/v2/web_feed_subscription_coordinator.h
+++ b/components/feed/core/v2/web_feed_subscription_coordinator.h
@@ -17,6 +17,7 @@
 #include "components/feed/core/v2/web_feed_subscriptions/unsubscribe_from_web_feed_task.h"
 #include "components/feed/core/v2/web_feed_subscriptions/web_feed_index.h"
 
+class PrefService;
 namespace feed {
 namespace internal {
 class WebFeedSubscriptionModel;
@@ -28,7 +29,8 @@
 // Coordinates the state of subscription to web feeds.
 class WebFeedSubscriptionCoordinator : public WebFeedSubscriptions {
  public:
-  explicit WebFeedSubscriptionCoordinator(FeedStream* feed_stream);
+  explicit WebFeedSubscriptionCoordinator(PrefService* profile_prefs,
+                                          FeedStream* feed_stream);
   virtual ~WebFeedSubscriptionCoordinator();
   WebFeedSubscriptionCoordinator(const WebFeedSubscriptionCoordinator&) =
       delete;
@@ -61,6 +63,7 @@
       base::OnceCallback<void(std::vector<WebFeedMetadata>)> callback) override;
   void RefreshSubscriptions(
       base::OnceCallback<void(RefreshResult)> callback) override;
+  bool IsWebFeedSubscriber() override;
 
   // Types / functions exposed for task implementations.
 
@@ -153,9 +156,14 @@
   void FetchSubscribedWebFeedsComplete(
       FetchSubscribedWebFeedsTask::Result result);
   void CallRefreshCompleteCallbacks(RefreshResult);
+  void UpdateIsSubscriberPref();
 
   FeedStream* feed_stream_;  // Always non-null, it owns this.
+  PrefService* profile_prefs_;
+
   WebFeedIndex index_;
+  // Whether `Populate()` has been called.
+  bool populated_ = false;
   // A model of subscriptions. In memory only while needed.
   // TODO(harringtond): Unload the model eventually.
   std::unique_ptr<WebFeedSubscriptionModel> model_;
diff --git a/components/feed/core/v2/web_feed_subscriptions/web_feed_index.cc b/components/feed/core/v2/web_feed_subscriptions/web_feed_index.cc
index c547820..464e0d4f 100644
--- a/components/feed/core/v2/web_feed_subscriptions/web_feed_index.cc
+++ b/components/feed/core/v2/web_feed_subscriptions/web_feed_index.cc
@@ -371,8 +371,7 @@
 }  // namespace
 
 WebFeedIndex::WebFeedIndex() {
-  recommended_ = std::make_unique<EntrySet>();
-  subscribed_ = std::make_unique<EntrySet>();
+  Clear();
 }
 
 WebFeedIndex::~WebFeedIndex() = default;
@@ -413,6 +412,13 @@
   subscribed_ = std::move(builder).Build();
 }
 
+void WebFeedIndex::Clear() {
+  recommended_ = std::make_unique<EntrySet>();
+  subscribed_ = std::make_unique<EntrySet>();
+  recommended_feeds_update_time_ = base::Time();
+  subscribed_feeds_update_time_ = base::Time();
+}
+
 WebFeedIndex::Entry WebFeedIndex::FindWebFeed(const std::string& web_feed_id) {
   for (const Entry& e : subscribed_->entries()) {
     if (e.web_feed_id == web_feed_id)
@@ -443,6 +449,10 @@
   return false;
 }
 
+bool WebFeedIndex::HasSubscriptions() const {
+  return !subscribed_->entries().empty();
+}
+
 std::vector<WebFeedIndex::Entry> WebFeedIndex::GetRecommendedEntriesForTesting()
     const {
   return recommended_->entries();
diff --git a/components/feed/core/v2/web_feed_subscriptions/web_feed_index.h b/components/feed/core/v2/web_feed_subscriptions/web_feed_index.h
index b42a88e..d784337 100644
--- a/components/feed/core/v2/web_feed_subscriptions/web_feed_index.h
+++ b/components/feed/core/v2/web_feed_subscriptions/web_feed_index.h
@@ -46,6 +46,8 @@
   // Populate the recommended feed index.
   void Populate(const feedstore::RecommendedWebFeedIndex& recommended_feeds);
 
+  void Clear();
+
   // Returns the Web Feed `Entry` which matches `page_info`. If there's more
   // than one match, preferentially returns subscribed Web Feed entries.
   Entry FindWebFeed(const WebFeedPageInformation& page_info);
@@ -59,6 +61,7 @@
   base::Time GetSubscribedFeedsUpdateTime() const {
     return subscribed_feeds_update_time_;
   }
+  bool HasSubscriptions() const;
 
   std::vector<Entry> GetRecommendedEntriesForTesting() const;
 
diff --git a/components/flags_ui/flags_state.cc b/components/flags_ui/flags_state.cc
index e291349..4a1e032 100644
--- a/components/flags_ui/flags_state.cc
+++ b/components/flags_ui/flags_state.cc
@@ -456,7 +456,7 @@
     // For any featrue name in |features| that is not in |switch_added_values| -
     // i.e. it wasn't added by about_flags code, add it to |remaining_features|.
     for (const auto& feature : features) {
-      if (!base::Contains(switch_added_values, feature.as_string()))
+      if (!base::Contains(switch_added_values, std::string(feature)))
         remaining_features.push_back(feature);
     }
 
diff --git a/components/gcm_driver/crypto/message_payload_parser.cc b/components/gcm_driver/crypto/message_payload_parser.cc
index f239878..a147baa 100644
--- a/components/gcm_driver/crypto/message_payload_parser.cc
+++ b/components/gcm_driver/crypto/message_payload_parser.cc
@@ -5,6 +5,7 @@
 #include "components/gcm_driver/crypto/message_payload_parser.h"
 
 #include "base/big_endian.h"
+#include "base/strings/string_piece.h"
 #include "components/gcm_driver/crypto/gcm_decryption_result.h"
 
 namespace gcm {
@@ -33,7 +34,7 @@
     return;
   }
 
-  salt_ = message.substr(0, kSaltSize).as_string();
+  salt_ = std::string(message.substr(0, kSaltSize));
   message.remove_prefix(kSaltSize);
 
   base::ReadBigEndian(message.data(), &record_size_);
@@ -60,10 +61,10 @@
     return;
   }
 
-  public_key_ = message.substr(0, kUncompressedPointSize).as_string();
+  public_key_ = std::string(message.substr(0, kUncompressedPointSize));
   message.remove_prefix(kUncompressedPointSize);
 
-  ciphertext_ = message.as_string();
+  ciphertext_ = std::string(message);
   DCHECK_GE(ciphertext_.size(), kMinimumRecordSize);
 
   is_valid_ = true;
diff --git a/components/google/core/common/google_util.cc b/components/google/core/common/google_util.cc
index d0676d9e..f0df49dd1 100644
--- a/components/google/core/common/google_util.cc
+++ b/components/google/core/common/google_util.cc
@@ -16,6 +16,7 @@
 #include "base/stl_util.h"
 #include "base/strings/strcat.h"
 #include "base/strings/string_number_conversions.h"
+#include "base/strings/string_piece.h"
 #include "base/strings/string_split.h"
 #include "base/strings/string_util.h"
 #include "base/strings/utf_string_conversions.h"
@@ -181,7 +182,7 @@
   // so use Spain instead.
   if (country_code == "cat")
     return "es";
-  return country_code.as_string();
+  return std::string(country_code);
 }
 
 GURL GetGoogleSearchURL(const GURL& google_homepage_url) {
diff --git a/components/history/core/browser/url_database.cc b/components/history/core/browser/url_database.cc
index 432d208..9987f00 100644
--- a/components/history/core/browser/url_database.cc
+++ b/components/history/core/browser/url_database.cc
@@ -9,6 +9,7 @@
 #include <vector>
 
 #include "base/i18n/case_conversion.h"
+#include "base/logging.h"
 #include "base/strings/string_util.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/time/time.h"
diff --git a/components/invalidation/impl/fcm_invalidation_service.cc b/components/invalidation/impl/fcm_invalidation_service.cc
index 51963792..0fb595ca 100644
--- a/components/invalidation/impl/fcm_invalidation_service.cc
+++ b/components/invalidation/impl/fcm_invalidation_service.cc
@@ -6,7 +6,6 @@
 
 #include "base/i18n/time_formatting.h"
 #include "build/build_config.h"
-#include "components/invalidation/impl/invalidation_switches.h"
 #include "components/invalidation/public/invalidator_state.h"
 #include "components/prefs/scoped_user_pref_update.h"
 #include "google_apis/gaia/gaia_constants.h"
@@ -113,11 +112,8 @@
   // IsReadyToStart checks if account is available (active account logged in
   // and token is available). As currently observed, FCMInvalidationService
   // isn't always notified on Android when token is available.
-  if (base::FeatureList::IsEnabled(
-          switches::kFCMInvalidationsStartOnceActiveAccountAvailable)) {
-    valid_account_info_available =
-        !identity_provider_->GetActiveAccountId().empty();
-  }
+  valid_account_info_available =
+      !identity_provider_->GetActiveAccountId().empty();
 #endif
 
   if (!valid_account_info_available) {
diff --git a/components/invalidation/impl/fcm_network_handler.cc b/components/invalidation/impl/fcm_network_handler.cc
index a315159..4e00d1e7 100644
--- a/components/invalidation/impl/fcm_network_handler.cc
+++ b/components/invalidation/impl/fcm_network_handler.cc
@@ -16,6 +16,7 @@
 #include "base/metrics/histogram_macros.h"
 #include "base/observer_list.h"
 #include "base/strings/string_number_conversions.h"
+#include "base/strings/string_piece.h"
 #include "base/strings/string_util.h"
 #include "base/task/post_task.h"
 #include "build/build_config.h"
@@ -98,11 +99,11 @@
 // If the provided sender does not match either pattern, return it unchanged.
 std::string UnpackPrivateTopic(base::StringPiece private_topic) {
   if (base::StartsWith(private_topic, "/topics/private/")) {
-    return private_topic.substr(strlen("/topics")).as_string();
+    return std::string(private_topic.substr(strlen("/topics")));
   } else if (base::StartsWith(private_topic, "/topics/")) {
-    return private_topic.substr(strlen("/topics/")).as_string();
+    return std::string(private_topic.substr(strlen("/topics/")));
   } else {
-    return private_topic.as_string();
+    return std::string(private_topic);
   }
 }
 
diff --git a/components/invalidation/impl/invalidation_switches.cc b/components/invalidation/impl/invalidation_switches.cc
index 8efcebec1..80c052a 100644
--- a/components/invalidation/impl/invalidation_switches.cc
+++ b/components/invalidation/impl/invalidation_switches.cc
@@ -18,15 +18,6 @@
 
 }  // namespace
 
-// This feature affects only Android.
-const base::Feature kFCMInvalidationsStartOnceActiveAccountAvailable = {
-    "FCMInvalidationsStartOnceActiveAccountAvailable",
-    base::FEATURE_ENABLED_BY_DEFAULT};
-
-const base::Feature kFCMInvalidationsForSyncDontCheckVersion = {
-    "FCMInvalidationsForSyncDontCheckVersion",
-    base::FEATURE_ENABLED_BY_DEFAULT};
-
 const base::Feature kSyncInstanceIDTokenTTL {
   "SyncInstanceIDTokenTTL",
 #if defined(OS_WIN) || defined(OS_MAC) || defined(OS_LINUX) || \
diff --git a/components/invalidation/impl/invalidation_switches.h b/components/invalidation/impl/invalidation_switches.h
index f13c14ed8..a9124743 100644
--- a/components/invalidation/impl/invalidation_switches.h
+++ b/components/invalidation/impl/invalidation_switches.h
@@ -11,8 +11,6 @@
 namespace invalidation {
 namespace switches {
 
-extern const base::Feature kFCMInvalidationsStartOnceActiveAccountAvailable;
-extern const base::Feature kFCMInvalidationsForSyncDontCheckVersion;
 extern const base::Feature kSyncInstanceIDTokenTTL;
 extern const base::FeatureParam<int> kSyncInstanceIDTokenTTLSeconds;
 extern const base::Feature kPolicyInstanceIDTokenTTL;
diff --git a/components/language/core/browser/heuristic_language_model.cc b/components/language/core/browser/heuristic_language_model.cc
index 98b4fe95..89f14d1b 100644
--- a/components/language/core/browser/heuristic_language_model.cc
+++ b/components/language/core/browser/heuristic_language_model.cc
@@ -9,6 +9,7 @@
 #include <vector>
 
 #include "base/feature_list.h"
+#include "base/strings/string_piece.h"
 #include "base/strings/string_split.h"
 #include "components/prefs/pref_service.h"
 
@@ -41,7 +42,7 @@
   const std::vector<base::StringPiece> tokens = base::SplitStringPiece(
       lang, "-", base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
 
-  *base = tokens.size() > 0 ? tokens[0].as_string() : "";
+  *base = tokens.size() > 0 ? std::string(tokens[0]) : "";
   return tokens.size() > 1 && !tokens[1].empty();
 }
 
diff --git a/components/lookalikes/core/lookalike_url_util.cc b/components/lookalikes/core/lookalike_url_util.cc
index 7677aa96..bb64053 100644
--- a/components/lookalikes/core/lookalike_url_util.cc
+++ b/components/lookalikes/core/lookalike_url_util.cc
@@ -212,13 +212,12 @@
     const base::span<const base::StringPiece>& domain_labels,
     const LookalikeTargetAllowlistChecker& in_target_allowlist) {
   DCHECK(domain_labels.size() >= 2);
-  std::string potential_hostname =
-      domain_labels[domain_labels.size() - 1].as_string();
+  std::string potential_hostname(domain_labels[domain_labels.size() - 1]);
   // Attach each token from the end to the embedded target to check if that
   // subdomain has been allowlisted.
   for (int i = domain_labels.size() - 2; i >= 0; i--) {
     potential_hostname =
-        domain_labels[i].as_string() + "." + potential_hostname;
+        std::string(domain_labels[i]) + "." + potential_hostname;
     if (in_target_allowlist.Run(potential_hostname)) {
       return true;
     }
@@ -323,14 +322,13 @@
 bool IsEmbeddingItself(const base::span<const base::StringPiece>& domain_labels,
                        const std::string& embedding_domain) {
   DCHECK(domain_labels.size() >= 2);
-  std::string potential_hostname =
-      domain_labels[domain_labels.size() - 1].as_string();
+  std::string potential_hostname(domain_labels[domain_labels.size() - 1]);
   // Attach each token from the end to the embedded target to check if that
   // subdomain is the embedding domain. (e.g. using the earlier example, check
   // each ["com", "example.com", "foo.example.com"] against "example.com".
   for (int i = domain_labels.size() - 2; i >= 0; i--) {
     potential_hostname =
-        domain_labels[i].as_string() + "." + potential_hostname;
+        std::string(domain_labels[i]) + "." + potential_hostname;
     if (embedding_domain == potential_hostname) {
       return true;
     }
diff --git a/components/media_router/browser/logger_impl.cc b/components/media_router/browser/logger_impl.cc
index 6a9b967..48bb1872 100644
--- a/components/media_router/browser/logger_impl.cc
+++ b/components/media_router/browser/logger_impl.cc
@@ -6,6 +6,7 @@
 
 #include "base/i18n/time_formatting.h"
 #include "base/json/json_string_value_serializer.h"
+#include "base/strings/string_piece.h"
 #include "base/values.h"
 #include "components/media_router/common/media_source.h"
 #include "components/media_router/common/mojom/logger.mojom-shared.h"
@@ -128,11 +129,11 @@
     : severity(severity),
       category(category),
       time(time),
-      component(component.as_string()),
-      message(message.as_string()),
-      sink_id(sink_id.as_string()),
+      component(component),
+      message(message),
+      sink_id(sink_id),
       media_source(std::move(media_source)),
-      session_id(session_id.as_string()) {}
+      session_id(session_id) {}
 
 LoggerImpl::Entry::Entry(Entry&& other)
     : severity(other.severity),
diff --git a/components/media_router/common/media_source.cc b/components/media_router/common/media_source.cc
index 407f5266..b3e450e 100644
--- a/components/media_router/common/media_source.cc
+++ b/components/media_router/common/media_source.cc
@@ -10,6 +10,7 @@
 #include <ostream>
 #include <string>
 
+#include "base/strings/string_piece.h"
 #include "base/strings/string_util.h"
 #include "base/strings/stringprintf.h"
 #include "components/media_router/common/media_source.h"
@@ -113,16 +114,16 @@
     bool with_audio) {
   DCHECK(!registered_desktop_stream_id.empty());
   std::string id =
-      kDesktopMediaUrnPrefix.as_string() + registered_desktop_stream_id;
+      std::string(kDesktopMediaUrnPrefix) + registered_desktop_stream_id;
   if (with_audio) {
-    id += kDesktopMediaUrnAudioParam.as_string();
+    id += std::string(kDesktopMediaUrnAudioParam);
   }
   return MediaSource(id);
 }
 
 // static
 MediaSource MediaSource::ForUnchosenDesktop() {
-  return MediaSource(kUnchosenDesktopMediaUrn.as_string());
+  return MediaSource(std::string(kUnchosenDesktopMediaUrn));
 }
 
 // static
diff --git a/components/metrics/file_metrics_provider.cc b/components/metrics/file_metrics_provider.cc
index 708ddc3b..9c3a068 100644
--- a/components/metrics/file_metrics_provider.cc
+++ b/components/metrics/file_metrics_provider.cc
@@ -238,8 +238,8 @@
 void FileMetricsProvider::RegisterSourcePrefs(
     PrefRegistrySimple* prefs,
     const base::StringPiece prefs_key) {
-  prefs->RegisterInt64Pref(metrics::prefs::kMetricsLastSeenPrefix +
-                           prefs_key.as_string(), 0);
+  prefs->RegisterInt64Pref(
+      metrics::prefs::kMetricsLastSeenPrefix + std::string(prefs_key), 0);
 }
 
 //  static
diff --git a/components/metrics/serialization/metric_sample.cc b/components/metrics/serialization/metric_sample.cc
index 3876fc3..d6549a6 100644
--- a/components/metrics/serialization/metric_sample.cc
+++ b/components/metrics/serialization/metric_sample.cc
@@ -9,6 +9,7 @@
 
 #include "base/check_op.h"
 #include "base/strings/string_number_conversions.h"
+#include "base/strings/string_piece.h"
 #include "base/strings/string_split.h"
 #include "base/strings/stringprintf.h"
 
@@ -129,7 +130,7 @@
     return nullptr;
   }
 
-  return HistogramSample(parts[0].as_string(), sample, min, max, bucket_count);
+  return HistogramSample(std::string(parts[0]), sample, min, max, bucket_count);
 }
 
 // static
@@ -151,7 +152,7 @@
   if (parts[0].empty() || !base::StringToInt(parts[1], &sample))
     return nullptr;
 
-  return SparseHistogramSample(parts[0].as_string(), sample);
+  return SparseHistogramSample(std::string(parts[0]), sample);
 }
 
 // static
@@ -176,7 +177,7 @@
     return nullptr;
   }
 
-  return LinearHistogramSample(parts[0].as_string(), sample, max);
+  return LinearHistogramSample(std::string(parts[0]), sample, max);
 }
 
 // static
diff --git a/components/new_or_sad_tab_strings.grdp b/components/new_or_sad_tab_strings.grdp
index f2e71538c..43ce1b8f 100644
--- a/components/new_or_sad_tab_strings.grdp
+++ b/components/new_or_sad_tab_strings.grdp
@@ -120,11 +120,11 @@
       <if expr="not is_android">
         <message name="IDS_NEW_TAB_OTR_HEADING"
                  desc="Heading used when a person opens an OTR window">
-          You’ve gone incognito
+          You’ve gone Incognito
         </message>
         <message name="IDS_NEW_TAB_OTR_DESCRIPTION"
                  desc="Used when a person opens an OTR window">
-          Pages you view in incognito tabs won’t stick around in your browser’s history, cookie store, or search history after you’ve closed all of your incognito tabs. Any files you download or bookmarks you create will be kept.
+          Pages you view in Incognito tabs won’t stick around in your browser’s history, cookie store, or search history after you’ve closed all of your Incognito tabs. Any files you download or bookmarks you create will be kept.
         </message>
         <message name="IDS_NEW_TAB_OTR_LEARN_MORE_LINK"
                  desc="OTR window link text to learn more">
@@ -132,7 +132,7 @@
         </message>
         <message name="IDS_NEW_TAB_OTR_MESSAGE_WARNING"
                  desc="OTR window warning message. This follows the IDS_NEW_TAB_OTR_DESCRIPTION paragraph">
-          However, you aren’t invisible. Going incognito doesn’t hide your browsing from your employer, your internet service provider, or the websites you visit.
+          However, you aren’t invisible. Going Incognito doesn’t hide your browsing from your employer, your internet service provider, or the websites you visit.
         </message>
       </if>
       <message name="IDS_NEW_TAB_UNDO_THUMBNAIL_REMOVE"
@@ -144,7 +144,7 @@
       <!-- TODO(msramek): Merge with the above section once this is fully launched. -->
 
       <message name="IDS_NEW_TAB_OTR_TITLE" desc="Title of the Incognito new tab page. The Incognito mode provides private browsing experience by hiding browsing activity from other people using the same device. However, it does not make the user completely invisible or anonymous; please don't translate it as such." formatter_data="android_java">
-        You’ve gone incognito
+        You’ve gone Incognito
       </message>
       <message name="IDS_NEW_TAB_OTR_SUBTITLE" desc="Subtitle of the Incognito new tab page, explaining to the user that the Incognito mode hides their browsing activity from other people using the same device. The second sentence clarifies that there are two important exceptions from this rule - downloaded files and added bookmarks will be persisted even after the Incognito session is closed." formatter_data="android_java">
         Now you can browse privately, and other people who use this device won’t see your activity. However, downloads and bookmarks will be saved.
diff --git a/components/ntp_tiles/icon_cacher_impl.cc b/components/ntp_tiles/icon_cacher_impl.cc
index 7e92fa1..a0bcf17 100644
--- a/components/ntp_tiles/icon_cacher_impl.cc
+++ b/components/ntp_tiles/icon_cacher_impl.cc
@@ -9,6 +9,7 @@
 #include "base/bind.h"
 #include "base/metrics/field_trial_params.h"
 #include "base/metrics/histogram_macros.h"
+#include "base/strings/string_piece.h"
 #include "components/favicon/core/favicon_service.h"
 #include "components/favicon/core/favicon_util.h"
 #include "components/favicon/core/large_icon_service.h"
@@ -189,9 +190,8 @@
                          weak_ptr_factory_.GetWeakPtr(), site,
                          std::move(preliminary_icon_available))));
   image_fetcher_->GetImageDecoder()->DecodeImage(
-      ui::ResourceBundle::GetSharedInstance()
-          .GetRawDataResource(site.default_icon_resource)
-          .as_string(),
+      std::string(ui::ResourceBundle::GetSharedInstance().GetRawDataResource(
+          site.default_icon_resource)),
       gfx::Size(kDesiredFrameSize, kDesiredFrameSize),
       preliminary_callback->callback());
   return preliminary_callback;
diff --git a/components/omnibox_strings.grdp b/components/omnibox_strings.grdp
index 6bcbcc6..70d6a927 100644
--- a/components/omnibox_strings.grdp
+++ b/components/omnibox_strings.grdp
@@ -139,16 +139,16 @@
     Manage payment methods button, press Enter to manage your payments and credit card info in Chrome settings
   </message>
 
-  <message name="IDS_OMNIBOX_PEDAL_LAUNCH_INCOGNITO_HINT" desc="The button text contents to suggest pedal action, launch incognito.">
+  <message name="IDS_OMNIBOX_PEDAL_LAUNCH_INCOGNITO_HINT" desc="The button text contents to suggest pedal action, launch Incognito.">
     Open Incognito window
   </message>
-  <message name="IDS_OMNIBOX_PEDAL_LAUNCH_INCOGNITO_SUGGESTION_CONTENTS" desc="The suggestion content text to suggest pedal action, launch incognito.">
+  <message name="IDS_OMNIBOX_PEDAL_LAUNCH_INCOGNITO_SUGGESTION_CONTENTS" desc="The suggestion content text to suggest pedal action, launch Incognito.">
     Open a new Incognito window to browse privately
   </message>
-  <message name="IDS_ACC_OMNIBOX_PEDAL_LAUNCH_INCOGNITO_SUFFIX" desc="Suffix for spoken suggestion description with launch incognito pedal action to explain keystroke used to launch incognito.">
+  <message name="IDS_ACC_OMNIBOX_PEDAL_LAUNCH_INCOGNITO_SUFFIX" desc="Suffix for spoken suggestion description with launch Incognito pedal action to explain keystroke used to launch Incognito.">
     <ph name="LAUNCH_INCOGNITO_FOCUSED_FRIENDLY_MATCH_TEXT">$1<ex>The Chromium Projects http://www.chromium.org bookmark</ex></ph>, press Tab then Enter to open a new Incognito window to browse privately
   </message>
-  <message name="IDS_ACC_OMNIBOX_PEDAL_LAUNCH_INCOGNITO" desc="Announcement when launch incognito pedal button is focused.">
+  <message name="IDS_ACC_OMNIBOX_PEDAL_LAUNCH_INCOGNITO" desc="Announcement when launch Incognito pedal button is focused.">
     Open Incognito Window button, press Enter to open a new Incognito window to browse privately
   </message>
 
diff --git a/components/password_manager/core/browser/android_affiliation/affiliation_utils.cc b/components/password_manager/core/browser/android_affiliation/affiliation_utils.cc
index cdc5735..aa2d429 100644
--- a/components/password_manager/core/browser/android_affiliation/affiliation_utils.cc
+++ b/components/password_manager/core/browser/android_affiliation/affiliation_utils.cc
@@ -246,14 +246,14 @@
 
 std::string FacetURI::scheme() const {
   return is_valid()
-             ? ComponentString(canonical_spec_, parsed_.scheme).as_string()
+             ? std::string(ComponentString(canonical_spec_, parsed_.scheme))
              : "";
 }
 
 std::string FacetURI::android_package_name() const {
   if (!IsValidAndroidFacetURI())
     return "";
-  return ComponentString(canonical_spec_, parsed_.host).as_string();
+  return std::string(ComponentString(canonical_spec_, parsed_.host));
 }
 
 FacetURI::FacetURI(const std::string& canonical_spec, bool is_valid)
diff --git a/components/password_manager/core/browser/password_ui_utils.cc b/components/password_manager/core/browser/password_ui_utils.cc
index d711aab..465d896 100644
--- a/components/password_manager/core/browser/password_ui_utils.cc
+++ b/components/password_manager/core/browser/password_ui_utils.cc
@@ -9,6 +9,7 @@
 #include <vector>
 
 #include "base/metrics/histogram_macros.h"
+#include "base/strings/string_piece.h"
 #include "base/strings/string_split.h"
 #include "base/strings/string_util.h"
 #include "base/strings/utf_string_conversions.h"
@@ -70,7 +71,7 @@
     }
   }
 
-  return result.find('.') != base::StringPiece::npos ? result.as_string()
+  return result.find('.') != base::StringPiece::npos ? std::string(result)
                                                      : original;
 }
 
diff --git a/components/payments_strings.grdp b/components/payments_strings.grdp
index 558ce460..c589b52a 100644
--- a/components/payments_strings.grdp
+++ b/components/payments_strings.grdp
@@ -552,8 +552,8 @@
     Payment Manifest Parser
   </message>
   <if expr="is_android">
-    <message name="IDS_EXTERNAL_PAYMENT_APP_LEAVE_INCOGNITO_WARNING" desc="Alert dialog text warning the user that incognito mode does not continue into external payment apps." formatter_data="android_java">
-      Leaving incognito mode to pay via an external application. Continue?
+    <message name="IDS_EXTERNAL_PAYMENT_APP_LEAVE_INCOGNITO_WARNING" desc="Alert dialog text warning the user that Incognito mode does not continue into external payment apps." formatter_data="android_java">
+      Leaving Incognito mode to pay via an external application. Continue?
     </message>
   </if>
 
@@ -658,7 +658,7 @@
     Check out faster next time
   </message>
   <message name="IDS_PAYMENT_CREDENTIAL_ENROLLMENT_OFF_THE_RECORD_DESCRIPTION" desc="Extra description at the bottom of a dialog that asks for the user's permission to set up device-based sign-in (i.e., using their computer's biometric sensor or screen unlock/PIN) to verify card payments. This is shown only in Incognito mode and explains that the sign-in data will be stored to the device.">
-    Sign-in data will be stored on this device after you exit incognito mode.
+    Sign-in data will be stored on this device after you exit Incognito mode.
   </message>
   <message name="IDS_PAYMENT_CREDENTIAL_ENROLLMENT_CANCEL_BUTTON_LABEL" desc="Cancel button label on a dialog that asks for the user's permission to set up device-based sign-in (i.e., using their computer's biometric sensor or screen unlock/PIN) to verify card payments.">
     No thanks
diff --git a/components/performance_manager/graph/graph_impl.cc b/components/performance_manager/graph/graph_impl.cc
index ca99daf..f9fed62 100644
--- a/components/performance_manager/graph/graph_impl.cc
+++ b/components/performance_manager/graph/graph_impl.cc
@@ -15,6 +15,7 @@
 #include "base/macros.h"
 #include "base/notreached.h"
 #include "base/stl_util.h"
+#include "base/strings/string_piece.h"
 #include "components/performance_manager/graph/frame_node_impl.h"
 #include "components/performance_manager/graph/node_base.h"
 #include "components/performance_manager/graph/page_node_impl.h"
@@ -117,7 +118,7 @@
   }
 #endif
   bool inserted =
-      describers_.insert(std::make_pair(describer, name.as_string())).second;
+      describers_.insert(std::make_pair(describer, std::string(name))).second;
   DCHECK(inserted);
 }
 
diff --git a/components/policy/content/safe_sites_navigation_throttle.cc b/components/policy/content/safe_sites_navigation_throttle.cc
index 6ed425d2..dec33b96 100644
--- a/components/policy/content/safe_sites_navigation_throttle.cc
+++ b/components/policy/content/safe_sites_navigation_throttle.cc
@@ -40,7 +40,7 @@
           base::BindRepeating(&SafeSitesNavigationThrottle::OnDeferredResult,
                               base::Unretained(this))),
       safe_sites_error_page_content_(
-          safe_sites_error_page_content.as_string()) {}
+          std::string(safe_sites_error_page_content)) {}
 
 SafeSitesNavigationThrottle::~SafeSitesNavigationThrottle() = default;
 
diff --git a/components/safe_browsing/content/renderer/phishing_classifier/phishing_url_feature_extractor.cc b/components/safe_browsing/content/renderer/phishing_classifier/phishing_url_feature_extractor.cc
index cbd60d1..83b680ae 100644
--- a/components/safe_browsing/content/renderer/phishing_classifier/phishing_url_feature_extractor.cc
+++ b/components/safe_browsing/content/renderer/phishing_classifier/phishing_url_feature_extractor.cc
@@ -9,6 +9,7 @@
 #include <vector>
 
 #include "base/metrics/histogram_macros.h"
+#include "base/strings/string_piece.h"
 #include "base/strings/string_split.h"
 #include "base/strings/string_util.h"
 #include "base/timer/elapsed_timer.h"
@@ -109,7 +110,7 @@
     // Copy over only the splits that are 3 or more chars long.
     // TODO(bryner): Determine a meaningful min size.
     if (token.length() >= kMinPathComponentLength)
-      tokens->push_back(token.as_string());
+      tokens->push_back(std::string(token));
   }
 }
 
diff --git a/components/safe_browsing/core/db/v4_store.cc b/components/safe_browsing/core/db/v4_store.cc
index ec7ed07..1ad7c27e 100644
--- a/components/safe_browsing/core/db/v4_store.cc
+++ b/components/safe_browsing/core/db/v4_store.cc
@@ -14,6 +14,7 @@
 #include "base/metrics/histogram_functions.h"
 #include "base/metrics/histogram_macros.h"
 #include "base/stl_util.h"
+#include "base/strings/string_piece.h"
 #include "base/strings/stringprintf.h"
 #include "components/safe_browsing/core/db/prefix_iterator.h"
 #include "components/safe_browsing/core/db/v4_rice.h"
@@ -747,7 +748,7 @@
     const PrefixSize& prefix_size = pair.first;
     base::StringPiece hash_prefix = full_hash.substr(0, prefix_size);
     if (HashPrefixMatches(hash_prefix, pair.second, prefix_size))
-      return hash_prefix.as_string();
+      return std::string(hash_prefix);
   }
   return HashPrefix();
 }
diff --git a/components/search_engines/template_url_data.cc b/components/search_engines/template_url_data.cc
index bd2c32c..9e38747 100644
--- a/components/search_engines/template_url_data.cc
+++ b/components/search_engines/template_url_data.cc
@@ -7,6 +7,7 @@
 #include "base/check.h"
 #include "base/guid.h"
 #include "base/i18n/case_conversion.h"
+#include "base/strings/string_piece.h"
 #include "base/strings/string_util.h"
 #include "base/strings/stringprintf.h"
 #include "base/strings/utf_string_conversions.h"
@@ -89,8 +90,8 @@
       sync_guid(GenerateGUID(prepopulate_id)) {
   SetShortName(name);
   SetKeyword(keyword);
-  SetURL(search_url.as_string());
-  input_encodings.push_back(encoding.as_string());
+  SetURL(std::string(search_url));
+  input_encodings.push_back(std::string(encoding));
   for (size_t i = 0; i < alternate_urls_list.GetSize(); ++i) {
     std::string alternate_url;
     alternate_urls_list.GetString(i, &alternate_url);
diff --git a/components/services/storage/dom_storage/dom_storage_database_unittest.cc b/components/services/storage/dom_storage/dom_storage_database_unittest.cc
index f183c3237..cebe4ac 100644
--- a/components/services/storage/dom_storage/dom_storage_database_unittest.cc
+++ b/components/services/storage/dom_storage/dom_storage_database_unittest.cc
@@ -59,7 +59,7 @@
 }
 
 std::string MakePrefixedKey(base::StringPiece prefix, base::StringPiece key) {
-  return prefix.as_string() + key.as_string();
+  return std::string(prefix) + std::string(key);
 }
 
 class StorageServiceDomStorageDatabaseTest : public testing::Test {
diff --git a/components/services/storage/dom_storage/session_storage_metadata.cc b/components/services/storage/dom_storage/session_storage_metadata.cc
index dfb1f43..9da5eeb 100644
--- a/components/services/storage/dom_storage/session_storage_metadata.cc
+++ b/components/services/storage/dom_storage/session_storage_metadata.cc
@@ -8,6 +8,7 @@
 #include "base/macros.h"
 #include "base/stl_util.h"
 #include "base/strings/string_number_conversions.h"
+#include "base/strings/string_piece.h"
 #include "base/strings/string_util.h"
 #include "components/services/storage/dom_storage/async_dom_storage_database.h"
 #include "third_party/blink/public/common/dom_storage/session_storage_namespace_id.h"
@@ -200,7 +201,7 @@
 
     auto origin = url::Origin::Create(origin_gurl);
     if (namespace_id != last_namespace_id) {
-      last_namespace_id = namespace_id.as_string();
+      last_namespace_id = std::string(namespace_id);
       DCHECK(namespace_origin_map_.find(last_namespace_id) ==
              namespace_origin_map_.end());
       last_namespace = &(namespace_origin_map_[last_namespace_id]);
diff --git a/components/services/storage/indexed_db/transactional_leveldb/transactional_leveldb_transaction_unittest.cc b/components/services/storage/indexed_db/transactional_leveldb/transactional_leveldb_transaction_unittest.cc
index 4042de6..644beee 100644
--- a/components/services/storage/indexed_db/transactional_leveldb/transactional_leveldb_transaction_unittest.cc
+++ b/components/services/storage/indexed_db/transactional_leveldb/transactional_leveldb_transaction_unittest.cc
@@ -637,13 +637,13 @@
 
   ASSERT_TRUE(it->IsValid());
   EXPECT_EQ(Compare(key_in_range2_, it->Key()), 0)
-      << key_in_range2_ << " != " << it->Key().as_string();
+      << key_in_range2_ << " != " << it->Key();
 
   status = it->Next();
   EXPECT_TRUE(status.ok());
   ASSERT_TRUE(it->IsValid());
   EXPECT_EQ(Compare(key_after_range_, it->Key()), 0)
-      << key_after_range_ << " != " << it->Key().as_string();
+      << key_after_range_ << " != " << it->Key();
 
   status = transaction_->Commit(/*sync_on_commit=*/false);
   EXPECT_TRUE(status.ok());
diff --git a/components/signin/core/browser/signin_header_helper.cc b/components/signin/core/browser/signin_header_helper.cc
index c1fbd255..155bc5b1 100644
--- a/components/signin/core/browser/signin_header_helper.cc
+++ b/components/signin/core/browser/signin_header_helper.cc
@@ -9,6 +9,7 @@
 #include "base/logging.h"
 #include "base/macros.h"
 #include "base/metrics/histogram_functions.h"
+#include "base/strings/string_piece.h"
 #include "base/strings/string_split.h"
 #include "components/google/core/common/google_util.h"
 #include "components/signin/core/browser/chrome_connected_header_helper.h"
@@ -149,9 +150,9 @@
       continue;
     }
     dictionary.insert(
-        {field.substr(0, delim).as_string(),
+        {std::string(field.substr(0, delim)),
          net::UnescapeURLComponent(
-             field.substr(delim + 1).as_string(),
+             field.substr(delim + 1),
              net::UnescapeRule::PATH_SEPARATORS |
                  net::UnescapeRule::URL_SPECIAL_CHARS_EXCEPT_PATH_SEPARATORS)});
   }
diff --git a/components/signin/internal/identity_manager/mutable_profile_oauth2_token_service_delegate.cc b/components/signin/internal/identity_manager/mutable_profile_oauth2_token_service_delegate.cc
index 26d8660..82a6d66 100644
--- a/components/signin/internal/identity_manager/mutable_profile_oauth2_token_service_delegate.cc
+++ b/components/signin/internal/identity_manager/mutable_profile_oauth2_token_service_delegate.cc
@@ -50,30 +50,6 @@
   NUM_LOAD_TOKEN_FROM_DB_STATUS
 };
 
-// Used to record events related to token revocation requests in histograms.
-// Do not change existing values, new values can only be added at the end.
-enum class TokenRevocationRequestProgress {
-  // The request was created.
-  kRequestCreated = 0,
-  // The request was sent over the network.
-  kRequestStarted = 1,
-  // The network request completed with a failure.
-  kRequestFailed = 2,
-  // The network request completed with a success.
-  kRequestSucceeded = 3,
-
-  kMaxValue = kRequestSucceeded
-};
-
-// Adds a sample to the TokenRevocationRequestProgress histogram. Encapsuled in
-// a function to reduce executable size, because histogram macros may generate a
-// lot of code.
-void RecordRefreshTokenRevocationRequestEvent(
-    TokenRevocationRequestProgress event) {
-  UMA_HISTOGRAM_ENUMERATION("Signin.RefreshTokenRevocationRequestProgress",
-                            event);
-}
-
 std::string ApplyAccountIdPrefix(const std::string& account_id) {
   return kAccountIdPrefix + account_id;
 }
@@ -166,8 +142,6 @@
                token_service_delegate_->GetURLLoaderFactory()),
       refresh_token_(refresh_token),
       attempt_(attempt) {
-  RecordRefreshTokenRevocationRequestEvent(
-      TokenRevocationRequestProgress::kRequestCreated);
   client->DelayNetworkCall(
       base::BindRepeating(&MutableProfileOAuth2TokenServiceDelegate::
                               RevokeServerRefreshToken::Start,
@@ -176,8 +150,6 @@
 
 void MutableProfileOAuth2TokenServiceDelegate::RevokeServerRefreshToken::
     Start() {
-  RecordRefreshTokenRevocationRequestEvent(
-      TokenRevocationRequestProgress::kRequestStarted);
   fetcher_.StartRevokeOAuth2Token(refresh_token_);
 }
 
@@ -207,18 +179,11 @@
 void MutableProfileOAuth2TokenServiceDelegate::RevokeServerRefreshToken::
     OnOAuth2RevokeTokenCompleted(
         GaiaAuthConsumer::TokenRevocationStatus status) {
-  UMA_HISTOGRAM_ENUMERATION("Signin.RefreshTokenRevocationStatus", status);
   if (ShouldRetry(status)) {
     token_service_delegate_->server_revokes_.push_back(
         std::make_unique<RevokeServerRefreshToken>(
             token_service_delegate_, token_service_delegate_->client_,
             refresh_token_, attempt_ + 1));
-  } else {
-    RecordRefreshTokenRevocationRequestEvent(
-        (status == GaiaAuthConsumer::TokenRevocationStatus::kSuccess)
-            ? TokenRevocationRequestProgress::kRequestSucceeded
-            : TokenRevocationRequestProgress::kRequestFailed);
-    UMA_HISTOGRAM_ENUMERATION("Signin.RefreshTokenRevocationCompleted", status);
   }
   // |this| pointer will be deleted when removed from the vector, so don't
   // access any members after call to erase().
diff --git a/components/signin/public/identity_manager/identity_manager_builder.h b/components/signin/public/identity_manager/identity_manager_builder.h
index 09aab28..29264ded 100644
--- a/components/signin/public/identity_manager/identity_manager_builder.h
+++ b/components/signin/public/identity_manager/identity_manager_builder.h
@@ -75,7 +75,7 @@
   std::unique_ptr<ProfileOAuth2TokenService> token_service;
 
 #if !defined(OS_ANDROID)
-  bool delete_signin_cookies_on_exit;
+  bool delete_signin_cookies_on_exit = false;
   scoped_refptr<TokenWebData> token_web_data;
 #endif
 
diff --git a/components/spellcheck/common/spellcheck_common.cc b/components/spellcheck/common/spellcheck_common.cc
index b7cd410..f3e9dff 100644
--- a/components/spellcheck/common/spellcheck_common.cc
+++ b/components/spellcheck/common/spellcheck_common.cc
@@ -9,6 +9,7 @@
 #include "base/containers/contains.h"
 #include "base/files/file_path.h"
 #include "base/metrics/field_trial.h"
+#include "base/strings/string_piece.h"
 #include "base/strings/string_util.h"
 #include "third_party/icu/source/common/unicode/uloc.h"
 #include "third_party/icu/source/common/unicode/urename.h"
@@ -104,7 +105,7 @@
       return lang_region.language_region;
   }
 
-  return input_language.as_string();
+  return std::string(input_language);
 }
 
 base::FilePath GetVersionedFileName(base::StringPiece input_language,
@@ -168,7 +169,7 @@
   for (const auto& lang_region : kSupportedSpellCheckerLanguages) {
     // First look for exact match in the language region of the list.
     if (lang_region.language == language)
-      return language.as_string();
+      return std::string(language);
 
     // Next, look for exact match in the language_region part of the list.
     if (lang_region.language_region == language) {
diff --git a/components/storage_monitor/storage_info_utils.cc b/components/storage_monitor/storage_info_utils.cc
index 0a777b3..49fefa1 100644
--- a/components/storage_monitor/storage_info_utils.cc
+++ b/components/storage_monitor/storage_info_utils.cc
@@ -9,6 +9,7 @@
 #include "base/files/file_path.h"
 #include "base/stl_util.h"
 #include "base/strings/string_number_conversions.h"
+#include "base/strings/string_piece.h"
 #include "base/strings/string_split.h"
 #include "base/strings/utf_string_conversions.h"
 #include "components/storage_monitor/removable_device_constants.h"
@@ -27,7 +28,7 @@
 std::string GetStorageIdFromStorageName(const std::string& storage_name) {
   std::vector<base::StringPiece> name_parts = base::SplitStringPiece(
       storage_name, ":", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL);
-  return name_parts.size() == 3 ? name_parts[2].as_string() : std::string();
+  return name_parts.size() == 3 ? std::string(name_parts[2]) : std::string();
 }
 
 // Returns the |data_store_id| string in the required format.
diff --git a/components/subresource_filter/core/common/test_ruleset_creator.cc b/components/subresource_filter/core/common/test_ruleset_creator.cc
index 1fef23f..92fde645 100644
--- a/components/subresource_filter/core/common/test_ruleset_creator.cc
+++ b/components/subresource_filter/core/common/test_ruleset_creator.cc
@@ -10,6 +10,7 @@
 #include "base/check.h"
 #include "base/files/file_util.h"
 #include "base/strings/string_number_conversions.h"
+#include "base/strings/string_piece.h"
 #include "base/threading/thread_restrictions.h"
 #include "components/subresource_filter/core/common/indexed_ruleset.h"
 #include "components/subresource_filter/core/common/test_ruleset_utils.h"
@@ -155,7 +156,7 @@
   std::vector<proto::UrlRule> rules;
   for (int i = 0; i < num_of_suffixes; ++i) {
     std::string current_suffix =
-        suffix.as_string() + '_' + base::NumberToString(i);
+        std::string(suffix) + '_' + base::NumberToString(i);
     rules.push_back(CreateSuffixRule(current_suffix));
   }
   CreateRulesetWithRules(rules, test_ruleset_pair);
diff --git a/components/subresource_filter/core/common/test_ruleset_utils.cc b/components/subresource_filter/core/common/test_ruleset_utils.cc
index 2e13d78..a192895c 100644
--- a/components/subresource_filter/core/common/test_ruleset_utils.cc
+++ b/components/subresource_filter/core/common/test_ruleset_utils.cc
@@ -6,6 +6,8 @@
 
 #include <utility>
 
+#include "base/strings/string_piece.h"
+
 namespace subresource_filter {
 namespace testing {
 
@@ -33,7 +35,7 @@
   rule.set_url_pattern_type(proto::URL_PATTERN_TYPE_SUBSTRING);
   rule.set_anchor_left(proto::ANCHOR_TYPE_NONE);
   rule.set_anchor_right(proto::ANCHOR_TYPE_BOUNDARY);
-  rule.set_url_pattern(suffix.as_string());
+  rule.set_url_pattern(std::string(suffix));
   return rule;
 }
 
@@ -45,7 +47,7 @@
   rule.set_url_pattern_type(proto::URL_PATTERN_TYPE_SUBSTRING);
   rule.set_anchor_left(proto::ANCHOR_TYPE_NONE);
   rule.set_anchor_right(proto::ANCHOR_TYPE_BOUNDARY);
-  rule.set_url_pattern(suffix.as_string());
+  rule.set_url_pattern(std::string(suffix));
   return rule;
 }
 
@@ -65,7 +67,7 @@
   rule.set_url_pattern_type(proto::URL_PATTERN_TYPE_SUBSTRING);
   rule.set_anchor_left(proto::ANCHOR_TYPE_NONE);
   rule.set_anchor_right(proto::ANCHOR_TYPE_NONE);
-  rule.set_url_pattern(pattern.as_string());
+  rule.set_url_pattern(std::string(pattern));
   return rule;
 }
 
diff --git a/components/sync/driver/glue/sync_engine_backend.cc b/components/sync/driver/glue/sync_engine_backend.cc
index c305fb1..7419bf2 100644
--- a/components/sync/driver/glue/sync_engine_backend.cc
+++ b/components/sync/driver/glue/sync_engine_backend.cc
@@ -156,26 +156,16 @@
                                        invalidation::INVALIDATIONS_ENABLED);
 }
 
-bool SyncEngineBackend::ShouldIgnoreRedundantInvalidation(
+void SyncEngineBackend::RecordRedundantInvalidationsMetric(
     const invalidation::Invalidation& invalidation,
-    ModelType type) {
-  bool fcm_invalidation = base::FeatureList::IsEnabled(
-      invalidation::switches::kFCMInvalidationsForSyncDontCheckVersion);
-  bool redundant_invalidation = false;
+    ModelType type) const {
   auto last_invalidation = last_invalidation_versions_.find(type);
   if (!invalidation.is_unknown_version() &&
       last_invalidation != last_invalidation_versions_.end() &&
       invalidation.version() <= last_invalidation->second) {
-    DVLOG(1) << "Ignoring redundant invalidation for "
-             << ModelTypeToString(type) << " with version "
-             << invalidation.version() << ", last seen version was "
-             << last_invalidation->second;
-    redundant_invalidation = true;
     UMA_HISTOGRAM_ENUMERATION("Sync.RedundantInvalidationPerModelType2",
                               ModelTypeHistogramValue(type));
   }
-
-  return !fcm_invalidation && redundant_invalidation;
 }
 
 void SyncEngineBackend::DoOnIncomingInvalidation(
@@ -192,9 +182,7 @@
       invalidation::SingleTopicInvalidationSet invalidation_set =
           invalidation_map.ForTopic(topic);
       for (invalidation::Invalidation invalidation : invalidation_set) {
-        if (ShouldIgnoreRedundantInvalidation(invalidation, type)) {
-          continue;
-        }
+        RecordRedundantInvalidationsMetric(invalidation, type);
 
         std::unique_ptr<InvalidationInterface> inv_adapter(
             new InvalidationAdapter(invalidation));
diff --git a/components/sync/driver/glue/sync_engine_backend.h b/components/sync/driver/glue/sync_engine_backend.h
index adc230c4..60b6cae 100644
--- a/components/sync/driver/glue/sync_engine_backend.h
+++ b/components/sync/driver/glue/sync_engine_backend.h
@@ -185,11 +185,9 @@
 
   ~SyncEngineBackend() override;
 
-  // For the olg tango based invalidations method returns true if the
-  // invalidation has version lower than last seen version for this datatype.
-  bool ShouldIgnoreRedundantInvalidation(
+  void RecordRedundantInvalidationsMetric(
       const invalidation::Invalidation& invalidation,
-      ModelType Type);
+      ModelType Type) const;
 
   void LoadAndConnectNigoriController();
 
diff --git a/components/sync/engine/loopback_server/loopback_server_entity.cc b/components/sync/engine/loopback_server/loopback_server_entity.cc
index 34c1724..b66441c 100644
--- a/components/sync/engine/loopback_server/loopback_server_entity.cc
+++ b/components/sync/engine/loopback_server/loopback_server_entity.cc
@@ -11,6 +11,7 @@
 #include "base/memory/ref_counted.h"
 #include "base/notreached.h"
 #include "base/strings/string_number_conversions.h"
+#include "base/strings/string_piece.h"
 #include "base/strings/string_split.h"
 #include "base/strings/string_util.h"
 #include "base/strings/stringprintf.h"
@@ -150,7 +151,7 @@
     return std::string();
   }
 
-  return tokens[1].as_string();
+  return std::string(tokens[1]);
 }
 
 LoopbackServerEntity::LoopbackServerEntity(const string& id,
diff --git a/components/translate/core/browser/translate_accept_languages.cc b/components/translate/core/browser/translate_accept_languages.cc
index 6f15b92..c89339c 100644
--- a/components/translate/core/browser/translate_accept_languages.cc
+++ b/components/translate/core/browser/translate_accept_languages.cc
@@ -8,6 +8,7 @@
 
 #include "base/bind.h"
 #include "base/metrics/histogram_macros.h"
+#include "base/strings/string_piece.h"
 #include "base/strings/string_split.h"
 #include "base/strings/string_util.h"
 #include "components/language/core/common/language_util.h"
@@ -65,8 +66,8 @@
     // for which the CLD reports zh-CN and zh-TW.
     size_t index = lang.find('-');
     if (index != base::StringPiece::npos && lang != "zh-CN" && lang != "zh-TW")
-      accept_languages_.insert(lang.substr(0, index).as_string());
-    accept_languages_.insert(lang.as_string());
+      accept_languages_.insert(std::string(lang.substr(0, index)));
+    accept_languages_.insert(std::string(lang));
   }
 }
 
diff --git a/components/ui_devtools/viz/surface_element.cc b/components/ui_devtools/viz/surface_element.cc
index e374049..c2cad382 100644
--- a/components/ui_devtools/viz/surface_element.cc
+++ b/components/ui_devtools/viz/surface_element.cc
@@ -4,6 +4,7 @@
 
 #include "components/ui_devtools/viz/surface_element.h"
 
+#include "base/strings/string_piece.h"
 #include "components/ui_devtools/Protocol.h"
 #include "components/ui_devtools/ui_element_delegate.h"
 #include "components/viz/service/frame_sinks/frame_sink_manager_impl.h"
@@ -53,10 +54,9 @@
 void SurfaceElement::SetVisible(bool visible) {}
 
 std::vector<std::string> SurfaceElement::GetAttributes() const {
-  return {
-      "SurfaceId", surface_id_.ToString(), "FrameSink Debug Label",
-      frame_sink_manager_->GetFrameSinkDebugLabel(surface_id_.frame_sink_id())
-          .as_string()};
+  return {"SurfaceId", surface_id_.ToString(), "FrameSink Debug Label",
+          std::string(frame_sink_manager_->GetFrameSinkDebugLabel(
+              surface_id_.frame_sink_id()))};
 }
 
 std::pair<gfx::NativeWindow, gfx::Rect>
diff --git a/components/url_pattern_index/url_rule_test_support.cc b/components/url_pattern_index/url_rule_test_support.cc
index 5654e15..6ab0d06 100644
--- a/components/url_pattern_index/url_rule_test_support.cc
+++ b/components/url_pattern_index/url_rule_test_support.cc
@@ -3,6 +3,7 @@
 // found in the LICENSE file.
 
 #include "components/url_pattern_index/url_rule_test_support.h"
+#include "base/strings/string_piece.h"
 
 #include "base/check.h"
 #include "net/base/registry_controlled_domains/registry_controlled_domain.h"
@@ -23,7 +24,7 @@
   rule.set_anchor_left(url_pattern.anchor_left());
   rule.set_anchor_right(url_pattern.anchor_right());
   rule.set_match_case(url_pattern.match_case());
-  rule.set_url_pattern(url_pattern.url_pattern().as_string());
+  rule.set_url_pattern(std::string(url_pattern.url_pattern()));
 
   return rule;
 }
diff --git a/components/url_pattern_index/url_rule_util_unittest.cc b/components/url_pattern_index/url_rule_util_unittest.cc
index 69337da..3e41c2b 100644
--- a/components/url_pattern_index/url_rule_util_unittest.cc
+++ b/components/url_pattern_index/url_rule_util_unittest.cc
@@ -10,6 +10,7 @@
 #include <vector>
 
 #include "base/macros.h"
+#include "base/strings/string_piece.h"
 #include "components/url_pattern_index/flat/url_pattern_index_generated.h"
 #include "components/url_pattern_index/url_pattern.h"
 #include "components/url_pattern_index/url_pattern_index.h"
@@ -37,7 +38,7 @@
   rule.set_anchor_left(url_pattern.anchor_left());
   rule.set_anchor_right(url_pattern.anchor_right());
   rule.set_match_case(url_pattern.match_case());
-  rule.set_url_pattern(url_pattern.url_pattern().as_string());
+  rule.set_url_pattern(std::string(url_pattern.url_pattern()));
 
   testing::AddDomains(domains, &rule);
 
diff --git a/components/variations/active_field_trials.cc b/components/variations/active_field_trials.cc
index ae0fb9fd..7edcfabc 100644
--- a/components/variations/active_field_trials.cc
+++ b/components/variations/active_field_trials.cc
@@ -9,6 +9,7 @@
 #include <vector>
 
 #include "base/lazy_instance.h"
+#include "base/strings/string_piece.h"
 #include "base/strings/stringprintf.h"
 #include "base/strings/utf_string_conversions.h"
 #include "components/variations/hashing.h"
@@ -29,8 +30,8 @@
   DCHECK(name_group_ids->empty());
   for (auto it = active_groups.begin(); it != active_groups.end(); ++it) {
     name_group_ids->push_back(
-        MakeActiveGroupId(it->trial_name + suffix.as_string(),
-                          it->group_name + suffix.as_string()));
+        MakeActiveGroupId(it->trial_name + std::string(suffix),
+                          it->group_name + std::string(suffix)));
   }
 }
 
diff --git a/components/viz/common/quads/compositor_render_pass_unittest.cc b/components/viz/common/quads/compositor_render_pass_unittest.cc
index 0b83d642..d5eb08c0 100644
--- a/components/viz/common/quads/compositor_render_pass_unittest.cc
+++ b/components/viz/common/quads/compositor_render_pass_unittest.cc
@@ -86,7 +86,7 @@
   // Stick a quad in the pass, this should not get copied.
   SharedQuadState* shared_state = pass->CreateAndAppendSharedQuadState();
   shared_state->SetAll(gfx::Transform(), gfx::Rect(), gfx::Rect(),
-                       gfx::MaskFilterInfo(), gfx::Rect(), false, false, 1,
+                       gfx::MaskFilterInfo(), base::nullopt, false, 1,
                        SkBlendMode::kSrcOver, 0);
 
   auto* color_quad = pass->CreateAndAppendDrawQuad<SolidColorDrawQuad>();
@@ -145,7 +145,7 @@
   // Two quads using one shared state.
   SharedQuadState* shared_state1 = pass->CreateAndAppendSharedQuadState();
   shared_state1->SetAll(gfx::Transform(), gfx::Rect(0, 0, 1, 1), gfx::Rect(),
-                        gfx::MaskFilterInfo(), gfx::Rect(), false, false, 1,
+                        gfx::MaskFilterInfo(), base::nullopt, false, 1,
                         SkBlendMode::kSrcOver, 0);
 
   auto* color_quad1 = pass->CreateAndAppendDrawQuad<SolidColorDrawQuad>();
@@ -161,7 +161,7 @@
   // And two quads using another shared state.
   SharedQuadState* shared_state2 = pass->CreateAndAppendSharedQuadState();
   shared_state2->SetAll(gfx::Transform(), gfx::Rect(0, 0, 2, 2), gfx::Rect(),
-                        gfx::MaskFilterInfo(), gfx::Rect(), false, false, 1,
+                        gfx::MaskFilterInfo(), base::nullopt, false, 1,
                         SkBlendMode::kSrcOver, 0);
 
   auto* color_quad3 = pass->CreateAndAppendDrawQuad<SolidColorDrawQuad>();
@@ -201,9 +201,9 @@
 
   SharedQuadState* contrib_shared_state =
       contrib->CreateAndAppendSharedQuadState();
-  contrib_shared_state->SetAll(gfx::Transform(), gfx::Rect(0, 0, 2, 2),
-                               gfx::Rect(), gfx::MaskFilterInfo(), gfx::Rect(),
-                               false, false, 1, SkBlendMode::kSrcOver, 0);
+  contrib_shared_state->SetAll(
+      gfx::Transform(), gfx::Rect(0, 0, 2, 2), gfx::Rect(),
+      gfx::MaskFilterInfo(), base::nullopt, false, 1, SkBlendMode::kSrcOver, 0);
 
   auto* contrib_quad = contrib->CreateAndAppendDrawQuad<SolidColorDrawQuad>();
   contrib_quad->SetNew(contrib->shared_quad_state_list.back(),
@@ -255,7 +255,7 @@
   // A shared state with a quad.
   SharedQuadState* shared_state1 = pass->CreateAndAppendSharedQuadState();
   shared_state1->SetAll(gfx::Transform(), gfx::Rect(0, 0, 1, 1), gfx::Rect(),
-                        gfx::MaskFilterInfo(), gfx::Rect(), false, false, 1,
+                        gfx::MaskFilterInfo(), base::nullopt, false, 1,
                         SkBlendMode::kSrcOver, 0);
 
   auto* color_quad1 = pass->CreateAndAppendDrawQuad<SolidColorDrawQuad>();
@@ -266,19 +266,19 @@
   // A shared state with no quads, they were culled.
   SharedQuadState* shared_state2 = pass->CreateAndAppendSharedQuadState();
   shared_state2->SetAll(gfx::Transform(), gfx::Rect(0, 0, 2, 2), gfx::Rect(),
-                        gfx::MaskFilterInfo(), gfx::Rect(), false, false, 1,
+                        gfx::MaskFilterInfo(), base::nullopt, false, 1,
                         SkBlendMode::kSrcOver, 0);
 
   // A second shared state with no quads.
   SharedQuadState* shared_state3 = pass->CreateAndAppendSharedQuadState();
   shared_state3->SetAll(gfx::Transform(), gfx::Rect(0, 0, 2, 2), gfx::Rect(),
-                        gfx::MaskFilterInfo(), gfx::Rect(), false, false, 1,
+                        gfx::MaskFilterInfo(), base::nullopt, false, 1,
                         SkBlendMode::kSrcOver, 0);
 
   // A last shared state with a quad again.
   SharedQuadState* shared_state4 = pass->CreateAndAppendSharedQuadState();
   shared_state4->SetAll(gfx::Transform(), gfx::Rect(0, 0, 2, 2), gfx::Rect(),
-                        gfx::MaskFilterInfo(), gfx::Rect(), false, false, 1,
+                        gfx::MaskFilterInfo(), base::nullopt, false, 1,
                         SkBlendMode::kSrcOver, 0);
 
   auto* color_quad2 = pass->CreateAndAppendDrawQuad<SolidColorDrawQuad>();
diff --git a/components/viz/common/quads/draw_quad_perftest.cc b/components/viz/common/quads/draw_quad_perftest.cc
index 6b178ea..7b8f0ccc 100644
--- a/components/viz/common/quads/draw_quad_perftest.cc
+++ b/components/viz/common/quads/draw_quad_perftest.cc
@@ -38,8 +38,6 @@
   gfx::Transform quad_transform = gfx::Transform(1.0, 0.0, 0.5, 1.0, 0.5, 0.0);
   gfx::Rect content_rect(26, 28);
   gfx::Rect visible_layer_rect(10, 12, 14, 16);
-  gfx::Rect clip_rect(19, 21, 23, 25);
-  bool is_clipped = false;
   bool are_contents_opaque = false;
   float opacity = 1.f;
   int sorting_context_id = 65536;
@@ -47,7 +45,7 @@
 
   SharedQuadState* state = render_pass->CreateAndAppendSharedQuadState();
   state->SetAll(quad_transform, content_rect, visible_layer_rect,
-                gfx::MaskFilterInfo(), clip_rect, is_clipped,
+                gfx::MaskFilterInfo(), /*clip_rect=*/base::nullopt,
                 are_contents_opaque, opacity, blend_mode, sorting_context_id);
   return state;
 }
diff --git a/components/viz/common/quads/shared_quad_state.cc b/components/viz/common/quads/shared_quad_state.cc
index b33afa43..67f9fad0 100644
--- a/components/viz/common/quads/shared_quad_state.cc
+++ b/components/viz/common/quads/shared_quad_state.cc
@@ -25,22 +25,6 @@
                              const gfx::Rect& quad_layer_rect,
                              const gfx::Rect& visible_quad_layer_rect,
                              const gfx::MaskFilterInfo& mask_filter_info,
-                             const gfx::Rect& clip_rect,
-                             bool is_clipped,
-                             bool are_contents_opaque,
-                             float opacity,
-                             SkBlendMode blend_mode,
-                             int sorting_context_id) {
-  SetAll(quad_to_target_transform, quad_layer_rect, visible_quad_layer_rect,
-         mask_filter_info,
-         is_clipped ? base::make_optional(clip_rect) : base::nullopt,
-         are_contents_opaque, opacity, blend_mode, sorting_context_id);
-}
-
-void SharedQuadState::SetAll(const gfx::Transform& quad_to_target_transform,
-                             const gfx::Rect& quad_layer_rect,
-                             const gfx::Rect& visible_quad_layer_rect,
-                             const gfx::MaskFilterInfo& mask_filter_info,
                              const base::Optional<gfx::Rect>& clip_rect,
                              bool are_contents_opaque,
                              float opacity,
diff --git a/components/viz/common/quads/shared_quad_state.h b/components/viz/common/quads/shared_quad_state.h
index abf10b9..bad6c8a 100644
--- a/components/viz/common/quads/shared_quad_state.h
+++ b/components/viz/common/quads/shared_quad_state.h
@@ -34,22 +34,6 @@
   SharedQuadState(const SharedQuadState& other);
   ~SharedQuadState();
 
-  // If the provided is_clipped is true, this method will set clip_rect to
-  // nullopt, otherwise it will be set to the provided clip_rect.
-  // New usages should use the next SetAll method which accepts clip_rect
-  // as an Optional.
-  // TODO(crbug/1194630): Delete this function after all callsites have been
-  // updated to use the Optional version.
-  void SetAll(const gfx::Transform& quad_to_target_transform,
-              const gfx::Rect& quad_layer_rect,
-              const gfx::Rect& visible_layer_rect,
-              const gfx::MaskFilterInfo& mask_filter_info,
-              const gfx::Rect& clip_rect,
-              bool is_clipped,
-              bool are_contents_opaque,
-              float opacity,
-              SkBlendMode blend_mode,
-              int sorting_context_id);
   void SetAll(const gfx::Transform& quad_to_target_transform,
               const gfx::Rect& quad_layer_rect,
               const gfx::Rect& visible_layer_rect,
diff --git a/components/viz/common/surfaces/frame_sink_id.cc b/components/viz/common/surfaces/frame_sink_id.cc
index 1cc2a10..553d3e6 100644
--- a/components/viz/common/surfaces/frame_sink_id.cc
+++ b/components/viz/common/surfaces/frame_sink_id.cc
@@ -4,6 +4,7 @@
 
 #include "components/viz/common/surfaces/frame_sink_id.h"
 
+#include "base/strings/string_piece.h"
 #include "base/strings/stringprintf.h"
 
 namespace viz {
@@ -14,7 +15,7 @@
 
 std::string FrameSinkId::ToString(base::StringPiece debug_label) const {
   return base::StringPrintf("FrameSinkId[%s](%u, %u)",
-                            debug_label.as_string().c_str(), client_id_,
+                            std::string(debug_label).c_str(), client_id_,
                             sink_id_);
 }
 
diff --git a/components/viz/service/compositor_frame_fuzzer/compositor_frame_fuzzer_util.cc b/components/viz/service/compositor_frame_fuzzer/compositor_frame_fuzzer_util.cc
index 226e84a89..7dc0b552 100644
--- a/components/viz/service/compositor_frame_fuzzer/compositor_frame_fuzzer_util.cc
+++ b/components/viz/service/compositor_frame_fuzzer/compositor_frame_fuzzer_util.cc
@@ -341,12 +341,15 @@
     SharedQuadState* shared_quad_state,
     const proto::DrawQuad& quad_spec) {
   if (quad_spec.has_sqs()) {
+    base::Optional<gfx::Rect> clip_rect;
+    if (quad_spec.sqs().is_clipped()) {
+      clip_rect = GetRectFromProtobuf(quad_spec.sqs().clip_rect());
+    }
     shared_quad_state->SetAll(
         GetTransformFromProtobuf(quad_spec.sqs().transform()),
         GetRectFromProtobuf(quad_spec.sqs().layer_rect()),
         GetRectFromProtobuf(quad_spec.sqs().visible_rect()),
-        gfx::MaskFilterInfo(), GetRectFromProtobuf(quad_spec.sqs().clip_rect()),
-        quad_spec.sqs().is_clipped(), quad_spec.sqs().are_contents_opaque(),
+        gfx::MaskFilterInfo(), clip_rect, quad_spec.sqs().are_contents_opaque(),
         Normalize(quad_spec.sqs().opacity()), SkBlendMode::kSrcOver,
         quad_spec.sqs().sorting_context_id());
   } else {
@@ -364,9 +367,8 @@
     shared_quad_state->SetAll(
         transform, GetRectFromProtobuf(quad_spec.rect()),
         GetRectFromProtobuf(quad_spec.visible_rect()), gfx::MaskFilterInfo(),
-        gfx::Rect(), /*is_clipped=*/false,
-        /*are_contents_opaque=*/true, /*opacity=*/1.0, SkBlendMode::kSrcOver,
-        /*sorting_context_id=*/0);
+        /*clip_rect=*/base::nullopt, /*are_contents_opaque=*/true,
+        /*opacity=*/1.0, SkBlendMode::kSrcOver, /*sorting_context_id=*/0);
   }
 }
 
diff --git a/components/viz/service/compositor_frame_fuzzer/fuzzer_browser_process.cc b/components/viz/service/compositor_frame_fuzzer/fuzzer_browser_process.cc
index de70a88d..c08bef7 100644
--- a/components/viz/service/compositor_frame_fuzzer/fuzzer_browser_process.cc
+++ b/components/viz/service/compositor_frame_fuzzer/fuzzer_browser_process.cc
@@ -131,7 +131,7 @@
                        gfx::Rect(kRendererFrameSize),
                        gfx::Rect(kRendererFrameSize),
                        /*mask_filter_info=*/gfx::MaskFilterInfo(),
-                       gfx::Rect(kRendererFrameSize), /*is_clipped=*/false,
+                       /*clip_rect=*/base::nullopt,
                        /*are_contents_opaque=*/false, /*opacity=*/1,
                        SkBlendMode::kSrcOver, /*sorting_context_id=*/0);
   auto* surface_quad = pass->CreateAndAppendDrawQuad<SurfaceDrawQuad>();
@@ -144,9 +144,8 @@
   auto* toolbar_sqs = pass->CreateAndAppendSharedQuadState();
   toolbar_sqs->SetAll(
       gfx::Transform(), gfx::Rect(kTopBarSize), gfx::Rect(kTopBarSize),
-      /*mask_filter_info=*/gfx::MaskFilterInfo(), gfx::Rect(kTopBarSize),
-      /*is_clipped=*/false, /*are_contents_opaque=*/false,
-      /*opacity=*/1, SkBlendMode::kSrcOver,
+      /*mask_filter_info=*/gfx::MaskFilterInfo(), /*clip_rect=*/base::nullopt,
+      /*are_contents_opaque=*/false, /*opacity=*/1, SkBlendMode::kSrcOver,
       /*sorting_context_id=*/0);
   auto* color_quad = pass->CreateAndAppendDrawQuad<SolidColorDrawQuad>();
   color_quad->SetNew(toolbar_sqs, gfx::Rect(kTopBarSize),
diff --git a/components/viz/service/display/display_perftest.cc b/components/viz/service/display/display_perftest.cc
index 22f2bf90..9444257f 100644
--- a/components/viz/service/display/display_perftest.cc
+++ b/components/viz/service/display/display_perftest.cc
@@ -90,7 +90,6 @@
   SharedQuadState* CreateSharedQuadState(AggregatedRenderPass* render_pass,
                                          gfx::Rect rect) {
     gfx::Transform quad_transform = gfx::Transform();
-    bool is_clipped = false;
     bool are_contents_opaque = true;
     float opacity = 1.f;
     int sorting_context_id = 65536;
@@ -98,8 +97,9 @@
 
     SharedQuadState* state = render_pass->CreateAndAppendSharedQuadState();
     state->SetAll(quad_transform, rect, rect,
-                  /*mask_filter_info=*/gfx::MaskFilterInfo(), rect, is_clipped,
-                  are_contents_opaque, opacity, blend_mode, sorting_context_id);
+                  /*mask_filter_info=*/gfx::MaskFilterInfo(),
+                  /*clip_rect=*/base::nullopt, are_contents_opaque, opacity,
+                  blend_mode, sorting_context_id);
     return state;
   }
 
diff --git a/components/viz/service/display/display_unittest.cc b/components/viz/service/display/display_unittest.cc
index 9aead51..6c4821f 100644
--- a/components/viz/service/display/display_unittest.cc
+++ b/components/viz/service/display/display_unittest.cc
@@ -786,9 +786,8 @@
           gfx::Transform(), /*quad_layer_rect=*/sub_surface_rect,
           /*visible_quad_layer_rect=*/sub_surface_rect,
           /*mask_filter_info=*/gfx::MaskFilterInfo(),
-          /*clip_rect=*/sub_surface_rect, /*is_clipped=*/false,
-          /*are_contents_opaque=*/true, /*opacity=*/1.0f, SkBlendMode::kSrcOver,
-          /*sorting_context_id=*/0);
+          /*clip_rect=*/base::nullopt, /*are_contents_opaque=*/true,
+          /*opacity=*/1.0f, SkBlendMode::kSrcOver, /*sorting_context_id=*/0);
       auto* quad1 = pass->quad_list.AllocateAndConstruct<SurfaceDrawQuad>();
       quad1->SetNew(shared_quad_state1, /*rect=*/sub_surface_rect,
                     /*visible_rect=*/sub_surface_rect,
@@ -802,7 +801,7 @@
       shared_quad_state2->SetAll(gfx::Transform(), /*quad_layer_rect=*/rect1,
                                  /*visible_quad_layer_rect=*/rect1,
                                  /*mask_filter_info=*/gfx::MaskFilterInfo(),
-                                 /*clip_rect=*/rect1, /*is_clipped=*/false,
+                                 /*clip_rect=*/base::nullopt,
                                  /*are_contents_opaque=*/true, /*opacity=*/1.0f,
                                  SkBlendMode::kSrcOver,
                                  /*sorting_context_id=*/0);
@@ -929,7 +928,6 @@
   display_->Initialize(&client, manager_.surface_manager());
   AggregatedFrame frame = MakeDefaultAggregatedFrame(/*num_render_passes=*/2);
 
-  bool is_clipped = false;
   bool are_contents_opaque = true;
   float opacity = 1.f;
 
@@ -941,13 +939,13 @@
 
     auto* src_sqs = render_pass->CreateAndAppendSharedQuadState();
     src_sqs->SetAll(
-        gfx::Transform(), src_rect, src_rect, gfx::MaskFilterInfo(), src_rect,
-        is_clipped, are_contents_opaque, opacity,
+        gfx::Transform(), src_rect, src_rect, gfx::MaskFilterInfo(),
+        base::nullopt, are_contents_opaque, opacity,
         is_root_render_pass ? SkBlendMode::kSrcOver : SkBlendMode::kSrcIn, 0);
     auto* dest_sqs = render_pass->CreateAndAppendSharedQuadState();
     dest_sqs->SetAll(
         gfx::Transform(), dest_rect, dest_rect, gfx::MaskFilterInfo(),
-        dest_rect, is_clipped, are_contents_opaque, opacity,
+        base::nullopt, are_contents_opaque, opacity,
         is_root_render_pass ? SkBlendMode::kSrcOver : SkBlendMode::kDstIn, 0);
     auto* src_quad =
         render_pass->quad_list.AllocateAndConstruct<SolidColorDrawQuad>();
@@ -977,7 +975,6 @@
   display_->Initialize(&client, manager_.surface_manager());
   AggregatedFrame frame = MakeDefaultAggregatedFrame(/*num_render_passes=*/2);
 
-  bool is_clipped = false;
   bool are_contents_opaque = true;
   float opacity = 1.f;
 
@@ -1007,8 +1004,8 @@
   for (int i = 0; i < 3; i++) {
     shared_quad_states[i] = root_render_pass->CreateAndAppendSharedQuadState();
     shared_quad_states[i]->SetAll(
-        gfx::Transform(), rects[i], rects[i], gfx::MaskFilterInfo(), rects[i],
-        is_clipped, are_contents_opaque, opacity, SkBlendMode::kSrcOver, 0);
+        gfx::Transform(), rects[i], rects[i], gfx::MaskFilterInfo(),
+        base::nullopt, are_contents_opaque, opacity, SkBlendMode::kSrcOver, 0);
 
     if (i == 0) {  // Backdrop filter quad
       auto* new_quad =
@@ -1060,7 +1057,6 @@
   gfx::Rect rect6(25, 0, 50, 160);
   gfx::Rect rect7(0, 20, 100, 100);
 
-  bool is_clipped = false;
   bool are_contents_opaque = true;
   float opacity = 1.f;
   SharedQuadState* shared_quad_state =
@@ -1073,8 +1069,8 @@
   // +----+
   {
     shared_quad_state->SetAll(
-        gfx::Transform(), rect1, rect1, gfx::MaskFilterInfo(), rect1,
-        is_clipped, are_contents_opaque, opacity, SkBlendMode::kSrcOver, 0);
+        gfx::Transform(), rect1, rect1, gfx::MaskFilterInfo(), base::nullopt,
+        are_contents_opaque, opacity, SkBlendMode::kSrcOver, 0);
 
     quad->SetNew(shared_quad_state, rect1, rect1, SK_ColorBLACK, false);
     EXPECT_EQ(1u, NumVisibleRects(frame.render_pass_list.front()->quad_list));
@@ -1098,12 +1094,12 @@
   //   +----+
   {
     shared_quad_state->SetAll(
-        gfx::Transform(), rect1, rect1, gfx::MaskFilterInfo(), rect1,
-        is_clipped, are_contents_opaque, opacity, SkBlendMode::kSrcOver, 0);
+        gfx::Transform(), rect1, rect1, gfx::MaskFilterInfo(), base::nullopt,
+        are_contents_opaque, opacity, SkBlendMode::kSrcOver, 0);
 
     shared_quad_state2->SetAll(
-        gfx::Transform(), rect2, rect2, gfx::MaskFilterInfo(), rect2,
-        is_clipped, are_contents_opaque, opacity, SkBlendMode::kSrcOver, 0);
+        gfx::Transform(), rect2, rect2, gfx::MaskFilterInfo(), base::nullopt,
+        are_contents_opaque, opacity, SkBlendMode::kSrcOver, 0);
 
     quad->SetNew(shared_quad_state, rect1, rect1, SK_ColorBLACK, false);
     quad2->SetNew(shared_quad_state2, rect2, rect2, SK_ColorBLACK, false);
@@ -1133,12 +1129,12 @@
   //   +--+                                    +--+
   {
     shared_quad_state->SetAll(
-        gfx::Transform(), rect1, rect1, gfx::MaskFilterInfo(), rect1,
-        is_clipped, are_contents_opaque, opacity, SkBlendMode::kSrcOver, 0);
+        gfx::Transform(), rect1, rect1, gfx::MaskFilterInfo(), base::nullopt,
+        are_contents_opaque, opacity, SkBlendMode::kSrcOver, 0);
 
     shared_quad_state2->SetAll(
-        gfx::Transform(), rect3, rect3, gfx::MaskFilterInfo(), rect3,
-        is_clipped, are_contents_opaque, opacity, SkBlendMode::kSrcOver, 0);
+        gfx::Transform(), rect3, rect3, gfx::MaskFilterInfo(), base::nullopt,
+        are_contents_opaque, opacity, SkBlendMode::kSrcOver, 0);
 
     quad->SetNew(shared_quad_state, rect1, rect1, SK_ColorBLACK, false);
     quad2->SetNew(shared_quad_state2, rect3, rect3, SK_ColorBLACK, false);
@@ -1167,12 +1163,12 @@
   //  +--+                                        +--+
   {
     shared_quad_state->SetAll(
-        gfx::Transform(), rect7, rect7, gfx::MaskFilterInfo(), rect7,
-        is_clipped, are_contents_opaque, opacity, SkBlendMode::kSrcOver, 0);
+        gfx::Transform(), rect7, rect7, gfx::MaskFilterInfo(), base::nullopt,
+        are_contents_opaque, opacity, SkBlendMode::kSrcOver, 0);
 
     shared_quad_state2->SetAll(
-        gfx::Transform(), rect6, rect6, gfx::MaskFilterInfo(), rect6,
-        is_clipped, are_contents_opaque, opacity, SkBlendMode::kSrcOver, 0);
+        gfx::Transform(), rect6, rect6, gfx::MaskFilterInfo(), base::nullopt,
+        are_contents_opaque, opacity, SkBlendMode::kSrcOver, 0);
 
     quad->SetNew(shared_quad_state, rect7, rect7, SK_ColorBLACK, false);
     quad2->SetNew(shared_quad_state2, rect6, rect6, SK_ColorBLACK, false);
@@ -1200,12 +1196,12 @@
   // +----+
   {
     shared_quad_state->SetAll(
-        gfx::Transform(), rect1, rect1, gfx::MaskFilterInfo(), rect1,
-        is_clipped, are_contents_opaque, opacity, SkBlendMode::kSrcOver, 0);
+        gfx::Transform(), rect1, rect1, gfx::MaskFilterInfo(), base::nullopt,
+        are_contents_opaque, opacity, SkBlendMode::kSrcOver, 0);
 
     shared_quad_state2->SetAll(
-        gfx::Transform(), rect4, rect4, gfx::MaskFilterInfo(), rect4,
-        is_clipped, are_contents_opaque, opacity, SkBlendMode::kSrcOver, 0);
+        gfx::Transform(), rect4, rect4, gfx::MaskFilterInfo(), base::nullopt,
+        are_contents_opaque, opacity, SkBlendMode::kSrcOver, 0);
 
     quad->SetNew(shared_quad_state, rect1, rect1, SK_ColorBLACK, false);
     quad2->SetNew(shared_quad_state2, rect4, rect4, SK_ColorBLACK, false);
@@ -1225,19 +1221,18 @@
                                     ->quad_list.ElementAt(1)
                                     ->visible_rect.ToString());
   }
-
   // +-----++
   // |     ||
   // +-----+|
   // +------+
   {
     shared_quad_state->SetAll(
-        gfx::Transform(), rect1, rect1, gfx::MaskFilterInfo(), rect1,
-        is_clipped, are_contents_opaque, opacity, SkBlendMode::kSrcOver, 0);
+        gfx::Transform(), rect1, rect1, gfx::MaskFilterInfo(), base::nullopt,
+        are_contents_opaque, opacity, SkBlendMode::kSrcOver, 0);
 
     shared_quad_state2->SetAll(
-        gfx::Transform(), rect5, rect5, gfx::MaskFilterInfo(), rect5,
-        is_clipped, are_contents_opaque, opacity, SkBlendMode::kSrcOver, 0);
+        gfx::Transform(), rect5, rect5, gfx::MaskFilterInfo(), base::nullopt,
+        are_contents_opaque, opacity, SkBlendMode::kSrcOver, 0);
 
     quad->SetNew(shared_quad_state, rect1, rect1, SK_ColorBLACK, false);
     quad2->SetNew(shared_quad_state2, rect5, rect5, SK_ColorBLACK, false);
@@ -1280,7 +1275,6 @@
   rects.emplace_back(150, 0, 150, 150);
   rects.emplace_back(25, 25, 50, 50);
 
-  bool is_clipped = false;
   bool are_contents_opaque = true;
   float opacity = 1.f;
 
@@ -1290,7 +1284,7 @@
     auto* quad = frame.render_pass_list.front()
                      ->quad_list.AllocateAndConstruct<SolidColorDrawQuad>();
     shared_quad_state->SetAll(
-        gfx::Transform(), rect, rect, gfx::MaskFilterInfo(), rect, is_clipped,
+        gfx::Transform(), rect, rect, gfx::MaskFilterInfo(), base::nullopt,
         are_contents_opaque, opacity, SkBlendMode::kSrcOver, 0);
     quad->SetNew(shared_quad_state, rect, rect, SK_ColorBLACK, false);
   }
@@ -1336,7 +1330,6 @@
   rects.emplace_back(25, 25, 50, 50);
   rects.emplace_back(150, 0, 100, 100);
 
-  bool is_clipped = false;
   bool are_contents_opaque = true;
   float opacity = 1.f;
 
@@ -1346,7 +1339,7 @@
     auto* quad = frame.render_pass_list.front()
                      ->quad_list.AllocateAndConstruct<SolidColorDrawQuad>();
     shared_quad_state->SetAll(
-        gfx::Transform(), rect, rect, gfx::MaskFilterInfo(), rect, is_clipped,
+        gfx::Transform(), rect, rect, gfx::MaskFilterInfo(), base::nullopt,
         are_contents_opaque, opacity, SkBlendMode::kSrcOver, 0);
     quad->SetNew(shared_quad_state, rect, rect, SK_ColorBLACK, false);
   }
@@ -1388,7 +1381,6 @@
   gfx::Rect rect3(50, 50, 50, 25);
   gfx::Rect rect4(0, 0, 50, 50);
 
-  bool is_clipped = false;
   bool are_contents_opaque = true;
   float opacity = 1.f;
   SharedQuadState* shared_quad_state =
@@ -1405,11 +1397,11 @@
   //                         +-----+
   {
     shared_quad_state->SetAll(
-        gfx::Transform(), rect1, rect1, gfx::MaskFilterInfo(), rect1,
-        is_clipped, are_contents_opaque, opacity, SkBlendMode::kSrcOver, 0);
+        gfx::Transform(), rect1, rect1, gfx::MaskFilterInfo(), base::nullopt,
+        are_contents_opaque, opacity, SkBlendMode::kSrcOver, 0);
     shared_quad_state2->SetAll(
-        gfx::Transform(), rect1, rect1, gfx::MaskFilterInfo(), rect1,
-        is_clipped, are_contents_opaque, opacity, SkBlendMode::kSrcOver, 0);
+        gfx::Transform(), rect1, rect1, gfx::MaskFilterInfo(), base::nullopt,
+        are_contents_opaque, opacity, SkBlendMode::kSrcOver, 0);
 
     quad->SetNew(shared_quad_state, rect1, rect1, SK_ColorBLACK, false);
     quad2->SetNew(shared_quad_state2, rect1, rect1, SK_ColorBLACK, false);
@@ -1421,7 +1413,6 @@
                                     ->quad_list.ElementAt(0)
                                     ->visible_rect.ToString());
   }
-
   //  +-----+
   //  | +-+ |
   //  | +-+ |
@@ -1430,11 +1421,11 @@
     quad2 = frame.render_pass_list.front()
                 ->quad_list.AllocateAndConstruct<SolidColorDrawQuad>();
     shared_quad_state->SetAll(
-        gfx::Transform(), rect1, rect1, gfx::MaskFilterInfo(), rect1,
-        is_clipped, are_contents_opaque, opacity, SkBlendMode::kSrcOver, 0);
+        gfx::Transform(), rect1, rect1, gfx::MaskFilterInfo(), base::nullopt,
+        are_contents_opaque, opacity, SkBlendMode::kSrcOver, 0);
     shared_quad_state2->SetAll(
-        gfx::Transform(), rect2, rect2, gfx::MaskFilterInfo(), rect2,
-        is_clipped, are_contents_opaque, opacity, SkBlendMode::kSrcOver, 0);
+        gfx::Transform(), rect2, rect2, gfx::MaskFilterInfo(), base::nullopt,
+        are_contents_opaque, opacity, SkBlendMode::kSrcOver, 0);
 
     quad->SetNew(shared_quad_state, rect1, rect1, SK_ColorBLACK, false);
     quad2->SetNew(shared_quad_state2, rect2, rect2, SK_ColorBLACK, false);
@@ -1456,11 +1447,11 @@
     quad2 = frame.render_pass_list.front()
                 ->quad_list.AllocateAndConstruct<SolidColorDrawQuad>();
     shared_quad_state->SetAll(
-        gfx::Transform(), rect1, rect1, gfx::MaskFilterInfo(), rect1,
-        is_clipped, are_contents_opaque, opacity, SkBlendMode::kSrcOver, 0);
+        gfx::Transform(), rect1, rect1, gfx::MaskFilterInfo(), base::nullopt,
+        are_contents_opaque, opacity, SkBlendMode::kSrcOver, 0);
     shared_quad_state2->SetAll(
-        gfx::Transform(), rect3, rect3, gfx::MaskFilterInfo(), rect3,
-        is_clipped, are_contents_opaque, opacity, SkBlendMode::kSrcOver, 0);
+        gfx::Transform(), rect3, rect3, gfx::MaskFilterInfo(), base::nullopt,
+        are_contents_opaque, opacity, SkBlendMode::kSrcOver, 0);
 
     quad->SetNew(shared_quad_state, rect1, rect1, SK_ColorBLACK, false);
     quad2->SetNew(shared_quad_state2, rect3, rect3, SK_ColorBLACK, false);
@@ -1482,12 +1473,12 @@
     quad2 = frame.render_pass_list.front()
                 ->quad_list.AllocateAndConstruct<SolidColorDrawQuad>();
     shared_quad_state->SetAll(
-        gfx::Transform(), rect1, rect1, gfx::MaskFilterInfo(), rect1,
-        is_clipped, are_contents_opaque, opacity, SkBlendMode::kSrcOver, 0);
+        gfx::Transform(), rect1, rect1, gfx::MaskFilterInfo(), base::nullopt,
+        are_contents_opaque, opacity, SkBlendMode::kSrcOver, 0);
 
     shared_quad_state2->SetAll(
-        gfx::Transform(), rect4, rect4, gfx::MaskFilterInfo(), rect4,
-        is_clipped, are_contents_opaque, opacity, SkBlendMode::kSrcOver, 0);
+        gfx::Transform(), rect4, rect4, gfx::MaskFilterInfo(), base::nullopt,
+        are_contents_opaque, opacity, SkBlendMode::kSrcOver, 0);
 
     quad->SetNew(shared_quad_state, rect1, rect1, SK_ColorBLACK, false);
     quad2->SetNew(shared_quad_state2, rect4, rect4, SK_ColorBLACK, false);
@@ -1533,7 +1524,6 @@
   half_scale.Scale3d(0.5, 0.5, 0.5);
   gfx::Transform double_scale;
   double_scale.Scale(2, 2);
-  bool is_clipped = false;
   bool are_contents_opaque = true;
   float opacity = 1.f;
   SharedQuadState* shared_quad_state =
@@ -1547,10 +1537,10 @@
 
   {
     shared_quad_state->SetAll(
-        gfx::Transform(), rect1, rect1, gfx::MaskFilterInfo(), rect1,
-        is_clipped, are_contents_opaque, opacity, SkBlendMode::kSrcOver, 0);
+        gfx::Transform(), rect1, rect1, gfx::MaskFilterInfo(), base::nullopt,
+        are_contents_opaque, opacity, SkBlendMode::kSrcOver, 0);
     shared_quad_state2->SetAll(half_scale, rect2, rect2, gfx::MaskFilterInfo(),
-                               rect2, is_clipped, are_contents_opaque, opacity,
+                               base::nullopt, are_contents_opaque, opacity,
                                SkBlendMode::kSrcOver, 0);
 
     quad->SetNew(shared_quad_state, rect1, rect1, SK_ColorBLACK, false);
@@ -1570,10 +1560,10 @@
     quad2 = frame.render_pass_list.front()
                 ->quad_list.AllocateAndConstruct<SolidColorDrawQuad>();
     shared_quad_state->SetAll(
-        gfx::Transform(), rect1, rect1, gfx::MaskFilterInfo(), rect1,
-        is_clipped, are_contents_opaque, opacity, SkBlendMode::kSrcOver, 0);
+        gfx::Transform(), rect1, rect1, gfx::MaskFilterInfo(), base::nullopt,
+        are_contents_opaque, opacity, SkBlendMode::kSrcOver, 0);
     shared_quad_state2->SetAll(half_scale, rect3, rect3, gfx::MaskFilterInfo(),
-                               rect3, is_clipped, are_contents_opaque, opacity,
+                               base::nullopt, are_contents_opaque, opacity,
                                SkBlendMode::kSrcOver, 0);
 
     quad->SetNew(shared_quad_state, rect1, rect1, SK_ColorBLACK, false);
@@ -1593,11 +1583,11 @@
     quad2 = frame.render_pass_list.front()
                 ->quad_list.AllocateAndConstruct<SolidColorDrawQuad>();
     shared_quad_state->SetAll(
-        gfx::Transform(), rect1, rect1, gfx::MaskFilterInfo(), rect1,
-        is_clipped, are_contents_opaque, opacity, SkBlendMode::kSrcOver, 0);
+        gfx::Transform(), rect1, rect1, gfx::MaskFilterInfo(), base::nullopt,
+        are_contents_opaque, opacity, SkBlendMode::kSrcOver, 0);
 
     shared_quad_state2->SetAll(half_scale, rect4, rect4, gfx::MaskFilterInfo(),
-                               rect4, is_clipped, are_contents_opaque, opacity,
+                               base::nullopt, are_contents_opaque, opacity,
                                SkBlendMode::kSrcOver, 0);
 
     quad->SetNew(shared_quad_state, rect1, rect1, SK_ColorBLACK, false);
@@ -1616,7 +1606,7 @@
 
   {
     shared_quad_state->SetAll(double_scale, rect1, rect1, gfx::MaskFilterInfo(),
-                              rect1, is_clipped, are_contents_opaque, opacity,
+                              base::nullopt, are_contents_opaque, opacity,
                               SkBlendMode::kSrcOver, 0);
 
     quad->SetNew(shared_quad_state, rect1, rect1, SK_ColorBLACK, false);
@@ -1634,11 +1624,11 @@
     quad2 = frame.render_pass_list.front()
                 ->quad_list.AllocateAndConstruct<SolidColorDrawQuad>();
     shared_quad_state->SetAll(
-        gfx::Transform(), rect1, rect1, gfx::MaskFilterInfo(), rect1,
-        is_clipped, are_contents_opaque, opacity, SkBlendMode::kSrcOver, 0);
+        gfx::Transform(), rect1, rect1, gfx::MaskFilterInfo(), base::nullopt,
+        are_contents_opaque, opacity, SkBlendMode::kSrcOver, 0);
 
     shared_quad_state2->SetAll(
-        double_scale, rect5, rect5, gfx::MaskFilterInfo(), rect5, is_clipped,
+        double_scale, rect5, rect5, gfx::MaskFilterInfo(), base::nullopt,
         are_contents_opaque, opacity, SkBlendMode::kSrcOver, 0);
 
     quad->SetNew(shared_quad_state, rect1, rect1, SK_ColorBLACK, false);
@@ -1662,11 +1652,11 @@
 
   {
     shared_quad_state->SetAll(
-        gfx::Transform(), rect1, rect1, gfx::MaskFilterInfo(), rect1,
-        is_clipped, are_contents_opaque, opacity, SkBlendMode::kSrcOver, 0);
+        gfx::Transform(), rect1, rect1, gfx::MaskFilterInfo(), base::nullopt,
+        are_contents_opaque, opacity, SkBlendMode::kSrcOver, 0);
 
     shared_quad_state2->SetAll(
-        double_scale, rect6, rect6, gfx::MaskFilterInfo(), rect6, is_clipped,
+        double_scale, rect6, rect6, gfx::MaskFilterInfo(), base::nullopt,
         are_contents_opaque, opacity, SkBlendMode::kSrcOver, 0);
 
     quad->SetNew(shared_quad_state, rect1, rect1, SK_ColorBLACK, false);
@@ -1691,11 +1681,11 @@
 
   {
     shared_quad_state->SetAll(
-        gfx::Transform(), rect1, rect1, gfx::MaskFilterInfo(), rect1,
-        is_clipped, are_contents_opaque, opacity, SkBlendMode::kSrcOver, 0);
+        gfx::Transform(), rect1, rect1, gfx::MaskFilterInfo(), base::nullopt,
+        are_contents_opaque, opacity, SkBlendMode::kSrcOver, 0);
 
     shared_quad_state2->SetAll(
-        double_scale, rect7, rect7, gfx::MaskFilterInfo(), rect7, is_clipped,
+        double_scale, rect7, rect7, gfx::MaskFilterInfo(), base::nullopt,
         are_contents_opaque, opacity, SkBlendMode::kSrcOver, 0);
 
     quad->SetNew(shared_quad_state, rect1, rect1, SK_ColorBLACK, false);
@@ -1719,11 +1709,11 @@
 
   {
     shared_quad_state->SetAll(
-        gfx::Transform(), rect1, rect1, gfx::MaskFilterInfo(), rect1,
-        is_clipped, are_contents_opaque, opacity, SkBlendMode::kSrcOver, 0);
+        gfx::Transform(), rect1, rect1, gfx::MaskFilterInfo(), base::nullopt,
+        are_contents_opaque, opacity, SkBlendMode::kSrcOver, 0);
 
     shared_quad_state2->SetAll(
-        double_scale, rect8, rect8, gfx::MaskFilterInfo(), rect8, is_clipped,
+        double_scale, rect8, rect8, gfx::MaskFilterInfo(), base::nullopt,
         are_contents_opaque, opacity, SkBlendMode::kSrcOver, 0);
 
     quad->SetNew(shared_quad_state, rect1, rect1, SK_ColorBLACK, false);
@@ -1747,11 +1737,11 @@
 
   {
     shared_quad_state->SetAll(
-        double_scale, rect10, rect10, gfx::MaskFilterInfo(), rect10, is_clipped,
+        double_scale, rect10, rect10, gfx::MaskFilterInfo(), base::nullopt,
         are_contents_opaque, opacity, SkBlendMode::kSrcOver, 0);
 
     shared_quad_state2->SetAll(
-        double_scale, rect9, rect9, gfx::MaskFilterInfo(), rect9, is_clipped,
+        double_scale, rect9, rect9, gfx::MaskFilterInfo(), base::nullopt,
         are_contents_opaque, opacity, SkBlendMode::kSrcOver, 0);
 
     quad->SetNew(shared_quad_state, rect10, rect10, SK_ColorBLACK, false);
@@ -1792,7 +1782,6 @@
   epsilon_scale.Scale(epsilon, epsilon);
   gfx::Transform larger_epsilon_scale;
   larger_epsilon_scale.Scale(larger_than_epsilon, larger_than_epsilon);
-  bool is_clipped = false;
   bool are_contents_opaque = true;
   float opacity = 1.f;
   SharedQuadState* shared_quad_state =
@@ -1807,10 +1796,10 @@
 
   {
     shared_quad_state->SetAll(
-        gfx::Transform(), rect, rect, gfx::MaskFilterInfo(), rect, is_clipped,
+        gfx::Transform(), rect, rect, gfx::MaskFilterInfo(), base::nullopt,
         are_contents_opaque, opacity, SkBlendMode::kSrcOver, 0);
     shared_quad_state2->SetAll(zero_scale, rect, rect, gfx::MaskFilterInfo(),
-                               rect, is_clipped, are_contents_opaque, opacity,
+                               base::nullopt, are_contents_opaque, opacity,
                                SkBlendMode::kSrcOver, 0);
 
     quad->SetNew(shared_quad_state, rect, rect, SK_ColorBLACK, false);
@@ -1831,10 +1820,10 @@
 
   {
     shared_quad_state->SetAll(
-        gfx::Transform(), rect, rect, gfx::MaskFilterInfo(), rect, is_clipped,
+        gfx::Transform(), rect, rect, gfx::MaskFilterInfo(), base::nullopt,
         are_contents_opaque, opacity, SkBlendMode::kSrcOver, 0);
     shared_quad_state2->SetAll(epsilon_scale, rect, rect, gfx::MaskFilterInfo(),
-                               rect, is_clipped, are_contents_opaque, opacity,
+                               base::nullopt, are_contents_opaque, opacity,
                                SkBlendMode::kSrcOver, 1);
 
     quad->SetNew(shared_quad_state, rect, rect, SK_ColorBLACK, false);
@@ -1861,11 +1850,11 @@
 
   {
     shared_quad_state->SetAll(
-        gfx::Transform(), rect, rect, gfx::MaskFilterInfo(), rect, is_clipped,
+        gfx::Transform(), rect, rect, gfx::MaskFilterInfo(), base::nullopt,
         are_contents_opaque, opacity, SkBlendMode::kSrcOver, 0);
     shared_quad_state2->SetAll(
-        larger_epsilon_scale, rect, rect, gfx::MaskFilterInfo(), rect,
-        is_clipped, are_contents_opaque, opacity, SkBlendMode::kSrcOver, 0);
+        larger_epsilon_scale, rect, rect, gfx::MaskFilterInfo(), base::nullopt,
+        are_contents_opaque, opacity, SkBlendMode::kSrcOver, 0);
 
     quad->SetNew(shared_quad_state, rect, rect, SK_ColorBLACK, false);
     quad2->SetNew(shared_quad_state2, rect, rect, SK_ColorBLACK, false);
@@ -1895,7 +1884,6 @@
   gfx::Rect rect(0, 0, 100, 100);
 
   gfx::Transform negative_scale;
-  bool is_clipped = false;
   bool are_contents_opaque = true;
   float opacity = 1.f;
   SharedQuadState* shared_quad_state =
@@ -1910,10 +1898,10 @@
   {
     negative_scale.Scale3d(-1, 1, 1);
     shared_quad_state->SetAll(
-        gfx::Transform(), rect, rect, gfx::MaskFilterInfo(), rect, is_clipped,
+        gfx::Transform(), rect, rect, gfx::MaskFilterInfo(), base::nullopt,
         are_contents_opaque, opacity, SkBlendMode::kSrcOver, 0);
     shared_quad_state2->SetAll(
-        negative_scale, rect, rect, gfx::MaskFilterInfo(), rect, is_clipped,
+        negative_scale, rect, rect, gfx::MaskFilterInfo(), base::nullopt,
         are_contents_opaque, opacity, SkBlendMode::kSrcOver, 0);
 
     quad->SetNew(shared_quad_state, rect, rect, SK_ColorBLACK, false);
@@ -1942,10 +1930,10 @@
     negative_scale.MakeIdentity();
     negative_scale.Scale3d(1, -1, 1);
     shared_quad_state->SetAll(
-        gfx::Transform(), rect, rect, gfx::MaskFilterInfo(), rect, is_clipped,
+        gfx::Transform(), rect, rect, gfx::MaskFilterInfo(), base::nullopt,
         are_contents_opaque, opacity, SkBlendMode::kSrcOver, 0);
     shared_quad_state2->SetAll(
-        negative_scale, rect, rect, gfx::MaskFilterInfo(), rect, is_clipped,
+        negative_scale, rect, rect, gfx::MaskFilterInfo(), base::nullopt,
         are_contents_opaque, opacity, SkBlendMode::kSrcOver, 0);
 
     quad->SetNew(shared_quad_state, rect, rect, SK_ColorBLACK, false);
@@ -1974,10 +1962,10 @@
     negative_scale.MakeIdentity();
     negative_scale.Scale3d(1, 1, -1);
     shared_quad_state->SetAll(
-        gfx::Transform(), rect, rect, gfx::MaskFilterInfo(), rect, is_clipped,
+        gfx::Transform(), rect, rect, gfx::MaskFilterInfo(), base::nullopt,
         are_contents_opaque, opacity, SkBlendMode::kSrcOver, 0);
     shared_quad_state2->SetAll(
-        negative_scale, rect, rect, gfx::MaskFilterInfo(), rect, is_clipped,
+        negative_scale, rect, rect, gfx::MaskFilterInfo(), base::nullopt,
         are_contents_opaque, opacity, SkBlendMode::kSrcOver, 0);
 
     quad->SetNew(shared_quad_state, rect, rect, SK_ColorBLACK, false);
@@ -2022,7 +2010,6 @@
 
   gfx::Transform rotate;
   rotate.RotateAboutYAxis(45);
-  bool is_clipped = false;
   bool are_contents_opaque = true;
   float opacity = 1.f;
   SharedQuadState* shared_quad_state =
@@ -2036,11 +2023,11 @@
   {
     // Apply rotation transform on |rect1| only.
     shared_quad_state->SetAll(rotate, rect1, rect1, gfx::MaskFilterInfo(),
-                              rect1, is_clipped, are_contents_opaque, opacity,
+                              base::nullopt, are_contents_opaque, opacity,
                               SkBlendMode::kSrcOver, 0);
     shared_quad_state2->SetAll(
-        gfx::Transform(), rect2, rect2, gfx::MaskFilterInfo(), rect2,
-        is_clipped, are_contents_opaque, opacity, SkBlendMode::kSrcOver, 0);
+        gfx::Transform(), rect2, rect2, gfx::MaskFilterInfo(), base::nullopt,
+        are_contents_opaque, opacity, SkBlendMode::kSrcOver, 0);
     quad->SetNew(shared_quad_state, rect1, rect1, SK_ColorBLACK, false);
     quad2->SetNew(shared_quad_state2, rect2, rect2, SK_ColorBLACK, false);
     EXPECT_EQ(2u, NumVisibleRects(frame.render_pass_list.front()->quad_list));
@@ -2060,10 +2047,10 @@
   {
     // Apply rotation transform on |rect1| and |rect2|.
     shared_quad_state->SetAll(rotate, rect1, rect1, gfx::MaskFilterInfo(),
-                              rect1, is_clipped, are_contents_opaque, opacity,
+                              base::nullopt, are_contents_opaque, opacity,
                               SkBlendMode::kSrcOver, 0);
     shared_quad_state2->SetAll(rotate, rect2, rect2, gfx::MaskFilterInfo(),
-                               rect2, is_clipped, are_contents_opaque, opacity,
+                               base::nullopt, are_contents_opaque, opacity,
                                SkBlendMode::kSrcOver, 0);
     quad->SetNew(shared_quad_state, rect1, rect1, SK_ColorBLACK, false);
     quad2->SetNew(shared_quad_state2, rect2, rect2, SK_ColorBLACK, false);
@@ -2082,11 +2069,11 @@
     quad2 = frame.render_pass_list.front()
                 ->quad_list.AllocateAndConstruct<SolidColorDrawQuad>();
     shared_quad_state->SetAll(rotate, rect1, rect1, gfx::MaskFilterInfo(),
-                              rect1, is_clipped, are_contents_opaque, opacity,
+                              base::nullopt, are_contents_opaque, opacity,
                               SkBlendMode::kSrcOver, 0);
     shared_quad_state2->SetAll(
-        gfx::Transform(), rect3, rect3, gfx::MaskFilterInfo(), rect3,
-        is_clipped, are_contents_opaque, opacity, SkBlendMode::kSrcOver, 0);
+        gfx::Transform(), rect3, rect3, gfx::MaskFilterInfo(), base::nullopt,
+        are_contents_opaque, opacity, SkBlendMode::kSrcOver, 0);
     quad->SetNew(shared_quad_state, rect1, rect1, SK_ColorBLACK, false);
     quad2->SetNew(shared_quad_state2, rect3, rect3, SK_ColorBLACK, false);
     EXPECT_EQ(2u, NumVisibleRects(frame.render_pass_list.front()->quad_list));
@@ -2108,10 +2095,10 @@
     // or translation transform and rotation transform applies to quads,
     // |visible_rect| of |quad2| should not be changed.
     shared_quad_state->SetAll(rotate, rect1, rect1, gfx::MaskFilterInfo(),
-                              rect1, is_clipped, are_contents_opaque, opacity,
+                              base::nullopt, are_contents_opaque, opacity,
                               SkBlendMode::kSrcOver, 0);
     shared_quad_state2->SetAll(rotate, rect3, rect3, gfx::MaskFilterInfo(),
-                               rect3, is_clipped, are_contents_opaque, opacity,
+                               base::nullopt, are_contents_opaque, opacity,
                                SkBlendMode::kSrcOver, 0);
     quad->SetNew(shared_quad_state, rect1, rect1, SK_ColorBLACK, false);
     quad2->SetNew(shared_quad_state2, rect3, rect3, SK_ColorBLACK, false);
@@ -2148,7 +2135,6 @@
   perspective.ApplyPerspectiveDepth(100);
   perspective.RotateAboutYAxis(45);
 
-  bool is_clipped = false;
   bool are_contents_opaque = true;
   float opacity = 1.f;
   SharedQuadState* shared_quad_state =
@@ -2161,11 +2147,11 @@
                     ->quad_list.AllocateAndConstruct<SolidColorDrawQuad>();
   {
     shared_quad_state->SetAll(perspective, rect1, rect1, gfx::MaskFilterInfo(),
-                              rect1, is_clipped, are_contents_opaque, opacity,
+                              base::nullopt, are_contents_opaque, opacity,
                               SkBlendMode::kSrcOver, 0);
     shared_quad_state2->SetAll(
-        gfx::Transform(), rect1, rect1, gfx::MaskFilterInfo(), rect1,
-        is_clipped, are_contents_opaque, opacity, SkBlendMode::kSrcOver, 0);
+        gfx::Transform(), rect1, rect1, gfx::MaskFilterInfo(), base::nullopt,
+        are_contents_opaque, opacity, SkBlendMode::kSrcOver, 0);
     quad->SetNew(shared_quad_state, rect1, rect1, SK_ColorBLACK, false);
     quad2->SetNew(shared_quad_state2, rect1, rect1, SK_ColorBLACK, false);
     EXPECT_EQ(2u, NumVisibleRects(frame.render_pass_list.front()->quad_list));
@@ -2185,10 +2171,10 @@
 
   {
     shared_quad_state->SetAll(
-        gfx::Transform(), rect1, rect1, gfx::MaskFilterInfo(), rect1,
-        is_clipped, are_contents_opaque, opacity, SkBlendMode::kSrcOver, 0);
+        gfx::Transform(), rect1, rect1, gfx::MaskFilterInfo(), base::nullopt,
+        are_contents_opaque, opacity, SkBlendMode::kSrcOver, 0);
     shared_quad_state2->SetAll(perspective, rect2, rect2, gfx::MaskFilterInfo(),
-                               rect2, is_clipped, are_contents_opaque, opacity,
+                               base::nullopt, are_contents_opaque, opacity,
                                SkBlendMode::kSrcOver, 0);
     quad->SetNew(shared_quad_state, rect1, rect1, SK_ColorBLACK, false);
     quad2->SetNew(shared_quad_state2, rect2, rect2, SK_ColorBLACK, false);
@@ -2217,7 +2203,6 @@
   gfx::Rect rect1(0, 0, 100, 100);
   gfx::Rect rect2(25, 25, 10, 10);
 
-  bool is_clipped = false;
   bool are_contents_opaque = true;
   float opacity1 = 1.f;
   float opacityLess1 = 0.5f;
@@ -2230,13 +2215,12 @@
   auto* quad2 = frame.render_pass_list.front()
                     ->quad_list.AllocateAndConstruct<SolidColorDrawQuad>();
   {
-    shared_quad_state->SetAll(gfx::Transform(), rect1, rect1,
-                              gfx::MaskFilterInfo(), rect1, is_clipped,
-                              are_contents_opaque, opacityLess1,
-                              SkBlendMode::kSrcOver, 0);
+    shared_quad_state->SetAll(
+        gfx::Transform(), rect1, rect1, gfx::MaskFilterInfo(), base::nullopt,
+        are_contents_opaque, opacityLess1, SkBlendMode::kSrcOver, 0);
     shared_quad_state2->SetAll(
-        gfx::Transform(), rect2, rect2, gfx::MaskFilterInfo(), rect2,
-        is_clipped, are_contents_opaque, opacity1, SkBlendMode::kSrcOver, 0);
+        gfx::Transform(), rect2, rect2, gfx::MaskFilterInfo(), base::nullopt,
+        are_contents_opaque, opacity1, SkBlendMode::kSrcOver, 0);
     quad->SetNew(shared_quad_state, rect1, rect1, SK_ColorBLACK, false);
     quad2->SetNew(shared_quad_state2, rect2, rect2, SK_ColorBLACK, false);
     EXPECT_EQ(2u, NumVisibleRects(frame.render_pass_list.front()->quad_list));
@@ -2254,11 +2238,11 @@
 
   {
     shared_quad_state->SetAll(
-        gfx::Transform(), rect1, rect1, gfx::MaskFilterInfo(), rect1,
-        is_clipped, are_contents_opaque, opacity1, SkBlendMode::kSrcOver, 0);
+        gfx::Transform(), rect1, rect1, gfx::MaskFilterInfo(), base::nullopt,
+        are_contents_opaque, opacity1, SkBlendMode::kSrcOver, 0);
     shared_quad_state2->SetAll(
-        gfx::Transform(), rect2, rect2, gfx::MaskFilterInfo(), rect2,
-        is_clipped, are_contents_opaque, opacity1, SkBlendMode::kSrcOver, 0);
+        gfx::Transform(), rect2, rect2, gfx::MaskFilterInfo(), base::nullopt,
+        are_contents_opaque, opacity1, SkBlendMode::kSrcOver, 0);
     quad->SetNew(shared_quad_state, rect1, rect1, SK_ColorBLACK, false);
     quad2->SetNew(shared_quad_state2, rect2, rect2, SK_ColorBLACK, false);
     EXPECT_EQ(2u, NumVisibleRects(frame.render_pass_list.front()->quad_list));
@@ -2282,7 +2266,6 @@
   gfx::Rect rect1(0, 0, 100, 100);
   gfx::Rect rect2(25, 25, 10, 10);
 
-  bool is_clipped = false;
   bool opaque_content = true;
   bool transparent_content = false;
   float opacity = 1.f;
@@ -2296,11 +2279,11 @@
                     ->quad_list.AllocateAndConstruct<SolidColorDrawQuad>();
   {
     shared_quad_state->SetAll(
-        gfx::Transform(), rect1, rect1, gfx::MaskFilterInfo(), rect1,
-        is_clipped, transparent_content, opacity, SkBlendMode::kSrcOver, 0);
+        gfx::Transform(), rect1, rect1, gfx::MaskFilterInfo(), base::nullopt,
+        transparent_content, opacity, SkBlendMode::kSrcOver, 0);
     shared_quad_state2->SetAll(
-        gfx::Transform(), rect2, rect2, gfx::MaskFilterInfo(), rect2,
-        is_clipped, opaque_content, opacity, SkBlendMode::kSrcOver, 0);
+        gfx::Transform(), rect2, rect2, gfx::MaskFilterInfo(), base::nullopt,
+        opaque_content, opacity, SkBlendMode::kSrcOver, 0);
     quad->SetNew(shared_quad_state, rect1, rect1, SK_ColorBLACK, false);
     quad2->SetNew(shared_quad_state2, rect2, rect2, SK_ColorBLACK, false);
     EXPECT_EQ(2u, NumVisibleRects(frame.render_pass_list.front()->quad_list));
@@ -2318,11 +2301,11 @@
 
   {
     shared_quad_state->SetAll(
-        gfx::Transform(), rect1, rect1, gfx::MaskFilterInfo(), rect1,
-        is_clipped, opaque_content, opacity, SkBlendMode::kSrcOver, 0);
+        gfx::Transform(), rect1, rect1, gfx::MaskFilterInfo(), base::nullopt,
+        opaque_content, opacity, SkBlendMode::kSrcOver, 0);
     shared_quad_state2->SetAll(
-        gfx::Transform(), rect2, rect2, gfx::MaskFilterInfo(), rect2,
-        is_clipped, opaque_content, opacity, SkBlendMode::kSrcOver, 0);
+        gfx::Transform(), rect2, rect2, gfx::MaskFilterInfo(), base::nullopt,
+        opaque_content, opacity, SkBlendMode::kSrcOver, 0);
     quad->SetNew(shared_quad_state, rect1, rect1, SK_ColorBLACK, false);
     quad2->SetNew(shared_quad_state2, rect2, rect2, SK_ColorBLACK, false);
     EXPECT_EQ(2u, NumVisibleRects(frame.render_pass_list.front()->quad_list));
@@ -2349,7 +2332,6 @@
 
   gfx::Transform translate_back;
   translate_back.Translate3d(0, 0, 100);
-  bool is_clipped = false;
   bool are_contents_opaque = true;
   float opacity = 1.f;
   SharedQuadState* shared_quad_state =
@@ -2367,11 +2349,11 @@
   //                         +-----+
   {
     shared_quad_state->SetAll(
-        translate_back, rect1, rect1, gfx::MaskFilterInfo(), rect1, is_clipped,
+        translate_back, rect1, rect1, gfx::MaskFilterInfo(), base::nullopt,
         are_contents_opaque, opacity, SkBlendMode::kSrcOver, 1);
     shared_quad_state2->SetAll(
-        gfx::Transform(), rect1, rect1, gfx::MaskFilterInfo(), rect1,
-        is_clipped, are_contents_opaque, opacity, SkBlendMode::kSrcOver, 1);
+        gfx::Transform(), rect1, rect1, gfx::MaskFilterInfo(), base::nullopt,
+        are_contents_opaque, opacity, SkBlendMode::kSrcOver, 1);
 
     quad->SetNew(shared_quad_state, rect1, rect1, SK_ColorBLACK, false);
     quad2->SetNew(shared_quad_state2, rect2, rect1, SK_ColorBLACK, false);
@@ -2402,7 +2384,6 @@
   gfx::Rect rect2(120, 120, 10, 10);
   gfx::Rect rect3(100, 100, 100, 20);
 
-  bool is_clipped = false;
   bool opaque_content = true;
   bool transparent_content = false;
   float opacity = 1.f;
@@ -2425,11 +2406,11 @@
     //           +-+
     //           +-+
     shared_quad_state->SetAll(
-        gfx::Transform(), rect1, rect1, gfx::MaskFilterInfo(), rect1,
-        is_clipped, transparent_content, opacity, SkBlendMode::kSrcOver, 0);
+        gfx::Transform(), rect1, rect1, gfx::MaskFilterInfo(), base::nullopt,
+        transparent_content, opacity, SkBlendMode::kSrcOver, 0);
     shared_quad_state2->SetAll(
-        gfx::Transform(), rect2, rect2, gfx::MaskFilterInfo(), rect2,
-        is_clipped, opaque_content, opacity, SkBlendMode::kSrcOver, 0);
+        gfx::Transform(), rect2, rect2, gfx::MaskFilterInfo(), base::nullopt,
+        opaque_content, opacity, SkBlendMode::kSrcOver, 0);
     quad->SetNew(shared_quad_state, rect1, rect1, SK_ColorBLACK, false);
     quad2->SetNew(shared_quad_state2, rect2, rect2, SK_ColorBLACK, false);
     EXPECT_EQ(2u, NumVisibleRects(frame.render_pass_list.front()->quad_list));
@@ -2454,11 +2435,11 @@
     //           +-+                                                  | +-+ |
     //           +-+                                                  +-----+
     shared_quad_state->SetAll(translate_up, rect1, rect1, gfx::MaskFilterInfo(),
-                              rect1, is_clipped, opaque_content, opacity,
+                              base::nullopt, opaque_content, opacity,
                               SkBlendMode::kSrcOver, 0);
     shared_quad_state2->SetAll(
-        gfx::Transform(), rect2, rect2, gfx::MaskFilterInfo(), rect2,
-        is_clipped, opaque_content, opacity, SkBlendMode::kSrcOver, 0);
+        gfx::Transform(), rect2, rect2, gfx::MaskFilterInfo(), base::nullopt,
+        opaque_content, opacity, SkBlendMode::kSrcOver, 0);
     quad->SetNew(shared_quad_state, rect1, rect1, SK_ColorBLACK, false);
     quad2->SetNew(shared_quad_state2, rect2, rect2, SK_ColorBLACK, false);
     EXPECT_EQ(2u, NumVisibleRects(frame.render_pass_list.front()->quad_list));
@@ -2484,11 +2465,11 @@
     quad2 = frame.render_pass_list.front()
                 ->quad_list.AllocateAndConstruct<SolidColorDrawQuad>();
     shared_quad_state->SetAll(translate_up, rect1, rect1, gfx::MaskFilterInfo(),
-                              rect1, is_clipped, opaque_content, opacity,
+                              base::nullopt, opaque_content, opacity,
                               SkBlendMode::kSrcOver, 0);
     shared_quad_state2->SetAll(
-        gfx::Transform(), rect3, rect3, gfx::MaskFilterInfo(), rect3,
-        is_clipped, opaque_content, opacity, SkBlendMode::kSrcOver, 0);
+        gfx::Transform(), rect3, rect3, gfx::MaskFilterInfo(), base::nullopt,
+        opaque_content, opacity, SkBlendMode::kSrcOver, 0);
     quad->SetNew(shared_quad_state, rect1, rect1, SK_ColorBLACK, false);
     quad2->SetNew(shared_quad_state2, rect3, rect3, SK_ColorBLACK, false);
     EXPECT_EQ(2u, NumVisibleRects(frame.render_pass_list.front()->quad_list));
@@ -2525,7 +2506,6 @@
   gfx::Rect rect4(10, 10, 180, 30);
   gfx::Rect rect5(10, 10, 120, 100);
 
-  bool is_clipped = false;
   bool opaque_content = true;
   float opacity = 1.f;
   SharedQuadState* shared_quad_state =
@@ -2548,14 +2528,14 @@
     //   +----+                            +----+
     //
     shared_quad_state->SetAll(
-        gfx::Transform(), rect1, rect1, gfx::MaskFilterInfo(), rect1,
-        is_clipped, opaque_content, opacity, SkBlendMode::kSrcOver, 0);
+        gfx::Transform(), rect1, rect1, gfx::MaskFilterInfo(), base::nullopt,
+        opaque_content, opacity, SkBlendMode::kSrcOver, 0);
     shared_quad_state2->SetAll(
-        gfx::Transform(), rect2, rect2, gfx::MaskFilterInfo(), rect2,
-        is_clipped, opaque_content, opacity, SkBlendMode::kSrcOver, 0);
+        gfx::Transform(), rect2, rect2, gfx::MaskFilterInfo(), base::nullopt,
+        opaque_content, opacity, SkBlendMode::kSrcOver, 0);
     shared_quad_state3->SetAll(
-        gfx::Transform(), rect3, rect3, gfx::MaskFilterInfo(), rect3,
-        is_clipped, opaque_content, opacity, SkBlendMode::kSrcOver, 0);
+        gfx::Transform(), rect3, rect3, gfx::MaskFilterInfo(), base::nullopt,
+        opaque_content, opacity, SkBlendMode::kSrcOver, 0);
     quad->SetNew(shared_quad_state, rect1, rect1, SK_ColorBLACK, false);
     quad2->SetNew(shared_quad_state2, rect2, rect2, SK_ColorBLACK, false);
     quad3->SetNew(shared_quad_state3, rect3, rect3, SK_ColorBLACK, false);
@@ -2583,8 +2563,8 @@
     quad3 = frame.render_pass_list.front()
                 ->quad_list.AllocateAndConstruct<SolidColorDrawQuad>();
     shared_quad_state3->SetAll(
-        gfx::Transform(), rect4, rect4, gfx::MaskFilterInfo(), rect4,
-        is_clipped, opaque_content, opacity, SkBlendMode::kSrcOver, 0);
+        gfx::Transform(), rect4, rect4, gfx::MaskFilterInfo(), base::nullopt,
+        opaque_content, opacity, SkBlendMode::kSrcOver, 0);
     quad3->SetNew(shared_quad_state3, rect4, rect4, SK_ColorBLACK, false);
     EXPECT_EQ(3u, NumVisibleRects(frame.render_pass_list.front()->quad_list));
     display_->RemoveOverdrawQuads(&frame);
@@ -2612,8 +2592,8 @@
     //   +----+                            +-|--+  |
     //                                       +-----+
     shared_quad_state3->SetAll(
-        gfx::Transform(), rect5, rect5, gfx::MaskFilterInfo(), rect5,
-        is_clipped, opaque_content, opacity, SkBlendMode::kSrcOver, 0);
+        gfx::Transform(), rect5, rect5, gfx::MaskFilterInfo(), base::nullopt,
+        opaque_content, opacity, SkBlendMode::kSrcOver, 0);
     quad3->SetNew(shared_quad_state3, rect5, rect5, SK_ColorBLACK, false);
     EXPECT_EQ(3u, NumVisibleRects(frame.render_pass_list.front()->quad_list));
     display_->RemoveOverdrawQuads(&frame);
@@ -2661,9 +2641,9 @@
     quads[i] =
         render_pass->quad_list.AllocateAndConstruct<SolidColorDrawQuad>();
     shared_quad_states[i]->SetAll(
-        gfx::Transform(), rects[i], rects[i], gfx::MaskFilterInfo(), rects[i],
-        false /*is_clipped*/, true /*are_contents_opaque*/, 1.f /*opacity*/,
-        SkBlendMode::kSrcOver, 0 /*sorting_context_id*/);
+        gfx::Transform(), rects[i], rects[i], gfx::MaskFilterInfo(),
+        /*clip_rect=*/base::nullopt, true /*are_contents_opaque*/,
+        1.f /*opacity*/, SkBlendMode::kSrcOver, 0 /*sorting_context_id*/);
     quads[i]->SetNew(shared_quad_states[i], rects[i], rects[i], SK_ColorBLACK,
                      false /*force_anti_aliasing_off*/);
   }
@@ -2699,7 +2679,6 @@
   frame.render_pass_list.push_back(std::move(render_pass2));
   gfx::Rect rect3(10, 10, 120, 30);
 
-  bool is_clipped = false;
   bool opaque_content = true;
   float opacity = 1.f;
 
@@ -2725,14 +2704,14 @@
     //   +----+                            +----+
     //
     shared_quad_state->SetAll(
-        gfx::Transform(), rect1, rect1, gfx::MaskFilterInfo(), rect1,
-        is_clipped, opaque_content, opacity, SkBlendMode::kSrcOver, 0);
+        gfx::Transform(), rect1, rect1, gfx::MaskFilterInfo(), base::nullopt,
+        opaque_content, opacity, SkBlendMode::kSrcOver, 0);
     shared_quad_state2->SetAll(
-        gfx::Transform(), rect2, rect2, gfx::MaskFilterInfo(), rect2,
-        is_clipped, opaque_content, opacity, SkBlendMode::kSrcOver, 0);
+        gfx::Transform(), rect2, rect2, gfx::MaskFilterInfo(), base::nullopt,
+        opaque_content, opacity, SkBlendMode::kSrcOver, 0);
     shared_quad_state3->SetAll(
-        gfx::Transform(), rect3, rect3, gfx::MaskFilterInfo(), rect3,
-        is_clipped, opaque_content, opacity, SkBlendMode::kSrcOver, 0);
+        gfx::Transform(), rect3, rect3, gfx::MaskFilterInfo(), base::nullopt,
+        opaque_content, opacity, SkBlendMode::kSrcOver, 0);
     quad->SetNew(shared_quad_state, rect1, rect1, SK_ColorBLACK, false);
     quad2->SetNew(shared_quad_state2, rect2, rect2, SK_ColorBLACK, false);
     quad3->SetNew(shared_quad_state3, rect3, rect3, SK_ColorBLACK, false);
@@ -2771,7 +2750,6 @@
                        gfx::Transform());
   frame.render_pass_list.push_back(std::move(render_pass2));
 
-  bool is_clipped = false;
   bool opaque_content = true;
   float opacity = 1.f;
   AggregatedRenderPassId render_pass_id{1};
@@ -2798,11 +2776,11 @@
     //
 
     shared_quad_state->SetAll(
-        gfx::Transform(), rect1, rect1, gfx::MaskFilterInfo(), rect1,
-        is_clipped, opaque_content, opacity, SkBlendMode::kSrcOver, 0);
+        gfx::Transform(), rect1, rect1, gfx::MaskFilterInfo(), base::nullopt,
+        opaque_content, opacity, SkBlendMode::kSrcOver, 0);
     shared_quad_state2->SetAll(
-        gfx::Transform(), rect1, rect1, gfx::MaskFilterInfo(), rect1,
-        is_clipped, opaque_content, opacity, SkBlendMode::kSrcOver, 0);
+        gfx::Transform(), rect1, rect1, gfx::MaskFilterInfo(), base::nullopt,
+        opaque_content, opacity, SkBlendMode::kSrcOver, 0);
     quad->SetNew(shared_quad_state, rect1, rect1, SK_ColorBLACK, false);
     quad1->SetNew(shared_quad_state2, rect1, rect1, render_pass_id,
                   mask_resource_id, gfx::RectF(), gfx::Size(),
@@ -2838,8 +2816,6 @@
   gfx::Rect clip_rect(0, 0, 60, 60);
   gfx::Rect rect3(50, 50, 20, 10);
 
-  bool clipped = true;
-  bool non_clipped = false;
   bool opaque_content = true;
   float opacity = 1.f;
   SharedQuadState* shared_quad_state =
@@ -2859,11 +2835,11 @@
     //   +------+
     //
     shared_quad_state->SetAll(
-        gfx::Transform(), rect1, rect1, gfx::MaskFilterInfo(), rect1,
-        non_clipped, opaque_content, opacity, SkBlendMode::kSrcOver, 0);
+        gfx::Transform(), rect1, rect1, gfx::MaskFilterInfo(), base::nullopt,
+        opaque_content, opacity, SkBlendMode::kSrcOver, 0);
     shared_quad_state2->SetAll(
-        gfx::Transform(), rect2, rect2, gfx::MaskFilterInfo(), rect2,
-        non_clipped, opaque_content, opacity, SkBlendMode::kSrcOver, 0);
+        gfx::Transform(), rect2, rect2, gfx::MaskFilterInfo(), base::nullopt,
+        opaque_content, opacity, SkBlendMode::kSrcOver, 0);
     quad->SetNew(shared_quad_state, rect1, rect1, SK_ColorBLACK, false);
     quad2->SetNew(shared_quad_state2, rect2, rect2, SK_ColorBLACK, false);
     EXPECT_EQ(2u, NumVisibleRects(frame.render_pass_list.front()->quad_list));
@@ -2885,12 +2861,12 @@
     //
     quad2 = frame.render_pass_list.front()
                 ->quad_list.AllocateAndConstruct<SolidColorDrawQuad>();
-    shared_quad_state->SetAll(
-        gfx::Transform(), rect1, rect1, gfx::MaskFilterInfo(), clip_rect,
-        clipped, opaque_content, opacity, SkBlendMode::kSrcOver, 0);
+    shared_quad_state->SetAll(gfx::Transform(), rect1, rect1,
+                              gfx::MaskFilterInfo(), clip_rect, opaque_content,
+                              opacity, SkBlendMode::kSrcOver, 0);
     shared_quad_state2->SetAll(
-        gfx::Transform(), rect2, rect2, gfx::MaskFilterInfo(), rect2,
-        non_clipped, opaque_content, opacity, SkBlendMode::kSrcOver, 0);
+        gfx::Transform(), rect2, rect2, gfx::MaskFilterInfo(), base::nullopt,
+        opaque_content, opacity, SkBlendMode::kSrcOver, 0);
     quad->SetNew(shared_quad_state, rect1, rect1, SK_ColorBLACK, false);
     quad2->SetNew(shared_quad_state2, rect2, rect2, SK_ColorBLACK, false);
     EXPECT_EQ(2u, NumVisibleRects(frame.render_pass_list.front()->quad_list));
@@ -2914,12 +2890,12 @@
     //   |   +-+|             =>                      +--+++
     //   +------+
     //
-    shared_quad_state->SetAll(
-        gfx::Transform(), rect1, rect1, gfx::MaskFilterInfo(), clip_rect,
-        clipped, opaque_content, opacity, SkBlendMode::kSrcOver, 0);
+    shared_quad_state->SetAll(gfx::Transform(), rect1, rect1,
+                              gfx::MaskFilterInfo(), clip_rect, opaque_content,
+                              opacity, SkBlendMode::kSrcOver, 0);
     shared_quad_state2->SetAll(
-        gfx::Transform(), rect3, rect3, gfx::MaskFilterInfo(), rect3,
-        non_clipped, opaque_content, opacity, SkBlendMode::kSrcOver, 0);
+        gfx::Transform(), rect3, rect3, gfx::MaskFilterInfo(), base::nullopt,
+        opaque_content, opacity, SkBlendMode::kSrcOver, 0);
     quad->SetNew(shared_quad_state, rect1, rect1, SK_ColorBLACK, false);
     quad2->SetNew(shared_quad_state2, rect3, rect3, SK_ColorBLACK, false);
     EXPECT_EQ(2u, NumVisibleRects(frame.render_pass_list.front()->quad_list));
@@ -2951,7 +2927,6 @@
   gfx::Rect rect1(0, 0, 100, 100);
   gfx::Rect rect2(50, 50, 25, 25);
 
-  bool is_clipped = false;
   bool opaque_content = true;
   float opacity = 1.f;
   SharedQuadState* shared_quad_state =
@@ -2964,11 +2939,11 @@
                     ->quad_list.AllocateAndConstruct<SolidColorDrawQuad>();
   {
     shared_quad_state->SetAll(
-        gfx::Transform(), rect1, rect1, gfx::MaskFilterInfo(), rect1,
-        is_clipped, opaque_content, opacity, SkBlendMode::kSrcOver, 0);
+        gfx::Transform(), rect1, rect1, gfx::MaskFilterInfo(), base::nullopt,
+        opaque_content, opacity, SkBlendMode::kSrcOver, 0);
     shared_quad_state2->SetAll(
-        gfx::Transform(), rect2, rect2, gfx::MaskFilterInfo(), rect2,
-        is_clipped, opaque_content, opacity, SkBlendMode::kSrcOver, 0);
+        gfx::Transform(), rect2, rect2, gfx::MaskFilterInfo(), base::nullopt,
+        opaque_content, opacity, SkBlendMode::kSrcOver, 0);
     quad->SetNew(shared_quad_state, rect1, rect1, SK_ColorBLACK, false);
     quad2->SetNew(shared_quad_state2, rect2, rect2, SK_ColorBLACK, false);
     frame.render_pass_list.front()->copy_requests.push_back(
@@ -3002,7 +2977,6 @@
   gfx::Rect rect6(0, 75, 25, 25);
   gfx::Rect rect7(0, 0, 10, 10);
 
-  bool is_clipped = false;
   bool opaque_content = true;
   AggregatedRenderPassId render_pass_id{1};
   ResourceId mask_resource_id(2);
@@ -3034,17 +3008,17 @@
     // |   R1  |   |    R2  |
     // +-------+---+--------+
     shared_quad_state->SetAll(
-        gfx::Transform(), rect1, rect1, gfx::MaskFilterInfo(), rect1,
-        is_clipped, opaque_content, opacity, SkBlendMode::kSrcOver, 0);
+        gfx::Transform(), rect1, rect1, gfx::MaskFilterInfo(), base::nullopt,
+        opaque_content, opacity, SkBlendMode::kSrcOver, 0);
     shared_quad_state2->SetAll(
-        gfx::Transform(), rect2, rect2, gfx::MaskFilterInfo(), rect2,
-        is_clipped, opaque_content, opacity, SkBlendMode::kSrcOver, 0);
+        gfx::Transform(), rect2, rect2, gfx::MaskFilterInfo(), base::nullopt,
+        opaque_content, opacity, SkBlendMode::kSrcOver, 0);
     shared_quad_state3->SetAll(
-        gfx::Transform(), rect3, rect3, gfx::MaskFilterInfo(), rect3,
-        is_clipped, opaque_content, opacity, SkBlendMode::kSrcOver, 0);
+        gfx::Transform(), rect3, rect3, gfx::MaskFilterInfo(), base::nullopt,
+        opaque_content, opacity, SkBlendMode::kSrcOver, 0);
     shared_quad_state4->SetAll(
-        gfx::Transform(), rect4, rect4, gfx::MaskFilterInfo(), rect4,
-        is_clipped, opaque_content, opacity, SkBlendMode::kSrcOver, 0);
+        gfx::Transform(), rect4, rect4, gfx::MaskFilterInfo(), base::nullopt,
+        opaque_content, opacity, SkBlendMode::kSrcOver, 0);
     R1->SetNew(shared_quad_state, rect1, rect1, render_pass_id,
                mask_resource_id, gfx::RectF(), gfx::Size(),
                gfx::Vector2dF(1, 1), gfx::PointF(), gfx::RectF(), false, 1.0f);
@@ -3082,17 +3056,17 @@
     // |   R2  |       R1  |
     // +-------+-----------+
     shared_quad_state->SetAll(
-        gfx::Transform(), rect5, rect5, gfx::MaskFilterInfo(), rect5,
-        is_clipped, opaque_content, opacity, SkBlendMode::kSrcOver, 0);
+        gfx::Transform(), rect5, rect5, gfx::MaskFilterInfo(), base::nullopt,
+        opaque_content, opacity, SkBlendMode::kSrcOver, 0);
     shared_quad_state2->SetAll(
-        gfx::Transform(), rect1, rect1, gfx::MaskFilterInfo(), rect1,
-        is_clipped, opaque_content, opacity, SkBlendMode::kSrcOver, 0);
+        gfx::Transform(), rect1, rect1, gfx::MaskFilterInfo(), base::nullopt,
+        opaque_content, opacity, SkBlendMode::kSrcOver, 0);
     shared_quad_state3->SetAll(
-        gfx::Transform(), rect3, rect3, gfx::MaskFilterInfo(), rect3,
-        is_clipped, opaque_content, opacity, SkBlendMode::kSrcOver, 0);
+        gfx::Transform(), rect3, rect3, gfx::MaskFilterInfo(), base::nullopt,
+        opaque_content, opacity, SkBlendMode::kSrcOver, 0);
     shared_quad_state4->SetAll(
-        gfx::Transform(), rect6, rect6, gfx::MaskFilterInfo(), rect6,
-        is_clipped, opaque_content, opacity, SkBlendMode::kSrcOver, 0);
+        gfx::Transform(), rect6, rect6, gfx::MaskFilterInfo(), base::nullopt,
+        opaque_content, opacity, SkBlendMode::kSrcOver, 0);
     R1->SetNew(shared_quad_state, rect5, rect5, render_pass_id,
                mask_resource_id, gfx::RectF(), gfx::Size(),
                gfx::Vector2dF(1, 1), gfx::PointF(), gfx::RectF(), false, 1.0f);
@@ -3129,17 +3103,17 @@
     // |   R2      |   R1  |
     // +-----------+-------+
     shared_quad_state->SetAll(
-        gfx::Transform(), rect5, rect5, gfx::MaskFilterInfo(), rect5,
-        is_clipped, opaque_content, opacity, SkBlendMode::kSrcOver, 0);
+        gfx::Transform(), rect5, rect5, gfx::MaskFilterInfo(), base::nullopt,
+        opaque_content, opacity, SkBlendMode::kSrcOver, 0);
     shared_quad_state2->SetAll(
-        gfx::Transform(), rect1, rect1, gfx::MaskFilterInfo(), rect1,
-        is_clipped, opaque_content, opacity, SkBlendMode::kSrcOver, 0);
+        gfx::Transform(), rect1, rect1, gfx::MaskFilterInfo(), base::nullopt,
+        opaque_content, opacity, SkBlendMode::kSrcOver, 0);
     shared_quad_state3->SetAll(
-        gfx::Transform(), rect3, rect3, gfx::MaskFilterInfo(), rect3,
-        is_clipped, opaque_content, opacity, SkBlendMode::kSrcOver, 0);
+        gfx::Transform(), rect3, rect3, gfx::MaskFilterInfo(), base::nullopt,
+        opaque_content, opacity, SkBlendMode::kSrcOver, 0);
     shared_quad_state4->SetAll(
-        gfx::Transform(), rect7, rect7, gfx::MaskFilterInfo(), rect7,
-        is_clipped, opaque_content, opacity, SkBlendMode::kSrcOver, 0);
+        gfx::Transform(), rect7, rect7, gfx::MaskFilterInfo(), base::nullopt,
+        opaque_content, opacity, SkBlendMode::kSrcOver, 0);
     R1->SetNew(shared_quad_state, rect5, rect5, render_pass_id,
                mask_resource_id, gfx::RectF(), gfx::Size(),
                gfx::Vector2dF(1, 1), gfx::PointF(), gfx::RectF(), false, 1.0f);
@@ -3191,7 +3165,6 @@
   gfx::Rect rect3_1(0, 0, 70, 30);
   gfx::Rect rect3_2(70, 0, 70, 30);
 
-  bool is_clipped = false;
   bool opaque_content = true;
   float opacity = 1.f;
   SharedQuadState* shared_quad_state =
@@ -3218,12 +3191,11 @@
     // |  |  |
     // +--+--+
     shared_quad_state->SetAll(
-        gfx::Transform(), rect1, rect1, gfx::MaskFilterInfo(), rect1,
-        is_clipped, opaque_content, opacity, SkBlendMode::kSrcOver, 0);
-    shared_quad_state2->SetAll(gfx::Transform(), rect_in_rect1, rect_in_rect1,
-                               gfx::MaskFilterInfo(), rect_in_rect1, is_clipped,
-                               opaque_content, opacity, SkBlendMode::kSrcOver,
-                               0);
+        gfx::Transform(), rect1, rect1, gfx::MaskFilterInfo(), base::nullopt,
+        opaque_content, opacity, SkBlendMode::kSrcOver, 0);
+    shared_quad_state2->SetAll(
+        gfx::Transform(), rect_in_rect1, rect_in_rect1, gfx::MaskFilterInfo(),
+        base::nullopt, opaque_content, opacity, SkBlendMode::kSrcOver, 0);
     quad1->SetNew(shared_quad_state, rect1_1, rect1_1, SK_ColorBLACK, false);
     quad2->SetNew(shared_quad_state, rect1_2, rect1_2, SK_ColorBLACK, false);
     quad3->SetNew(shared_quad_state, rect1_3, rect1_3, SK_ColorBLACK, false);
@@ -3259,10 +3231,10 @@
     // +--+--+
     quad5 = frame.render_pass_list.front()
                 ->quad_list.AllocateAndConstruct<SolidColorDrawQuad>();
-    shared_quad_state2->SetAll(
-        gfx::Transform(), rect_intersects_rect1, rect_intersects_rect1,
-        gfx::MaskFilterInfo(), rect_intersects_rect1, is_clipped,
-        opaque_content, opacity, SkBlendMode::kSrcOver, 0);
+    shared_quad_state2->SetAll(gfx::Transform(), rect_intersects_rect1,
+                               rect_intersects_rect1, gfx::MaskFilterInfo(),
+                               base::nullopt, opaque_content, opacity,
+                               SkBlendMode::kSrcOver, 0);
     quad5->SetNew(shared_quad_state2, rect_intersects_rect1,
                   rect_intersects_rect1, SK_ColorBLACK, false);
     EXPECT_EQ(5u, NumVisibleRects(frame.render_pass_list.front()->quad_list));
@@ -3301,11 +3273,11 @@
     auto* quad6 = frame.render_pass_list.front()
                       ->quad_list.AllocateAndConstruct<SolidColorDrawQuad>();
     shared_quad_state->SetAll(
-        gfx::Transform(), rect2, rect2, gfx::MaskFilterInfo(), rect2,
-        is_clipped, opaque_content, opacity, SkBlendMode::kSrcOver, 0);
+        gfx::Transform(), rect2, rect2, gfx::MaskFilterInfo(), base::nullopt,
+        opaque_content, opacity, SkBlendMode::kSrcOver, 0);
     shared_quad_state2->SetAll(
-        gfx::Transform(), rect3, rect3, gfx::MaskFilterInfo(), rect3,
-        is_clipped, opaque_content, opacity, SkBlendMode::kSrcOver, 0);
+        gfx::Transform(), rect3, rect3, gfx::MaskFilterInfo(), base::nullopt,
+        opaque_content, opacity, SkBlendMode::kSrcOver, 0);
     quad1->SetNew(shared_quad_state, rect2_1, rect2_1, SK_ColorBLACK, false);
     quad2->SetNew(shared_quad_state, rect2_2, rect2_2, SK_ColorBLACK, false);
     quad3->SetNew(shared_quad_state, rect2_3, rect2_3, SK_ColorBLACK, false);
@@ -3360,7 +3332,6 @@
                                 0, 0, 0, 1);   // row 4
   gfx::Transform non_invertible_miss_z;
   non_invertible_miss_z.Scale3d(1, 1, 0);
-  bool is_clipped = false;
   bool opaque_content = true;
   float opacity = 1.f;
   auto* quad1 = frame.render_pass_list.front()
@@ -3391,13 +3362,13 @@
     // and is hiding behind quad1; |quad3| follows a non-invertible transform
     // and it is not covered by the occlusion rect.
     shared_quad_state1->SetAll(invertible, rect1, rect1, gfx::MaskFilterInfo(),
-                               rect1, is_clipped, opaque_content, opacity,
+                               base::nullopt, opaque_content, opacity,
                                SkBlendMode::kSrcOver, 0);
     shared_quad_state2->SetAll(invertible, rect2, rect2, gfx::MaskFilterInfo(),
-                               rect2, is_clipped, opaque_content, opacity,
+                               base::nullopt, opaque_content, opacity,
                                SkBlendMode::kSrcOver, 0);
     shared_quad_state3->SetAll(
-        non_invertible, rect3, rect3, gfx::MaskFilterInfo(), rect3, is_clipped,
+        non_invertible, rect3, rect3, gfx::MaskFilterInfo(), base::nullopt,
         opaque_content, opacity, SkBlendMode::kSrcOver, 0);
     quad1->SetNew(shared_quad_state1, rect1, rect1, SK_ColorBLACK, false);
     quad2->SetNew(shared_quad_state2, rect2, rect2, SK_ColorBLACK, false);
@@ -3425,11 +3396,11 @@
     // Verify if draw occlusion can occlude quad with non-invertible
     // transform.
     shared_quad_state1->SetAll(invertible, rect1, rect1, gfx::MaskFilterInfo(),
-                               rect1, is_clipped, opaque_content, opacity,
+                               base::nullopt, opaque_content, opacity,
                                SkBlendMode::kSrcOver, 0);
     shared_quad_state3->SetAll(
-        non_invertible_miss_z, rect3, rect3, gfx::MaskFilterInfo(), rect3,
-        is_clipped, opaque_content, opacity, SkBlendMode::kSrcOver, 0);
+        non_invertible_miss_z, rect3, rect3, gfx::MaskFilterInfo(),
+        base::nullopt, opaque_content, opacity, SkBlendMode::kSrcOver, 0);
     quad1->SetNew(shared_quad_state1, rect1, rect1, SK_ColorBLACK, false);
     quad3->SetNew(shared_quad_state3, rect3, rect3, SK_ColorBLACK, false);
 
@@ -3456,7 +3427,6 @@
   // which caused the integer overflow in the bug.
   gfx::Rect rect1(237790, 237790);
 
-  bool is_clipped = false;
   bool are_contents_opaque = true;
   float opacity = 1.f;
   SharedQuadState* shared_quad_state =
@@ -3469,8 +3439,8 @@
   // +----+
   {
     shared_quad_state->SetAll(
-        gfx::Transform(), rect1, rect1, gfx::MaskFilterInfo(), rect1,
-        is_clipped, are_contents_opaque, opacity, SkBlendMode::kSrcOver, 0);
+        gfx::Transform(), rect1, rect1, gfx::MaskFilterInfo(), base::nullopt,
+        are_contents_opaque, opacity, SkBlendMode::kSrcOver, 0);
 
     quad->SetNew(shared_quad_state, rect1, rect1, SK_ColorBLACK, false);
     EXPECT_EQ(1u, NumVisibleRects(frame.render_pass_list.front()->quad_list));
@@ -3535,8 +3505,8 @@
     shared_quad_state1->SetAll(
         gfx::Transform(), rect1 /* quad_layer_rect */,
         rect1 /* visible_quad_layer_rect */,
-        gfx::MaskFilterInfo() /* mask_filter_info */, rect1 /*clip_rect */,
-        false /* is_clipped */, false /* are_contents_opaque */,
+        gfx::MaskFilterInfo() /* mask_filter_info */,
+        base::nullopt /*clip_rect */, false /* are_contents_opaque */,
         0.5f /* opacity */, SkBlendMode::kSrcOver, 0 /* sorting_context_id */);
     auto* quad1 = pass->quad_list.AllocateAndConstruct<SolidColorDrawQuad>();
     quad1->SetNew(shared_quad_state1, rect1 /* rect */,
@@ -3548,8 +3518,8 @@
     shared_quad_state2->SetAll(
         gfx::Transform(), rect2 /* quad_layer_rect */,
         rect2 /* visible_quad_layer_rect */,
-        gfx::MaskFilterInfo() /* mask_filter_info */, rect2 /*clip_rect */,
-        false /* is_clipped */, true /* are_contents_opaque */,
+        gfx::MaskFilterInfo() /* mask_filter_info */,
+        base::nullopt /*clip_rect */, true /* are_contents_opaque */,
         1.0f /* opacity */, SkBlendMode::kSrcOver, 0 /* sorting_context_id */);
     auto* quad2 = pass->quad_list.AllocateAndConstruct<SurfaceDrawQuad>();
     quad2->SetNew(shared_quad_state2, rect2 /* rect */,
@@ -3956,7 +3926,6 @@
   gfx::MaskFilterInfo mask_filter_info(
       gfx::RRectF(gfx::RectF(quad_rect), 10.f));
 
-  bool is_clipped = false;
   bool are_contents_opaque = true;
   float opacity = 1.f;
   SharedQuadState* shared_quad_state_with_rrect =
@@ -3972,16 +3941,15 @@
           ->quad_list.AllocateAndConstruct<SolidColorDrawQuad>();
 
   {
-    shared_quad_state_occluded->SetAll(gfx::Transform(), quad_rect, quad_rect,
-                                       gfx::MaskFilterInfo(), quad_rect,
-                                       is_clipped, are_contents_opaque, opacity,
-                                       SkBlendMode::kSrcOver, 0);
+    shared_quad_state_occluded->SetAll(
+        gfx::Transform(), quad_rect, quad_rect, gfx::MaskFilterInfo(),
+        base::nullopt, are_contents_opaque, opacity, SkBlendMode::kSrcOver, 0);
     occluded_quad->SetNew(shared_quad_state_occluded, quad_rect, quad_rect,
                           SK_ColorRED, false);
 
     shared_quad_state_with_rrect->SetAll(
-        gfx::Transform(), quad_rect, quad_rect, mask_filter_info, quad_rect,
-        is_clipped, are_contents_opaque, opacity, SkBlendMode::kSrcOver, 0);
+        gfx::Transform(), quad_rect, quad_rect, mask_filter_info, base::nullopt,
+        are_contents_opaque, opacity, SkBlendMode::kSrcOver, 0);
     rounded_corner_quad->SetNew(shared_quad_state_with_rrect, quad_rect,
                                 quad_rect, SK_ColorBLUE, false);
 
@@ -4012,7 +3980,6 @@
   gfx::MaskFilterInfo mask_filter_info(
       gfx::RRectF(gfx::RectF(quad_rect), 10.f));
 
-  bool is_clipped = false;
   bool are_contents_opaque = true;
   float opacity = 1.f;
   SharedQuadState* shared_quad_state_with_rrect =
@@ -4030,14 +3997,14 @@
   {
     shared_quad_state_occluded->SetAll(
         gfx::Transform(), occluded_quad_rect, occluded_quad_rect,
-        gfx::MaskFilterInfo(), occluded_quad_rect, is_clipped,
-        are_contents_opaque, opacity, SkBlendMode::kSrcOver, 0);
+        gfx::MaskFilterInfo(), base::nullopt, are_contents_opaque, opacity,
+        SkBlendMode::kSrcOver, 0);
     occluded_quad->SetNew(shared_quad_state_occluded, occluded_quad_rect,
                           occluded_quad_rect, SK_ColorRED, false);
 
     shared_quad_state_with_rrect->SetAll(
-        gfx::Transform(), quad_rect, quad_rect, mask_filter_info, quad_rect,
-        is_clipped, are_contents_opaque, opacity, SkBlendMode::kSrcOver, 0);
+        gfx::Transform(), quad_rect, quad_rect, mask_filter_info, base::nullopt,
+        are_contents_opaque, opacity, SkBlendMode::kSrcOver, 0);
     rounded_corner_quad->SetNew(shared_quad_state_with_rrect, quad_rect,
                                 quad_rect, SK_ColorBLUE, false);
 
@@ -4081,7 +4048,6 @@
   };
   gfx::Rect occluded_sqs_rect(0, 0, 1200, 510);
 
-  const bool is_clipped = false;
   const bool are_contents_opaque = true;
   const float opacity = 1.f;
   SharedQuadState* shared_quad_state_occluder =
@@ -4098,15 +4064,14 @@
   {
     shared_quad_state_occluder->SetAll(
         gfx::Transform(), occluding_rect, occluding_rect, gfx::MaskFilterInfo(),
-        occluding_rect, is_clipped, are_contents_opaque, opacity,
-        SkBlendMode::kSrcOver, 0);
+        base::nullopt, are_contents_opaque, opacity, SkBlendMode::kSrcOver, 0);
     quads[0]->SetNew(shared_quad_state_occluder, occluding_rect, occluding_rect,
                      SK_ColorRED, false);
 
-    shared_quad_state_occluded->SetAll(
-        gfx::Transform(), occluded_sqs_rect, occluded_sqs_rect,
-        gfx::MaskFilterInfo(), occluded_sqs_rect, is_clipped,
-        are_contents_opaque, opacity, SkBlendMode::kSrcOver, 0);
+    shared_quad_state_occluded->SetAll(gfx::Transform(), occluded_sqs_rect,
+                                       occluded_sqs_rect, gfx::MaskFilterInfo(),
+                                       base::nullopt, are_contents_opaque,
+                                       opacity, SkBlendMode::kSrcOver, 0);
     for (int i = 1; i < 4; i++) {
       quads[i]->SetNew(shared_quad_state_occluded, quad_rects[i - 1],
                        quad_rects[i - 1], SK_ColorRED, false);
@@ -4156,7 +4121,6 @@
 
   AggregatedFrame frame = MakeDefaultAggregatedFrame();
 
-  const bool is_clipped = false;
   const bool are_contents_opaque = true;
   const float opacity = 1.f;
 
@@ -4201,7 +4165,7 @@
     SharedQuadState* shared_quad_state_occluder =
         frame.render_pass_list.front()->CreateAndAppendSharedQuadState();
     shared_quad_state_occluder->SetAll(
-        gfx::Transform(), r, r, gfx::MaskFilterInfo(), r, is_clipped,
+        gfx::Transform(), r, r, gfx::MaskFilterInfo(), base::nullopt,
         are_contents_opaque, opacity, SkBlendMode::kSrcOver, 0);
     SolidColorDrawQuad* quad =
         frame.render_pass_list.front()
@@ -4215,8 +4179,7 @@
         frame.render_pass_list.front()->CreateAndAppendSharedQuadState();
     shared_quad_state_occluded->SetAll(
         gfx::Transform(), occluded_rect, occluded_rect, gfx::MaskFilterInfo(),
-        occluded_rect, is_clipped, are_contents_opaque, opacity,
-        SkBlendMode::kSrcOver, 0);
+        base::nullopt, are_contents_opaque, opacity, SkBlendMode::kSrcOver, 0);
     SolidColorDrawQuad* occluded_quad =
         frame.render_pass_list.front()
             ->quad_list.AllocateAndConstruct<SolidColorDrawQuad>();
@@ -4270,7 +4233,6 @@
 
   AggregatedFrame frame = MakeDefaultAggregatedFrame();
 
-  const bool is_clipped = false;
   const bool are_contents_opaque = true;
   const float opacity = 1.f;
 
@@ -4283,8 +4245,7 @@
           ->quad_list.AllocateAndConstruct<SolidColorDrawQuad>();
   shared_quad_state_occluding->SetAll(
       gfx::Transform(), occluding_rect, occluding_rect, gfx::MaskFilterInfo(),
-      occluding_rect, is_clipped, are_contents_opaque, opacity,
-      SkBlendMode::kSrcOver, 0);
+      base::nullopt, are_contents_opaque, opacity, SkBlendMode::kSrcOver, 0);
   occluding_quad->SetNew(shared_quad_state_occluding, occluding_rect,
                          occluding_rect, SK_ColorRED, false);
   // Occluded quad.
@@ -4296,8 +4257,7 @@
           ->quad_list.AllocateAndConstruct<SolidColorDrawQuad>();
   shared_quad_state_occluded->SetAll(
       gfx::Transform(), occluded_rect, occluded_rect, gfx::MaskFilterInfo(),
-      occluded_rect, is_clipped, are_contents_opaque, opacity,
-      SkBlendMode::kSrcOver, 0);
+      base::nullopt, are_contents_opaque, opacity, SkBlendMode::kSrcOver, 0);
   occluded_quad->SetNew(shared_quad_state_occluded, occluded_rect,
                         occluded_rect, SK_ColorRED, false);
 
@@ -4343,7 +4303,6 @@
   occluded_sqs_rect.Union(occluded_quad_rect_3);
   occluded_sqs_rect.Union(occluded_quad_rect_4);
 
-  bool is_clipped = false;
   bool are_contents_opaque = true;
   float opacity = 1.f;
   SharedQuadState* shared_quad_state_with_rrect =
@@ -4368,10 +4327,10 @@
           ->quad_list.AllocateAndConstruct<SolidColorDrawQuad>();
 
   {
-    shared_quad_state_occluded->SetAll(
-        gfx::Transform(), occluded_sqs_rect, occluded_sqs_rect,
-        gfx::MaskFilterInfo(), occluded_sqs_rect, is_clipped,
-        are_contents_opaque, opacity, SkBlendMode::kSrcOver, 0);
+    shared_quad_state_occluded->SetAll(gfx::Transform(), occluded_sqs_rect,
+                                       occluded_sqs_rect, gfx::MaskFilterInfo(),
+                                       base::nullopt, are_contents_opaque,
+                                       opacity, SkBlendMode::kSrcOver, 0);
     occluded_quad_1->SetNew(shared_quad_state_occluded, occluded_quad_rect_1,
                             occluded_quad_rect_1, SK_ColorRED, false);
     occluded_quad_2->SetNew(shared_quad_state_occluded, occluded_quad_rect_2,
@@ -4382,8 +4341,8 @@
                             occluded_quad_rect_4, SK_ColorRED, false);
 
     shared_quad_state_with_rrect->SetAll(
-        gfx::Transform(), quad_rect, quad_rect, mask_filter_info, quad_rect,
-        is_clipped, are_contents_opaque, opacity, SkBlendMode::kSrcOver, 0);
+        gfx::Transform(), quad_rect, quad_rect, mask_filter_info, base::nullopt,
+        are_contents_opaque, opacity, SkBlendMode::kSrcOver, 0);
     rounded_corner_quad->SetNew(shared_quad_state_with_rrect, quad_rect,
                                 quad_rect, SK_ColorBLUE, false);
 
diff --git a/components/viz/service/display/renderer_perftest.cc b/components/viz/service/display/renderer_perftest.cc
index 63c7620..a7bbc0bf 100644
--- a/components/viz/service/display/renderer_perftest.cc
+++ b/components/viz/service/display/renderer_perftest.cc
@@ -131,15 +131,13 @@
     const gfx::MaskFilterInfo& mask_filter_info) {
   const gfx::Rect layer_rect = rect;
   const gfx::Rect visible_layer_rect = rect;
-  const gfx::Rect clip_rect = rect;
-  const bool is_clipped = false;
   const bool are_contents_opaque = false;
   const float opacity = 1.0f;
   const SkBlendMode blend_mode = SkBlendMode::kSrcOver;
   const int sorting_context_id = 0;
   SharedQuadState* shared_state = render_pass->CreateAndAppendSharedQuadState();
   shared_state->SetAll(quad_to_target_transform, layer_rect, visible_layer_rect,
-                       mask_filter_info, clip_rect, is_clipped,
+                       mask_filter_info, /*clip_rect=*/base::nullopt,
                        are_contents_opaque, opacity, blend_mode,
                        sorting_context_id);
   return shared_state;
diff --git a/components/viz/service/display/software_renderer_unittest.cc b/components/viz/service/display/software_renderer_unittest.cc
index 750b922..a14cccb 100644
--- a/components/viz/service/display/software_renderer_unittest.cc
+++ b/components/viz/service/display/software_renderer_unittest.cc
@@ -155,7 +155,7 @@
   SharedQuadState* shared_quad_state =
       root_render_pass->CreateAndAppendSharedQuadState();
   shared_quad_state->SetAll(gfx::Transform(), outer_rect, outer_rect,
-                            gfx::MaskFilterInfo(), outer_rect, false, true, 1.0,
+                            gfx::MaskFilterInfo(), base::nullopt, true, 1.0,
                             SkBlendMode::kSrcOver, 0);
   auto* inner_quad =
       root_render_pass->CreateAndAppendDrawQuad<SolidColorDrawQuad>();
@@ -223,7 +223,7 @@
   SharedQuadState* shared_quad_state =
       root_render_pass->CreateAndAppendSharedQuadState();
   shared_quad_state->SetAll(gfx::Transform(), outer_rect, outer_rect,
-                            gfx::MaskFilterInfo(), outer_rect, false, true, 1.0,
+                            gfx::MaskFilterInfo(), base::nullopt, true, 1.0,
                             SkBlendMode::kSrcOver, 0);
   auto* inner_quad = root_render_pass->CreateAndAppendDrawQuad<TileDrawQuad>();
   inner_quad->SetNew(shared_quad_state, inner_rect, inner_rect, needs_blending,
@@ -285,7 +285,7 @@
   SharedQuadState* shared_quad_state =
       root_render_pass->CreateAndAppendSharedQuadState();
   shared_quad_state->SetAll(gfx::Transform(), tile_rect, tile_rect,
-                            gfx::MaskFilterInfo(), tile_rect, false, true, 1.0,
+                            gfx::MaskFilterInfo(), base::nullopt, true, 1.0,
                             SkBlendMode::kSrcOver, 0);
   auto* quad = root_render_pass->CreateAndAppendDrawQuad<TileDrawQuad>();
   quad->SetNew(shared_quad_state, tile_rect, tile_rect, needs_blending,
@@ -447,7 +447,7 @@
         root_pass->CreateAndAppendSharedQuadState();
     shared_quad_state->SetAll(gfx::Transform(), outer_rect, outer_rect,
                               gfx::MaskFilterInfo(), gfx::Rect(1, 1, 30, 30),
-                              true, true, 1.0, SkBlendMode::kSrcOver, 0);
+                              true, 1.0, SkBlendMode::kSrcOver, 0);
     auto* outer_quad = root_pass->CreateAndAppendDrawQuad<SolidColorDrawQuad>();
     outer_quad->SetNew(shared_quad_state, outer_rect, outer_rect, SK_ColorGREEN,
                        false);
@@ -463,7 +463,7 @@
     shared_quad_state->SetAll(
         gfx::Transform(), inner_rect, inner_rect,
         gfx::MaskFilterInfo(gfx::RRectF(gfx::RectF(5, 5, 10, 10), 2)),
-        inner_rect, false, true, 1.0, SkBlendMode::kSrcOver, 0);
+        base::nullopt, true, 1.0, SkBlendMode::kSrcOver, 0);
     auto* inner_quad = root_pass->CreateAndAppendDrawQuad<SolidColorDrawQuad>();
     inner_quad->SetNew(shared_quad_state, inner_rect, inner_rect, SK_ColorRED,
                        false);
diff --git a/components/viz/service/display_embedder/image_context_impl.cc b/components/viz/service/display_embedder/image_context_impl.cc
index bcaa036..5002a214 100644
--- a/components/viz/service/display_embedder/image_context_impl.cc
+++ b/components/viz/service/display_embedder/image_context_impl.cc
@@ -139,8 +139,7 @@
   if (BindOrCopyTextureIfNecessary(texture_base, &texture_size) &&
       texture_size != size()) {
     DLOG(ERROR) << "Failed to fulfill the promise texture - texture "
-                   "size does not match TransferableResource size: "
-                << texture_size.ToString() << " vs " << size().ToString();
+                   "size does not match TransferableResource size.";
     CreateFallbackImage(context_state);
     return;
   }
@@ -203,9 +202,7 @@
 
     if (representation->size() != size()) {
       DLOG(ERROR) << "Failed to fulfill the promise texture - SharedImage "
-                     "size does not match TransferableResource size: "
-                  << representation->size().ToString() << " vs "
-                  << size().ToString();
+                     "size does not match TransferableResource size.";
       return false;
     }
 
diff --git a/components/viz/service/transitions/surface_animation_manager.cc b/components/viz/service/transitions/surface_animation_manager.cc
index 882c207..452fd21 100644
--- a/components/viz/service/transitions/surface_animation_manager.cc
+++ b/components/viz/service/transitions/surface_animation_manager.cc
@@ -110,8 +110,8 @@
       /*quad_layer_rect=*/rect,
       /*visible_layer_rect=*/rect,
       /*mask_filter_info=*/gfx::MaskFilterInfo(),
-      /*clip_rect=*/gfx::Rect(),
-      /*is_clipped=*/false, /*are_contents_opaque=*/false,
+      /*clip_rect=*/base::nullopt,
+      /*are_contents_opaque=*/false,
       /*opacity=*/opacity,
       /*blend_mode=*/SkBlendMode::kSrcOver, /*sorting_context_id=*/0);
 
diff --git a/components/web_package/web_bundle_parser.cc b/components/web_package/web_bundle_parser.cc
index 9d3a4f5e..eeca882a 100644
--- a/components/web_package/web_bundle_parser.cc
+++ b/components/web_package/web_bundle_parser.cc
@@ -13,6 +13,7 @@
 #include "base/memory/weak_ptr.h"
 #include "base/numerics/checked_math.h"
 #include "base/strings/string_number_conversions.h"
+#include "base/strings/string_piece.h"
 #include "base/strings/string_util.h"
 #include "components/cbor/reader.h"
 #include "components/web_package/web_bundle_utils.h"
@@ -138,7 +139,7 @@
       DCHECK(!result.pseudos.contains(name));
       // Step 4.2.2. Set pseudos[name] to value.
       result.pseudos.insert(
-          std::make_pair(name.as_string(), value.as_string()));
+          std::make_pair(std::string(name), std::string(value)));
       // Step 4.3.3. Continue.
       continue;
     }
@@ -156,7 +157,8 @@
     DCHECK(!result.headers.contains(name));
 
     // Step 4.5. Append (name, value) to headers.
-    result.headers.insert(std::make_pair(name.as_string(), value.as_string()));
+    result.headers.insert(
+        std::make_pair(std::string(name), std::string(value)));
   }
 
   // Step 5. Return (headers, pseudos).
@@ -758,7 +760,7 @@
       }
       requests.insert(std::make_pair(
           parsed_url,
-          mojom::BundleIndexValue::New(variants_value.as_string(),
+          mojom::BundleIndexValue::New(std::string(variants_value),
                                        std::move(response_locations))));
     }
 
@@ -1051,7 +1053,7 @@
       }
       subset_hashes.insert(std::make_pair(
           parsed_url,
-          mojom::SubsetHashesValue::New(variants_value.as_string(),
+          mojom::SubsetHashesValue::New(std::string(variants_value),
                                         std::move(resource_integrities))));
     }
 
diff --git a/content/browser/accessibility/accessibility_event_recorder_uia_win.cc b/content/browser/accessibility/accessibility_event_recorder_uia_win.cc
index 7be8433..5538780 100644
--- a/content/browser/accessibility/accessibility_event_recorder_uia_win.cc
+++ b/content/browser/accessibility/accessibility_event_recorder_uia_win.cc
@@ -8,6 +8,8 @@
 #include <numeric>
 #include <utility>
 
+#include <psapi.h>
+
 #include "base/strings/string_util.h"
 #include "base/strings/stringprintf.h"
 #include "base/strings/utf_string_conversions.h"
@@ -16,6 +18,7 @@
 #include "base/win/scoped_safearray.h"
 #include "base/win/scoped_variant.h"
 #include "base/win/windows_version.h"
+#include "build/build_config.h"
 #include "content/browser/accessibility/browser_accessibility_com_win.h"
 #include "content/browser/accessibility/browser_accessibility_manager.h"
 #include "content/browser/accessibility/browser_accessibility_manager_win.h"
@@ -30,6 +33,25 @@
 
 namespace {
 
+#if defined(COMPILER_MSVC)
+#define RETURN_ADDRESS() _ReturnAddress()
+#elif defined(COMPILER_GCC) && !defined(OS_NACL)
+#define RETURN_ADDRESS() \
+  __builtin_extract_return_addr(__builtin_return_address(0))
+#else
+#define RETURN_ADDRESS() nullptr
+#endif
+
+static std::pair<uintptr_t, uintptr_t> GetModuleAddressRange(
+    const wchar_t* module_name) {
+  MODULEINFO info;
+  CHECK(GetModuleInformation(GetCurrentProcess(), GetModuleHandle(module_name),
+                             &info, sizeof(info)));
+
+  const uintptr_t start = reinterpret_cast<uintptr_t>(info.lpBaseOfDll);
+  return std::make_pair(start, start + info.SizeOfImage);
+}
+
 std::string UiaIdentifierToStringPretty(int32_t id) {
   auto str = base::WideToUTF8(UiaIdentifierToString(id));
   // Remove UIA_ prefix, and EventId/PropertyId suffixes
@@ -175,66 +197,10 @@
   // Wait for shutdown signal
   shutdown_signal_.Wait();
 
-  // Due to a bug in Windows (fixed in Windows 10 19H1), events are raised
-  // exactly twice for any in-proc off-thread event listeners. We filter out the
-  // duplicate events here, and forward the remaining events to our owner.
   {
     base::AutoLock lock{on_event_lock_};
-    if (event_logs_.size() == 1) {
-      // Only received events on a single thread... perhaps the bug was fixed?
-      // Forward all events.
-      for (auto&& event : event_logs_.begin()->second)
-        owner_->OnEvent(event);
-    } else if (event_logs_.size() == 2) {
-      // Events were raised on two threads, as expected.  Sort the lists and
-      // forward events, eliminating duplicates that occur in both threads.
-      auto&& events_thread1 = event_logs_.begin()->second;
-      auto&& events_thread2 = (++event_logs_.begin())->second;
-
-      std::sort(events_thread1.begin(), events_thread1.end());
-      std::sort(events_thread2.begin(), events_thread2.end());
-
-      auto it1 = events_thread1.begin();
-      auto it2 = events_thread2.begin();
-      while (it1 < events_thread1.end() && it2 < events_thread2.end()) {
-        if (*it1 == *it2) {
-          owner_->OnEvent(*it1);
-          it1++;
-          it2++;
-        } else if (*it1 < *it2) {
-          owner_->OnEvent(*it1);
-          it1++;
-        } else {
-          owner_->OnEvent(*it2);
-          it2++;
-        }
-      }
-      while (it1 < events_thread1.end())
-        owner_->OnEvent(*it1++);
-      while (it2 < events_thread2.end())
-        owner_->OnEvent(*it2++);
-    } else {
-      // Typically we'll get events on exactly two threads (one directly from
-      // UIA, the second from RPC), but sometimes RPC will split its events
-      // across different threads.
-      //
-      // Unfortunately, there is no robust method of eliminating duplicates in
-      // this case.  Tests with intentional duplicates could run afoul of this
-      // logic in rare scenarios; it is recommended that intentionally
-      // duplicated events be avoided in tests, when possible.
-      std::vector<std::string> combined;
-      for (auto&& log : event_logs_)
-        combined.insert(combined.end(), log.second.begin(), log.second.end());
-      std::sort(combined.begin(), combined.end());
-
-      std::string last_event;
-      for (auto&& event : combined) {
-        if (last_event != event)
-          owner_->OnEvent(last_event = event);
-        else
-          last_event = {};
-      }
-    }
+    for (const std::string& event : event_logs_)
+      owner_->OnEvent(event);
   }
 
   // Cleanup
@@ -251,24 +217,27 @@
 }
 
 void AccessibilityEventRecorderUia::Thread::SendShutdownSignal() {
-  // We expect to see the shutdown sentinel exactly twice (due to the Windows
-  // bug detailed in |ThreadMain| and fixed in 19H1), so don't actually shut
-  // down the thread until the second call.
-  if (shutdown_sentinel_received_ ||
-      base::win::GetVersion() >= base::win::Version::WIN10_19H1)
-    shutdown_signal_.Signal();
-  else
-    shutdown_sentinel_received_ = true;
+  shutdown_signal_.Signal();
 }
 
 void AccessibilityEventRecorderUia::Thread::OnEvent(const std::string& event) {
   // We need to synchronize event logging, since UIA event callbacks can be
   // coming from multiple threads.
   base::AutoLock lock{on_event_lock_};
-  event_logs_[base::PlatformThread::CurrentId()].push_back(event);
+  event_logs_.push_back(event);
 }
 
-AccessibilityEventRecorderUia::Thread::EventHandler::EventHandler() {}
+AccessibilityEventRecorderUia::Thread::EventHandler::EventHandler() {
+  // Some events are duplicated between UIAutomationCore.dll and RPCRT4.dll.
+  // On Win10, events are mainly sent from UIAutomationCore.dll with some
+  // duplicates sent from RPCRT4.dll.
+  // On Win7, events are mainly sent from RPCRT4.dll, with a few duplicates sent
+  // from UIAutomationCore.dll.
+  allowed_module_address_range_ = GetModuleAddressRange(
+      (base::win::GetVersion() == base::win::Version::WIN7)
+          ? L"RPCRT4.dll"
+          : L"UIAutomationCore.dll");
+}
 
 AccessibilityEventRecorderUia::Thread::EventHandler::~EventHandler() {}
 
@@ -287,7 +256,7 @@
 STDMETHODIMP
 AccessibilityEventRecorderUia::Thread::EventHandler::HandleFocusChangedEvent(
     IUIAutomationElement* sender) {
-  if (!owner_)
+  if (!owner_ || !IsCallerFromAllowedModule(RETURN_ADDRESS()))
     return S_OK;
 
   base::win::ScopedSafearray id;
@@ -319,17 +288,18 @@
     IUIAutomationElement* sender,
     PROPERTYID property_id,
     VARIANT new_value) {
-  if (owner_) {
-    std::string prop_str = UiaIdentifierToStringPretty(property_id);
-    if (prop_str.empty()) {
-      VLOG(1) << "Ignoring UIA property-changed event " << property_id;
-      return S_OK;
-    }
+  if (!owner_ || !IsCallerFromAllowedModule(RETURN_ADDRESS()))
+    return S_OK;
 
-    std::string log = base::StringPrintf("%s changed %s", prop_str.c_str(),
-                                         GetSenderInfo(sender).c_str());
-    owner_->OnEvent(log);
+  std::string prop_str = UiaIdentifierToStringPretty(property_id);
+  if (prop_str.empty()) {
+    VLOG(1) << "Ignoring UIA property-changed event " << property_id;
+    return S_OK;
   }
+
+  std::string log = base::StringPrintf("%s changed %s", prop_str.c_str(),
+                                       GetSenderInfo(sender).c_str());
+  owner_->OnEvent(log);
   return S_OK;
 }
 
@@ -338,34 +308,35 @@
     HandleStructureChangedEvent(IUIAutomationElement* sender,
                                 StructureChangeType change_type,
                                 SAFEARRAY* runtime_id) {
-  if (owner_) {
-    std::string type_str;
-    switch (change_type) {
-      case StructureChangeType_ChildAdded:
-        type_str = "ChildAdded";
-        break;
-      case StructureChangeType_ChildRemoved:
-        type_str = "ChildRemoved";
-        break;
-      case StructureChangeType_ChildrenInvalidated:
-        type_str = "ChildrenInvalidated";
-        break;
-      case StructureChangeType_ChildrenBulkAdded:
-        type_str = "ChildrenBulkAdded";
-        break;
-      case StructureChangeType_ChildrenBulkRemoved:
-        type_str = "ChildrenBulkRemoved";
-        break;
-      case StructureChangeType_ChildrenReordered:
-        type_str = "ChildrenReordered";
-        break;
-    }
+  if (!owner_ || !IsCallerFromAllowedModule(RETURN_ADDRESS()))
+    return S_OK;
 
-    std::string log =
-        base::StringPrintf("StructureChanged/%s %s", type_str.c_str(),
-                           GetSenderInfo(sender).c_str());
-    owner_->OnEvent(log);
+  std::string type_str;
+  switch (change_type) {
+    case StructureChangeType_ChildAdded:
+      type_str = "ChildAdded";
+      break;
+    case StructureChangeType_ChildRemoved:
+      type_str = "ChildRemoved";
+      break;
+    case StructureChangeType_ChildrenInvalidated:
+      type_str = "ChildrenInvalidated";
+      break;
+    case StructureChangeType_ChildrenBulkAdded:
+      type_str = "ChildrenBulkAdded";
+      break;
+    case StructureChangeType_ChildrenBulkRemoved:
+      type_str = "ChildrenBulkRemoved";
+      break;
+    case StructureChangeType_ChildrenReordered:
+      type_str = "ChildrenReordered";
+      break;
   }
+
+  std::string log =
+      base::StringPrintf("StructureChanged/%s %s", type_str.c_str(),
+                         GetSenderInfo(sender).c_str());
+  owner_->OnEvent(log);
   return S_OK;
 }
 
@@ -373,33 +344,45 @@
 AccessibilityEventRecorderUia::Thread::EventHandler::HandleAutomationEvent(
     IUIAutomationElement* sender,
     EVENTID event_id) {
-  if (owner_) {
-    if (event_id == owner_->shutdown_sentinel_) {
-      // This is a sentinel value that tells us the tests are finished.
-      owner_->SendShutdownSignal();
-    } else {
-      std::string event_str = UiaIdentifierToStringPretty(event_id);
-      if (event_str.empty()) {
-        VLOG(1) << "Ignoring UIA automation event " << event_id;
-        return S_OK;
-      }
+  if (!owner_ || !IsCallerFromAllowedModule(RETURN_ADDRESS()))
+    return S_OK;
 
-      // Remove duplicate menuclosed events with no event data.
-      // The "duplicates" are benign. UIA currently duplicates *all* events for
-      // in-process listeners, and the event-recorder tries to eliminate the
-      // duplicates... but since the recorder sometimes isn't able to retrieve
-      // the role, the duplicate-elimination logic doesn't see them as
-      // duplicates in this case.
-      std::string sender_info =
-          event_id == UIA_MenuClosedEventId ? "" : GetSenderInfo(sender);
-      std::string log =
-          base::StringPrintf("%s %s", event_str.c_str(), sender_info.c_str());
-      owner_->OnEvent(log);
+  if (event_id == owner_->shutdown_sentinel_) {
+    // This is a sentinel value that tells us the tests are finished.
+    owner_->SendShutdownSignal();
+  } else {
+    std::string event_str = UiaIdentifierToStringPretty(event_id);
+    if (event_str.empty()) {
+      VLOG(1) << "Ignoring UIA automation event " << event_id;
+      return S_OK;
     }
+
+    // Remove duplicate menuclosed events with no event data.
+    // The "duplicates" are benign. UIA currently duplicates *all* events for
+    // in-process listeners, and the event-recorder tries to eliminate the
+    // duplicates... but since the recorder sometimes isn't able to retrieve
+    // the role, the duplicate-elimination logic doesn't see them as
+    // duplicates in this case.
+    std::string sender_info =
+        event_id == UIA_MenuClosedEventId ? "" : GetSenderInfo(sender);
+    std::string log =
+        base::StringPrintf("%s %s", event_str.c_str(), sender_info.c_str());
+    owner_->OnEvent(log);
   }
   return S_OK;
 }
 
+// Due to a bug in Windows (fixed in Windows 10 19H1, but found broken in 20H2),
+// events are raised exactly twice for any in-proc off-thread event listeners.
+// To avoid this, in UIA API methods we can pass the RETURN_ADDRESS() to this
+// method to determine whether the caller belongs to a specific platform module.
+bool AccessibilityEventRecorderUia::Thread::EventHandler::
+    IsCallerFromAllowedModule(void* return_address) {
+  const auto address = reinterpret_cast<uintptr_t>(return_address);
+  return address >= allowed_module_address_range_.first &&
+         address < allowed_module_address_range_.second;
+}
+
 std::string AccessibilityEventRecorderUia::Thread::EventHandler::GetSenderInfo(
     IUIAutomationElement* sender) {
   std::string sender_info;
diff --git a/content/browser/accessibility/accessibility_event_recorder_uia_win.h b/content/browser/accessibility/accessibility_event_recorder_uia_win.h
index 8809a4a..601fa625 100644
--- a/content/browser/accessibility/accessibility_event_recorder_uia_win.h
+++ b/content/browser/accessibility/accessibility_event_recorder_uia_win.h
@@ -9,8 +9,8 @@
 #include <stdint.h>
 #include <uiautomation.h>
 #include <wrl/client.h>
-#include <map>
 #include <string>
+#include <utility>
 #include <vector>
 
 #include "base/atomicops.h"
@@ -67,12 +67,11 @@
     base::OnceClosure initialization_complete_;
     base::OnceClosure shutdown_complete_;
     base::WaitableEvent shutdown_signal_;
-    bool shutdown_sentinel_received_ = false;
 
     // Thread-specific wrapper for OnEvent to handle necessary locking
     void OnEvent(const std::string& event);
     base::Lock on_event_lock_;
-    std::map<base::PlatformThreadId, std::vector<std::string>> event_logs_;
+    std::vector<std::string> event_logs_;
 
     // An implementation of various UIA interfaces that forward event
     // notifications to the owning event recorder.
@@ -119,6 +118,9 @@
       AccessibilityEventRecorderUia::Thread* owner_ = nullptr;
 
      private:
+      std::pair<uintptr_t, uintptr_t> allowed_module_address_range_;
+      bool IsCallerFromAllowedModule(void* return_address);
+
       std::string GetSenderInfo(IUIAutomationElement* sender);
 
       Microsoft::WRL::ComPtr<IUIAutomationElement> root_;
diff --git a/content/browser/accessibility/accessibility_tree_formatter_win.cc b/content/browser/accessibility/accessibility_tree_formatter_win.cc
index d5b8585..81f421ff2 100644
--- a/content/browser/accessibility/accessibility_tree_formatter_win.cc
+++ b/content/browser/accessibility/accessibility_tree_formatter_win.cc
@@ -26,12 +26,14 @@
 #include "content/browser/accessibility/browser_accessibility_win.h"
 #include "content/public/browser/ax_inspect_factory.h"
 #include "third_party/iaccessible2/ia2_api_all.h"
+#include "ui/accessibility/platform/inspect/ax_inspect_utils.h"
 #include "ui/accessibility/platform/inspect/ax_inspect_utils_win.h"
 #include "ui/base/win/atl_module.h"
 #include "ui/gfx/win/hwnd_util.h"
 
 namespace content {
 
+using ui::AXFormatValue;
 using ui::IAccessible2RoleToString;
 using ui::IAccessible2StateToStringVector;
 using ui::IAccessibleStateToStringVector;
@@ -835,30 +837,6 @@
       continue;
 
     switch (value->type()) {
-      case base::Value::Type::STRING: {
-        std::string string_value;
-        value->GetAsString(&string_value);
-        WriteAttribute(
-            false,
-            base::StringPrintf("%s='%s'", attribute_name, string_value.c_str()),
-            &line);
-        break;
-      }
-      case base::Value::Type::INTEGER: {
-        WriteAttribute(false,
-                       base::StringPrintf("%s=%d", attribute_name,
-                                          value->GetIfInt().value_or(0)),
-                       &line);
-        break;
-      }
-      case base::Value::Type::DOUBLE: {
-        double double_value = 0.0;
-        value->GetAsDouble(&double_value);
-        WriteAttribute(
-            false, base::StringPrintf("%s=%.2f", attribute_name, double_value),
-            &line);
-        break;
-      }
       case base::Value::Type::LIST: {
         // Currently all list values are string and are written without
         // attribute names.
@@ -892,7 +870,10 @@
         break;
       }
       default:
-        NOTREACHED();
+        WriteAttribute(false,
+                       base::StringPrintf("%s=%s", attribute_name,
+                                          AXFormatValue(*value).c_str()),
+                       &line);
         break;
     }
   }
diff --git a/content/browser/cache_storage/cache_storage_histogram_utils.cc b/content/browser/cache_storage/cache_storage_histogram_utils.cc
index c845596..e51830f 100644
--- a/content/browser/cache_storage/cache_storage_histogram_utils.cc
+++ b/content/browser/cache_storage/cache_storage_histogram_utils.cc
@@ -5,6 +5,7 @@
 #include "content/browser/cache_storage/cache_storage_histogram_utils.h"
 #include "base/metrics/histogram_functions.h"
 #include "base/metrics/histogram_macros.h"
+#include "base/strings/string_piece.h"
 
 namespace content {
 
@@ -92,9 +93,9 @@
 std::string GetClientHistogramName(CacheStorageSchedulerUMA uma_type,
                                    CacheStorageSchedulerClient client_type) {
   std::string histogram_name("ServiceWorkerCache");
-  histogram_name.append(ClientToName(client_type).as_string());
+  histogram_name.append(std::string(ClientToName(client_type)));
   histogram_name.append(".Scheduler");
-  histogram_name.append(UMAToName(uma_type).as_string());
+  histogram_name.append(std::string(UMAToName(uma_type)));
   return histogram_name;
 }
 
@@ -111,7 +112,7 @@
   base::UmaHistogramCounts10000(histogram_name, value);
   if (!ShouldRecordOpUMA(op_type))
     return;
-  histogram_name.append(OpToName(op_type).as_string());
+  histogram_name.append(std::string(OpToName(op_type)));
   base::UmaHistogramCounts10000(histogram_name, value);
 }
 
@@ -127,7 +128,7 @@
   base::UmaHistogramLongTimes(histogram_name, value);
   if (!ShouldRecordOpUMA(op_type))
     return;
-  histogram_name.append(OpToName(op_type).as_string());
+  histogram_name.append(std::string(OpToName(op_type)));
   base::UmaHistogramLongTimes(histogram_name, value);
 }
 
diff --git a/content/browser/download/save_file_manager.cc b/content/browser/download/save_file_manager.cc
index 91786d976..0a6f1b0 100644
--- a/content/browser/download/save_file_manager.cc
+++ b/content/browser/download/save_file_manager.cc
@@ -9,6 +9,7 @@
 #include "base/bind.h"
 #include "base/files/file_util.h"
 #include "base/logging.h"
+#include "base/strings/string_piece.h"
 #include "base/strings/string_util.h"
 #include "build/build_config.h"
 #include "components/download/public/common/download_task_runner.h"
@@ -118,7 +119,7 @@
     download::GetDownloadTaskRunner()->PostTask(
         FROM_HERE,
         base::BindOnce(&SaveFileManager::UpdateSaveProgress, save_file_manager_,
-                       save_item_id_, string_piece.as_string()));
+                       save_item_id_, std::string(string_piece)));
     std::move(resume).Run();
   }
 
diff --git a/content/browser/gpu/viz_devtools_connector.cc b/content/browser/gpu/viz_devtools_connector.cc
index 39bd4a3b..37fec11 100644
--- a/content/browser/gpu/viz_devtools_connector.cc
+++ b/content/browser/gpu/viz_devtools_connector.cc
@@ -12,6 +12,7 @@
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/content_browser_client.h"
 #include "content/public/common/content_client.h"
+#include "content/public/common/content_features.h"
 #include "mojo/public/cpp/bindings/pending_receiver.h"
 
 namespace content {
@@ -24,8 +25,12 @@
   int port = 0;
   if (local_addr)
     port = local_addr->port();
-  GetIOThreadTaskRunner({})->PostTask(
-      FROM_HERE, base::BindOnce(std::move(callback), result, port));
+  if (base::FeatureList::IsEnabled(features::kProcessHostOnUI)) {
+    std::move(callback).Run(result, port);
+  } else {
+    GetIOThreadTaskRunner({})->PostTask(
+        FROM_HERE, base::BindOnce(std::move(callback), result, port));
+  }
 }
 
 void CreateSocketOnUiThread(
@@ -55,7 +60,7 @@
   mojo::PendingReceiver<network::mojom::TCPServerSocket>
       server_socket_receiver = server_socket.InitWithNewPipeAndPassReceiver();
   // Jump to the UI thread to get the network context, create the socket, then
-  // jump back to the IO thread to complete the callback.
+  // jump back to the process thread to complete the callback.
   GetUIThreadTaskRunner({})->PostTask(
       FROM_HERE,
       base::BindOnce(
diff --git a/content/browser/indexed_db/indexed_db_backing_store.cc b/content/browser/indexed_db/indexed_db_backing_store.cc
index e468c46d..8b86e51 100644
--- a/content/browser/indexed_db/indexed_db_backing_store.cc
+++ b/content/browser/indexed_db/indexed_db_backing_store.cc
@@ -19,6 +19,7 @@
 #include "base/metrics/histogram_functions.h"
 #include "base/optional.h"
 #include "base/sequence_checker.h"
+#include "base/strings/string_piece.h"
 #include "base/strings/string_util.h"
 #include "base/strings/stringprintf.h"
 #include "base/strings/utf_string_conversions.h"
@@ -996,7 +997,7 @@
                  leveldb_env::MakeSlice(iterator->Key()), max_key) < 0;
            status = iterator->Next()) {
         std::vector<IndexedDBExternalObject> temp_external_objects;
-        DecodeExternalObjects(iterator->Value().as_string(),
+        DecodeExternalObjects(std::string(iterator->Value()),
                               &temp_external_objects);
         for (auto& object : temp_external_objects) {
           if (object.object_type() !=
@@ -1261,7 +1262,7 @@
     return InternalInconsistencyStatus();
   }
 
-  record->bits = slice.as_string();
+  record->bits = std::string(slice);
   return transaction->GetExternalObjectsForRecord(database_id, leveldb_key,
                                                   record);
 }
@@ -2010,7 +2011,7 @@
       INTERNAL_READ_ERROR(FIND_KEY_IN_INDEX);
       return InternalInconsistencyStatus();
     }
-    *found_encoded_primary_key = slice.as_string();
+    *found_encoded_primary_key = std::string(slice);
 
     bool exists = false;
     s = indexed_db::VersionExists(leveldb_transaction, database_id,
@@ -2354,13 +2355,13 @@
       // If we've found a new key, remember it and keep going.
       if (!duplicate_key.IsValid()) {
         duplicate_key = *current_key_;
-        earliest_duplicate = iterator_->Key().as_string();
+        earliest_duplicate = std::string(iterator_->Key());
         continue;
       }
 
       // If we're still seeing duplicates, keep going.
       if (duplicate_key.Equals(*current_key_)) {
-        earliest_duplicate = iterator_->Key().as_string();
+        earliest_duplicate = std::string(iterator_->Key());
         continue;
       }
     }
@@ -2597,11 +2598,11 @@
   record_identifier_.Reset(encoded_key, version);
 
   *s = transaction_->GetExternalObjectsForRecord(
-      database_id_, iterator_->Key().as_string(), &current_value_);
+      database_id_, std::string(iterator_->Key()), &current_value_);
   if (!s->ok())
     return false;
 
-  current_value_.bits = value_slice.as_string();
+  current_value_.bits = std::string(value_slice);
   return true;
 }
 
@@ -2883,7 +2884,7 @@
     return false;
   }
 
-  current_value_.bits = slice.as_string();
+  current_value_.bits = std::string(slice);
   *s = transaction_->GetExternalObjectsForRecord(
       database_id_, primary_leveldb_key_, &current_value_);
   return s->ok();
diff --git a/content/browser/indexed_db/indexed_db_leveldb_operations.cc b/content/browser/indexed_db/indexed_db_leveldb_operations.cc
index 4b13eec..e3e0cd3 100644
--- a/content/browser/indexed_db/indexed_db_leveldb_operations.cc
+++ b/content/browser/indexed_db/indexed_db_leveldb_operations.cc
@@ -7,6 +7,7 @@
 #include "base/json/json_reader.h"
 #include "base/metrics/histogram_functions.h"
 #include "base/no_destructor.h"
+#include "base/strings/string_piece.h"
 #include "base/values.h"
 #include "build/chromeos_buildflags.h"
 #include "components/services/storage/indexed_db/scopes/leveldb_scopes.h"
@@ -500,7 +501,7 @@
   }
 
   do {
-    *found_key = it->Key().as_string();
+    *found_key = std::string(it->Key());
 
     // There can be several index keys that compare equal. We want the last one.
     *s = it->Next();
diff --git a/content/browser/indexed_db/indexed_db_tombstone_sweeper.cc b/content/browser/indexed_db/indexed_db_tombstone_sweeper.cc
index 5976b4b0..d36b023 100644
--- a/content/browser/indexed_db/indexed_db_tombstone_sweeper.cc
+++ b/content/browser/indexed_db/indexed_db_tombstone_sweeper.cc
@@ -10,6 +10,7 @@
 #include "base/rand_util.h"
 #include "base/sequenced_task_runner.h"
 #include "base/strings/string_number_conversions.h"
+#include "base/strings/string_piece.h"
 #include "base/threading/sequenced_task_runner_handle.h"
 #include "base/time/tick_clock.h"
 #include "components/services/storage/indexed_db/scopes/varint_coding.h"
@@ -414,7 +415,7 @@
       }
       continue;
     }
-    std::string encoded_primary_key = index_value_str.as_string();
+    std::string encoded_primary_key(index_value_str);
     std::string exists_key = ExistsEntryKey::Encode(
         database_id, object_store_id, encoded_primary_key);
 
diff --git a/content/browser/isolated_origin_util.cc b/content/browser/isolated_origin_util.cc
index 82d72f1c..87410df 100644
--- a/content/browser/isolated_origin_util.cc
+++ b/content/browser/isolated_origin_util.cc
@@ -7,6 +7,7 @@
 #include "content/browser/isolated_origin_util.h"
 
 #include "base/logging.h"
+#include "base/strings/string_piece.h"
 #include "base/strings/string_util.h"
 #include "net/base/registry_controlled_domains/registry_controlled_domain.h"
 #include "services/network/public/cpp/is_potentially_trustworthy.h"
@@ -35,7 +36,7 @@
     IsolatedOriginPattern&& other) = default;
 
 bool IsolatedOriginPattern::Parse(const base::StringPiece& unparsed_pattern) {
-  pattern_ = unparsed_pattern.as_string();
+  pattern_ = std::string(unparsed_pattern);
   origin_ = url::Origin();
   isolate_all_subdomains_ = false;
   is_valid_ = false;
diff --git a/content/browser/loader/cors_file_origin_browsertest.cc b/content/browser/loader/cors_file_origin_browsertest.cc
index 42f1913..ee79b76 100644
--- a/content/browser/loader/cors_file_origin_browsertest.cc
+++ b/content/browser/loader/cors_file_origin_browsertest.cc
@@ -10,6 +10,7 @@
 #include "base/files/file_path.h"
 #include "base/macros.h"
 #include "base/path_service.h"
+#include "base/strings/string_piece.h"
 #include "base/strings/stringprintf.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/synchronization/waitable_event.h"
@@ -272,7 +273,7 @@
   // Fetching http resources should be allowed by CORS when
   // universal access from file URLs is requested.
   std::string fetch_result = EvalJs(shell(), script).ExtractString();
-  fetch_result = TrimWhitespaceASCII(fetch_result, base::TRIM_ALL).as_string();
+  fetch_result = std::string(TrimWhitespaceASCII(fetch_result, base::TRIM_ALL));
   EXPECT_THAT(fetch_result, ::testing::HasSubstr("SUCCESS:"));
   EXPECT_THAT(fetch_result, ::testing::HasSubstr("This page has a title"));
 }
diff --git a/content/browser/loader/cross_site_document_blocking_browsertest.cc b/content/browser/loader/cross_site_document_blocking_browsertest.cc
index e6fc4884..03c57f2 100644
--- a/content/browser/loader/cross_site_document_blocking_browsertest.cc
+++ b/content/browser/loader/cross_site_document_blocking_browsertest.cc
@@ -14,6 +14,7 @@
 #include "base/files/file_util.h"
 #include "base/macros.h"
 #include "base/strings/pattern.h"
+#include "base/strings/string_piece.h"
 #include "base/strings/string_util.h"
 #include "base/strings/stringprintf.h"
 #include "base/strings/utf_string_conversions.h"
@@ -746,7 +747,7 @@
       EvalJs(shell(), JsReplace("fetch($1).then(response => response.text())",
                                 resource_url))
           .ExtractString();
-  fetch_result = TrimWhitespaceASCII(fetch_result, base::TRIM_ALL).as_string();
+  fetch_result = std::string(TrimWhitespaceASCII(fetch_result, base::TRIM_ALL));
 
   // Verify that the response was not blocked.
   EXPECT_EQ("runMe({ \"name\" : \"chromium\" });", fetch_result);
diff --git a/content/browser/loader/object_navigation_fallback_body_loader.cc b/content/browser/loader/object_navigation_fallback_body_loader.cc
index 8cb2708..e90e769 100644
--- a/content/browser/loader/object_navigation_fallback_body_loader.cc
+++ b/content/browser/loader/object_navigation_fallback_body_loader.cc
@@ -25,6 +25,7 @@
 #include "services/network/public/mojom/url_response_head.mojom.h"
 #include "url/gurl.h"
 #include "url/origin.h"
+#include "url/url_util.h"
 
 namespace content {
 
@@ -185,8 +186,9 @@
   // The final value for `transfer_size`, as well as `encoded_body_size`, and
   // `decoded_body_size` will be populated after loading the body.
   timing_info->did_reuse_connection = response_head.load_timing.socket_reused;
-  timing_info->is_secure_context =
-      network::IsUrlPotentiallyTrustworthy(common_params.url);
+  // Use url::Origin to handle cases like blob:https://.
+  timing_info->is_secure_transport = base::Contains(
+      url::GetSecureSchemes(), url::Origin::Create(common_params.url).scheme());
   timing_info->allow_negative_values = false;
   return timing_info;
 }
diff --git a/content/browser/media/cdm_registry_impl.cc b/content/browser/media/cdm_registry_impl.cc
index 313e6cfe..df7d580 100644
--- a/content/browser/media/cdm_registry_impl.cc
+++ b/content/browser/media/cdm_registry_impl.cc
@@ -76,7 +76,7 @@
 bool CdmRegistryImpl::FinalizeCdmCapability(
     const std::string& key_system,
     CdmInfo::Robustness robustness,
-    base::Optional<CdmCapability> cdm_capability) {
+    base::Optional<media::CdmCapability> cdm_capability) {
   base::AutoLock auto_lock(lock_);
 
   auto itr = cdms_.begin();
diff --git a/content/browser/media/cdm_registry_impl.h b/content/browser/media/cdm_registry_impl.h
index 5fbd175..ca286b1d 100644
--- a/content/browser/media/cdm_registry_impl.h
+++ b/content/browser/media/cdm_registry_impl.h
@@ -14,6 +14,7 @@
 #include "content/common/content_export.h"
 #include "content/public/browser/cdm_registry.h"
 #include "content/public/common/cdm_info.h"
+#include "media/cdm/cdm_capability.h"
 
 namespace content {
 
@@ -38,9 +39,10 @@
   // removed if `cdm_capability` is null, since the CDM does not support any
   // capability. Returns whether the CdmInfo was successfully updated with a
   // valid CdmCapability.
-  bool FinalizeCdmCapability(const std::string& key_system,
-                             CdmInfo::Robustness robustness,
-                             base::Optional<CdmCapability> cdm_capability);
+  bool FinalizeCdmCapability(
+      const std::string& key_system,
+      CdmInfo::Robustness robustness,
+      base::Optional<media::CdmCapability> cdm_capability);
 
  private:
   friend class CdmRegistryImplTest;
diff --git a/content/browser/media/cdm_registry_impl_unittest.cc b/content/browser/media/cdm_registry_impl_unittest.cc
index 5050be3b..a0362f0 100644
--- a/content/browser/media/cdm_registry_impl_unittest.cc
+++ b/content/browser/media/cdm_registry_impl_unittest.cc
@@ -18,6 +18,7 @@
 #include "base/version.h"
 #include "content/public/common/cdm_info.h"
 #include "media/base/video_codecs.h"
+#include "media/cdm/cdm_capability.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace content {
@@ -67,8 +68,8 @@
   ~CdmRegistryImplTest() override {}
 
  protected:
-  CdmCapability GetTestCdmCapability() {
-    return CdmCapability(
+  media::CdmCapability GetTestCdmCapability() {
+    return media::CdmCapability(
         {media::kCodecVP8, media::kCodecVP9}, {EncryptionScheme::kCenc},
         {CdmSessionType::kTemporary, CdmSessionType::kPersistentLicense});
   }
diff --git a/content/browser/media/key_system_support_impl.cc b/content/browser/media/key_system_support_impl.cc
index 0b9d954..8bb568b7 100644
--- a/content/browser/media/key_system_support_impl.cc
+++ b/content/browser/media/key_system_support_impl.cc
@@ -49,7 +49,7 @@
 // Returns a CdmCapability with codecs specified on command line. Returns null
 // if kOverrideHardwareSecureCodecsForTesting was not specified or not valid
 // codecs specified.
-base::Optional<CdmCapability>
+base::Optional<media::CdmCapability>
 GetHardwareSecureCapabilityOverriddenFromCommandLine() {
   auto* command_line = base::CommandLine::ForCurrentProcess();
   if (!command_line || !command_line->HasSwitch(
@@ -81,7 +81,7 @@
   }
 
   // Overridden codecs assume CENC/CBCS and temporary session support.
-  return CdmCapability(
+  return media::CdmCapability(
       std::move(video_codecs),
       {media::EncryptionScheme::kCenc, media::EncryptionScheme::kCbcs},
       {media::CdmSessionType::kTemporary});
@@ -89,7 +89,7 @@
 
 // Software secure capability can be obtained synchronously in all supported
 // cases. If needed, this can be easily converted to an asynchronous call.
-base::Optional<CdmCapability> GetSoftwareSecureCapability(
+base::Optional<media::CdmCapability> GetSoftwareSecureCapability(
     const std::string& key_system) {
   auto cdm_info = CdmRegistryImpl::GetInstance()->GetCdmInfo(
       key_system, CdmInfo::Robustness::kSoftwareSecure);
@@ -111,7 +111,7 @@
 
 // Trying to get hardware secure capability synchronously. If lazy
 // initialization is needed, set `lazy_initialize` to true.
-base::Optional<CdmCapability> GetHardwareSecureCapability(
+base::Optional<media::CdmCapability> GetHardwareSecureCapability(
     const std::string& key_system,
     bool* lazy_initialize) {
   *lazy_initialize = false;
@@ -237,7 +237,7 @@
     const std::string& key_system,
     IsKeySystemSupportedCallback callback,
     bool lazy_initialize,
-    base::Optional<CdmCapability> hw_secure_capability) {
+    base::Optional<media::CdmCapability> hw_secure_capability) {
   // See comment above. This could be called multiple times when we have
   // parallel `IsKeySystemSupported()` calls from different renderer processes.
   // This is okay and won't cause collision or corruption of data.
diff --git a/content/browser/media/key_system_support_impl.h b/content/browser/media/key_system_support_impl.h
index 32fb96ec..5a97c1f 100644
--- a/content/browser/media/key_system_support_impl.h
+++ b/content/browser/media/key_system_support_impl.h
@@ -12,6 +12,7 @@
 #include "base/optional.h"
 #include "content/common/content_export.h"
 #include "content/public/common/cdm_info.h"
+#include "media/cdm/cdm_capability.h"
 #include "media/mojo/mojom/key_system_support.mojom.h"
 #include "mojo/public/cpp/bindings/pending_receiver.h"
 
@@ -37,7 +38,7 @@
   friend class KeySystemSupportImplTest;
 
   using CdmCapabilityCB =
-      base::OnceCallback<void(base::Optional<CdmCapability>)>;
+      base::OnceCallback<void(base::Optional<media::CdmCapability>)>;
   using HardwareSecureCapabilityCB =
       base::RepeatingCallback<void(const std::string&, CdmCapabilityCB)>;
 
@@ -52,7 +53,7 @@
       const std::string& key_system,
       IsKeySystemSupportedCallback callback,
       bool lazy_initialize,
-      base::Optional<CdmCapability> hw_secure_capability);
+      base::Optional<media::CdmCapability> hw_secure_capability);
 
   HardwareSecureCapabilityCB hw_secure_capability_cb_for_testing_;
 
diff --git a/content/browser/media/key_system_support_impl_unittest.cc b/content/browser/media/key_system_support_impl_unittest.cc
index aa8218d2..5aa910c 100644
--- a/content/browser/media/key_system_support_impl_unittest.cc
+++ b/content/browser/media/key_system_support_impl_unittest.cc
@@ -22,6 +22,7 @@
 #include "media/base/decrypt_config.h"
 #include "media/base/media_switches.h"
 #include "media/base/video_codecs.h"
+#include "media/cdm/cdm_capability.h"
 #include "mojo/public/cpp/bindings/remote.h"
 #include "mojo/public/cpp/bindings/self_owned_receiver.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -88,8 +89,8 @@
         key_system_support_.BindNewPipeAndPassReceiver());
   }
 
-  CdmCapability TestCdmCapability() {
-    return CdmCapability(
+  media::CdmCapability TestCdmCapability() {
+    return media::CdmCapability(
         {VideoCodec::kCodecVP8, VideoCodec::kCodecVP9},
         {EncryptionScheme::kCenc, EncryptionScheme::kCbcs},
         {CdmSessionType::kTemporary, CdmSessionType::kPersistentLicense});
@@ -98,7 +99,7 @@
   // Registers |key_system| with |capability|. All other values for CdmInfo have
   // some default value as they're not returned by IsKeySystemSupported().
   void Register(const std::string& key_system,
-                base::Optional<CdmCapability> capability,
+                base::Optional<media::CdmCapability> capability,
                 Robustness robustness = Robustness::kSoftwareSecure) {
     DVLOG(1) << __func__;
 
diff --git a/content/browser/media/key_system_support_win.cc b/content/browser/media/key_system_support_win.cc
index b4dae77..750226a 100644
--- a/content/browser/media/key_system_support_win.cc
+++ b/content/browser/media/key_system_support_win.cc
@@ -34,7 +34,7 @@
   // TODO(xhwang/jrummell): Support hardware session types. Now only assume
   // temporary session support.
   std::move(cdm_capability_cb)
-      .Run(CdmCapability(
+      .Run(media::CdmCapability(
           key_system_capability->hw_secure_video_codecs,
           VectorToSet(key_system_capability->hw_secure_encryption_schemes),
           {media::CdmSessionType::kTemporary}));
diff --git a/content/browser/media/key_system_support_win.h b/content/browser/media/key_system_support_win.h
index 031e811..e2d0f4f 100644
--- a/content/browser/media/key_system_support_win.h
+++ b/content/browser/media/key_system_support_win.h
@@ -11,10 +11,12 @@
 #include "base/files/file_path.h"
 #include "base/optional.h"
 #include "content/public/common/cdm_info.h"
+#include "media/cdm/cdm_capability.h"
 
 namespace content {
 
-using CdmCapabilityCB = base::OnceCallback<void(base::Optional<CdmCapability>)>;
+using CdmCapabilityCB =
+    base::OnceCallback<void(base::Optional<media::CdmCapability>)>;
 
 // Returns the hardware secure CdmCapability supported in MediaFoundationService
 // for `key_system` by the CDM located in `cdm_path`.
diff --git a/content/browser/net/cross_origin_embedder_policy_reporter.cc b/content/browser/net/cross_origin_embedder_policy_reporter.cc
index 97939dd..7d6f45a 100644
--- a/content/browser/net/cross_origin_embedder_policy_reporter.cc
+++ b/content/browser/net/cross_origin_embedder_policy_reporter.cc
@@ -4,6 +4,7 @@
 
 #include "content/browser/net/cross_origin_embedder_policy_reporter.h"
 
+#include "base/strings/string_piece.h"
 #include "base/values.h"
 #include "content/public/browser/storage_partition.h"
 #include "services/network/public/cpp/request_destination.h"
@@ -94,7 +95,7 @@
 
     for (const auto& pair : body) {
       list.push_back(blink::mojom::ReportBodyElement::New(
-          pair.first.as_string(), pair.second.as_string()));
+          std::string(pair.first), std::string(pair.second)));
     }
     list.push_back(
         blink::mojom::ReportBodyElement::New("disposition", disposition));
diff --git a/content/browser/net/cross_origin_embedder_policy_reporter_unittest.cc b/content/browser/net/cross_origin_embedder_policy_reporter_unittest.cc
index 1dde8704..b8aad0fa 100644
--- a/content/browser/net/cross_origin_embedder_policy_reporter_unittest.cc
+++ b/content/browser/net/cross_origin_embedder_policy_reporter_unittest.cc
@@ -6,6 +6,7 @@
 
 #include <vector>
 
+#include "base/strings/string_piece.h"
 #include "base/test/task_environment.h"
 #include "base/values.h"
 #include "content/public/test/test_storage_partition.h"
@@ -165,19 +166,19 @@
       RequestDestination destination,
       base::StringPiece disposition) const {
     return {std::make_pair("type", "corp"),
-            std::make_pair("blockedURL", blocked_url.as_string()),
+            std::make_pair("blockedURL", std::string(blocked_url)),
             std::make_pair("destination",
                            network::RequestDestinationToString(destination)),
-            std::make_pair("disposition", disposition.as_string())};
+            std::make_pair("disposition", std::string(disposition))};
   }
 
   std::vector<std::pair<std::string, std::string>> CreateBodyInternal(
       base::StringPiece type,
       base::StringPiece blocked_url,
       base::StringPiece disposition) const {
-    return {std::make_pair("type", type.as_string()),
-            std::make_pair("blockedURL", blocked_url.as_string()),
-            std::make_pair("disposition", disposition.as_string())};
+    return {std::make_pair("type", std::string(type)),
+            std::make_pair("blockedURL", std::string(blocked_url)),
+            std::make_pair("disposition", std::string(disposition))};
   }
 
   base::test::TaskEnvironment task_environment_;
diff --git a/content/browser/renderer_host/back_forward_cache_impl.h b/content/browser/renderer_host/back_forward_cache_impl.h
index b269b08..3398726 100644
--- a/content/browser/renderer_host/back_forward_cache_impl.h
+++ b/content/browser/renderer_host/back_forward_cache_impl.h
@@ -157,9 +157,6 @@
       int navigation_entry_id,
       blink::mojom::PageRestoreParamsPtr page_restore_params);
 
-  // Evict all entries from the BackForwardCache.
-  void Flush();
-
   // Evict all cached pages in the same BrowsingInstance as
   // |site_instance|.
   void EvictFramesInRelatedSiteInstances(SiteInstance* site_instance);
@@ -215,6 +212,8 @@
 
   const std::list<std::unique_ptr<Entry>>& GetEntries();
 
+  // BackForwardCache overrides:
+  void Flush() override;
   void DisableForTesting(DisableForTestingReason reason) override;
 
   // RenderProcessHostInternalObserver methods
diff --git a/content/browser/renderer_host/navigation_controller_impl.cc b/content/browser/renderer_host/navigation_controller_impl.cc
index b4eccf6f..2caf747 100644
--- a/content/browser/renderer_host/navigation_controller_impl.cc
+++ b/content/browser/renderer_host/navigation_controller_impl.cc
@@ -44,6 +44,7 @@
 #include "base/logging.h"
 #include "base/metrics/histogram_macros.h"
 #include "base/stl_util.h"
+#include "base/strings/string_piece.h"
 #include "base/strings/string_util.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/time/time.h"
@@ -3714,9 +3715,8 @@
   if (frame_entry->method() == "POST") {
     request_body = frame_entry->GetPostData(&post_content_type);
     // Might have a LF at end.
-    post_content_type =
-        base::TrimWhitespaceASCII(post_content_type, base::TRIM_ALL)
-            .as_string();
+    post_content_type = std::string(
+        base::TrimWhitespaceASCII(post_content_type, base::TRIM_ALL));
   }
 
   // Create the NavigationParams based on |entry| and |frame_entry|.
diff --git a/content/browser/renderer_host/render_frame_host_impl.cc b/content/browser/renderer_host/render_frame_host_impl.cc
index 775e9bd..003477c 100644
--- a/content/browser/renderer_host/render_frame_host_impl.cc
+++ b/content/browser/renderer_host/render_frame_host_impl.cc
@@ -33,6 +33,7 @@
 #include "base/process/kill.h"
 #include "base/stl_util.h"
 #include "base/strings/string_number_conversions.h"
+#include "base/strings/string_piece.h"
 #include "base/strings/stringprintf.h"
 #include "base/system/sys_info.h"
 #include "base/task/current_thread.h"
diff --git a/content/browser/renderer_host/render_frame_host_manager_unittest.cc b/content/browser/renderer_host/render_frame_host_manager_unittest.cc
index 0298a07..98b2677 100644
--- a/content/browser/renderer_host/render_frame_host_manager_unittest.cc
+++ b/content/browser/renderer_host/render_frame_host_manager_unittest.cc
@@ -20,6 +20,7 @@
 #include "base/macros.h"
 #include "base/memory/scoped_refptr.h"
 #include "base/run_loop.h"
+#include "base/strings/string_piece.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/test/metrics/histogram_tester.h"
 #include "base/test/scoped_feature_list.h"
@@ -383,9 +384,8 @@
     if (frame_entry->method() == "POST") {
       request_body = frame_entry->GetPostData(&post_content_type);
       // Might have a LF at end.
-      post_content_type =
-          base::TrimWhitespaceASCII(post_content_type, base::TRIM_ALL)
-              .as_string();
+      post_content_type = std::string(
+          base::TrimWhitespaceASCII(post_content_type, base::TRIM_ALL));
     }
 
     auto& referrer = frame_entry->referrer();
diff --git a/content/browser/sms/sms_parser.cc b/content/browser/sms/sms_parser.cc
index e8f2488..fe8b502 100644
--- a/content/browser/sms/sms_parser.cc
+++ b/content/browser/sms/sms_parser.cc
@@ -8,6 +8,7 @@
 #include "content/browser/sms/sms_parser.h"
 
 #include "base/optional.h"
+#include "base/strings/string_piece.h"
 #include "net/base/url_util.h"
 #include "third_party/re2/src/re2/re2.h"
 #include "url/gurl.h"
@@ -80,7 +81,7 @@
   // TODO(yigu): The existing kOtpFormatRegex may filter out invalid SMSes that
   // would fall into |kHostAndPortNotParsed| or |kGURLNotValid| below. We should
   // clean up the code if the statement is confirmed by metrics.
-  if (!re2::RE2::PartialMatch(sms.as_string(), kOtpFormatRegex, &top_domain,
+  if (!re2::RE2::PartialMatch(std::string(sms), kOtpFormatRegex, &top_domain,
                               &otp, &embedded_domain))
     return Result(SmsParsingStatus::kOTPFormatRegexNotMatch);
 
diff --git a/content/browser/url_loader_factory_params_helper.cc b/content/browser/url_loader_factory_params_helper.cc
index 6823b84..bb8587f 100644
--- a/content/browser/url_loader_factory_params_helper.cc
+++ b/content/browser/url_loader_factory_params_helper.cc
@@ -6,6 +6,7 @@
 
 #include "base/command_line.h"
 #include "base/optional.h"
+#include "base/strings/string_piece.h"
 #include "content/browser/devtools/network_service_devtools_observer.h"
 #include "content/browser/renderer_host/render_frame_host_impl.h"
 #include "content/browser/storage_partition_impl.h"
@@ -105,7 +106,7 @@
   params->url_loader_network_observer = std::move(url_loader_network_observer);
   params->devtools_observer = std::move(devtools_observer);
 
-  params->debug_tag = debug_tag.as_string();
+  params->debug_tag = std::string(debug_tag);
 
   return params;
 }
diff --git a/content/browser/utility_process_sandbox_browsertest.cc b/content/browser/utility_process_sandbox_browsertest.cc
index 0d1c1ca..793f48cf 100644
--- a/content/browser/utility_process_sandbox_browsertest.cc
+++ b/content/browser/utility_process_sandbox_browsertest.cc
@@ -119,7 +119,7 @@
       case SandboxType::kCdm:
       case SandboxType::kPpapi:
       case SandboxType::kPrintCompositor:
-      case SandboxType::kSharingService:
+      case SandboxType::kService:
       case SandboxType::kUtility: {
         constexpr int kExpectedFullSandboxFlags =
             SandboxLinux::kPIDNS | SandboxLinux::kNetNS |
diff --git a/content/browser/utility_sandbox_delegate.cc b/content/browser/utility_sandbox_delegate.cc
index e6e51bb..5413414 100644
--- a/content/browser/utility_sandbox_delegate.cc
+++ b/content/browser/utility_sandbox_delegate.cc
@@ -63,7 +63,7 @@
 #endif  // BUILDFLAG(IS_CHROMEOS_ASH)
       sandbox_type_ == sandbox::policy::SandboxType::kAudio ||
 #if !defined(OS_MAC)
-      sandbox_type_ == sandbox::policy::SandboxType::kSharingService ||
+      sandbox_type_ == sandbox::policy::SandboxType::kService ||
 #endif
       sandbox_type_ == sandbox::policy::SandboxType::kSpeechRecognition;
   DCHECK(supported_sandbox_type);
diff --git a/content/browser/utility_sandbox_delegate_win.cc b/content/browser/utility_sandbox_delegate_win.cc
index 8683cd37..d2ea209b 100644
--- a/content/browser/utility_sandbox_delegate_win.cc
+++ b/content/browser/utility_sandbox_delegate_win.cc
@@ -252,7 +252,7 @@
     policy->SetTokenLevel(sandbox::USER_UNPROTECTED, sandbox::USER_UNPROTECTED);
   }
 
-  if (sandbox_type_ == sandbox::policy::SandboxType::kSharingService) {
+  if (sandbox_type_ == sandbox::policy::SandboxType::kService) {
     auto result = sandbox::policy::SandboxWin::AddWin32kLockdownPolicy(policy);
     if (result != sandbox::SBOX_ALL_OK)
       return false;
diff --git a/content/browser/web_package/signed_exchange_certificate_chain.cc b/content/browser/web_package/signed_exchange_certificate_chain.cc
index ceb3dbf..473032e 100644
--- a/content/browser/web_package/signed_exchange_certificate_chain.cc
+++ b/content/browser/web_package/signed_exchange_certificate_chain.cc
@@ -8,6 +8,7 @@
 #include "base/format_macros.h"
 #include "base/memory/ptr_util.h"
 #include "base/no_destructor.h"
+#include "base/strings/string_piece.h"
 #include "base/strings/string_split.h"
 #include "base/strings/stringprintf.h"
 #include "base/trace_event/trace_event.h"
@@ -108,7 +109,7 @@
             "ocsp is not a bytestring, or not found in the first cert map.");
         return nullptr;
       }
-      ocsp = ocsp_iter->second.GetBytestringAsString().as_string();
+      ocsp = std::string(ocsp_iter->second.GetBytestringAsString());
       if (ocsp.empty()) {
         signed_exchange_utils::ReportErrorAndTraceEvent(
             devtools_proxy, "ocsp must not be empty.");
@@ -137,7 +138,7 @@
               devtools_proxy, "sct is not a bytestring.");
           return nullptr;
         }
-        sct = sct_iter->second.GetBytestringAsString().as_string();
+        sct = std::string(sct_iter->second.GetBytestringAsString());
         if (sct.empty()) {
           signed_exchange_utils::ReportErrorAndTraceEvent(
               devtools_proxy, "sct must not be empty.");
diff --git a/content/browser/web_package/signed_exchange_envelope.cc b/content/browser/web_package/signed_exchange_envelope.cc
index d48f4bb4..05508dd 100644
--- a/content/browser/web_package/signed_exchange_envelope.cc
+++ b/content/browser/web_package/signed_exchange_envelope.cc
@@ -181,7 +181,7 @@
       signed_exchange_utils::ReportErrorAndTraceEvent(
           devtools_proxy,
           base::StringPrintf("Invalid header name. header_name: %s",
-                             name_str.as_string().c_str()));
+                             std::string(name_str).c_str()));
       return false;
     }
 
@@ -195,7 +195,7 @@
           devtools_proxy,
           base::StringPrintf(
               "Response header name should be lower-cased. header_name: %s",
-              name_str.as_string().c_str()));
+              std::string(name_str).c_str()));
       return false;
     }
 
@@ -206,7 +206,7 @@
           devtools_proxy,
           base::StringPrintf(
               "Exchange contains stateful response header. header_name: %s",
-              name_str.as_string().c_str()));
+              std::string(name_str).c_str()));
       return false;
     }
 
@@ -220,7 +220,7 @@
       signed_exchange_utils::ReportErrorAndTraceEvent(
           devtools_proxy,
           base::StringPrintf("Duplicate header value. header_name: %s",
-                             name_str.as_string().c_str()));
+                             std::string(name_str).c_str()));
       return false;
     }
   }
@@ -353,22 +353,22 @@
 
 bool SignedExchangeEnvelope::AddResponseHeader(base::StringPiece name,
                                                base::StringPiece value) {
-  std::string name_str = name.as_string();
+  std::string name_str(name);
   DCHECK_EQ(name_str, base::ToLowerASCII(name))
       << "Response header names should be always lower-cased.";
   if (response_headers_.find(name_str) != response_headers_.end())
     return false;
 
-  response_headers_.emplace(std::move(name_str), value.as_string());
+  response_headers_.emplace(std::move(name_str), std::string(value));
   return true;
 }
 
 void SignedExchangeEnvelope::SetResponseHeader(base::StringPiece name,
                                                base::StringPiece value) {
-  std::string name_str = name.as_string();
+  std::string name_str(name);
   DCHECK_EQ(name_str, base::ToLowerASCII(name))
       << "Response header names should be always lower-cased.";
-  response_headers_[name_str] = value.as_string();
+  response_headers_[name_str] = std::string(value);
 }
 
 scoped_refptr<net::HttpResponseHeaders>
diff --git a/content/browser/web_package/signed_exchange_utils.h b/content/browser/web_package/signed_exchange_utils.h
index 640baa9..1e09a59 100644
--- a/content/browser/web_package/signed_exchange_utils.h
+++ b/content/browser/web_package/signed_exchange_utils.h
@@ -8,6 +8,7 @@
 #include <string>
 
 #include "base/optional.h"
+#include "base/strings/string_piece.h"
 #include "content/browser/web_package/signed_exchange_consts.h"
 #include "content/browser/web_package/signed_exchange_error.h"
 #include "content/browser/web_package/signed_exchange_signature_verifier.h"
@@ -33,7 +34,7 @@
   std::string raw_string;
   URLWithRawString() = default;
   URLWithRawString(base::StringPiece url_string)
-      : url(url_string), raw_string(url_string.as_string()) {}
+      : url(url_string), raw_string(url_string) {}
 };
 
 // Utility method to call SignedExchangeDevToolsProxy::ReportError() and
diff --git a/content/browser/webui/web_ui_data_source_impl.cc b/content/browser/webui/web_ui_data_source_impl.cc
index c85f7cf..c6e8441 100644
--- a/content/browser/webui/web_ui_data_source_impl.cc
+++ b/content/browser/webui/web_ui_data_source_impl.cc
@@ -15,6 +15,7 @@
 #include "base/check_op.h"
 #include "base/macros.h"
 #include "base/memory/ref_counted_memory.h"
+#include "base/strings/string_piece.h"
 #include "base/strings/string_util.h"
 #include "base/strings/utf_string_conversions.h"
 #include "content/grit/content_resources.h"
@@ -138,20 +139,20 @@
                                     const std::u16string& value) {
   // TODO(dschuyler): Share only one copy of these strings.
   localized_strings_.SetKey(name, base::Value(value));
-  replacements_[name.as_string()] = base::UTF16ToUTF8(value);
+  replacements_[std::string(name)] = base::UTF16ToUTF8(value);
 }
 
 void WebUIDataSourceImpl::AddString(base::StringPiece name,
                                     const std::string& value) {
   localized_strings_.SetKey(name, base::Value(value));
-  replacements_[name.as_string()] = value;
+  replacements_[std::string(name)] = value;
 }
 
 void WebUIDataSourceImpl::AddLocalizedString(base::StringPiece name, int ids) {
   std::string utf8_str =
       base::UTF16ToUTF8(GetContentClient()->GetLocalizedString(ids));
   localized_strings_.SetKey(name, base::Value(utf8_str));
-  replacements_[name.as_string()] = utf8_str;
+  replacements_[std::string(name)] = utf8_str;
 }
 
 void WebUIDataSourceImpl::AddLocalizedStrings(
@@ -190,7 +191,7 @@
 
 void WebUIDataSourceImpl::AddResourcePath(base::StringPiece path,
                                           int resource_id) {
-  path_to_idr_map_[path.as_string()] = resource_id;
+  path_to_idr_map_[std::string(path)] = resource_id;
 }
 
 void WebUIDataSourceImpl::AddResourcePaths(
diff --git a/content/browser/webui/web_ui_impl.cc b/content/browser/webui/web_ui_impl.cc
index 2350d78..fc6ab53 100644
--- a/content/browser/webui/web_ui_impl.cc
+++ b/content/browser/webui/web_ui_impl.cc
@@ -12,6 +12,7 @@
 #include "base/callback_helpers.h"
 #include "base/debug/dump_without_crashing.h"
 #include "base/json/json_writer.h"
+#include "base/strings/string_piece.h"
 #include "base/strings/string_util.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/values.h"
@@ -252,7 +253,7 @@
 
 void WebUIImpl::RegisterMessageCallback(base::StringPiece message,
                                         const MessageCallback& callback) {
-  message_callbacks_.emplace(message.as_string(), callback);
+  message_callbacks_.emplace(std::string(message), callback);
 }
 
 void WebUIImpl::ProcessWebUIMessage(const GURL& source_url,
diff --git a/content/browser/worker_host/shared_worker_instance_unittest.cc b/content/browser/worker_host/shared_worker_instance_unittest.cc
index eddb330..27bb101 100644
--- a/content/browser/worker_host/shared_worker_instance_unittest.cc
+++ b/content/browser/worker_host/shared_worker_instance_unittest.cc
@@ -8,6 +8,7 @@
 #include <string>
 
 #include "base/macros.h"
+#include "base/strings/string_piece.h"
 #include "base/strings/utf_string_conversions.h"
 #include "components/services/storage/public/cpp/storage_key.h"
 #include "services/network/public/mojom/content_security_policy.mojom.h"
@@ -39,7 +40,7 @@
     } else {
       storage_key = storage::StorageKey(url::Origin::Create(GURL(url)));
     }
-    return instance.Matches(GURL(url), name.as_string(), storage_key);
+    return instance.Matches(GURL(url), std::string(name), storage_key);
   }
 
  private:
diff --git a/content/child/blink_platform_impl.cc b/content/child/blink_platform_impl.cc
index 93016b45..4e060554 100644
--- a/content/child/blink_platform_impl.cc
+++ b/content/child/blink_platform_impl.cc
@@ -20,6 +20,7 @@
 #include "base/single_thread_task_runner.h"
 #include "base/stl_util.h"
 #include "base/strings/string_number_conversions.h"
+#include "base/strings/string_piece.h"
 #include "base/strings/string_util.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/synchronization/lock.h"
@@ -184,7 +185,7 @@
   if (resource.empty())
     return WebData(resource.data(), resource.size());
   std::string uncompressed;
-  CHECK(compression::GzipUncompress(resource.as_string(), &uncompressed));
+  CHECK(compression::GzipUncompress(std::string(resource), &uncompressed));
   return WebData(uncompressed.data(), uncompressed.size());
 }
 
diff --git a/content/common/media/cdm_info.cc b/content/common/media/cdm_info.cc
index 29fa036..8a2ba58 100644
--- a/content/common/media/cdm_info.cc
+++ b/content/common/media/cdm_info.cc
@@ -8,23 +8,9 @@
 
 namespace content {
 
-CdmCapability::CdmCapability() = default;
-
-CdmCapability::CdmCapability(
-    std::vector<media::VideoCodec> video_codecs,
-    base::flat_set<media::EncryptionScheme> encryption_schemes,
-    base::flat_set<media::CdmSessionType> session_types)
-    : video_codecs(std::move(video_codecs)),
-      encryption_schemes(std::move(encryption_schemes)),
-      session_types(std::move(session_types)) {}
-
-CdmCapability::CdmCapability(const CdmCapability& other) = default;
-
-CdmCapability::~CdmCapability() = default;
-
 CdmInfo::CdmInfo(const std::string& key_system,
                  Robustness robustness,
-                 base::Optional<CdmCapability> capability,
+                 base::Optional<media::CdmCapability> capability,
                  bool supports_sub_key_systems,
                  const std::string& name,
                  const base::Token& guid,
@@ -45,7 +31,7 @@
 
 CdmInfo::CdmInfo(const std::string& key_system,
                  Robustness robustness,
-                 base::Optional<CdmCapability> capability)
+                 base::Optional<media::CdmCapability> capability)
     : key_system(key_system),
       robustness(robustness),
       capability(std::move(capability)) {
diff --git a/content/common/set_process_title_linux_unittest.cc b/content/common/set_process_title_linux_unittest.cc
index 5c47e2aa..6e5a26c1e 100644
--- a/content/common/set_process_title_linux_unittest.cc
+++ b/content/common/set_process_title_linux_unittest.cc
@@ -7,6 +7,7 @@
 
 #include "base/files/file_path.h"
 #include "base/files/file_util.h"
+#include "base/strings/string_piece.h"
 #include "base/strings/string_util.h"
 #include "content/common/set_process_title_linux.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -25,7 +26,7 @@
   // "title\0"          (on Linux 5.3--)
   //
   // For unit tests, just trim trailing null characters to support all cases.
-  return base::TrimString(cmdline, kNullChr, base::TRIM_TRAILING).as_string();
+  return std::string(base::TrimString(cmdline, kNullChr, base::TRIM_TRAILING));
 }
 
 TEST(SetProcTitleLinuxTest, Simple) {
diff --git a/content/public/browser/back_forward_cache.h b/content/public/browser/back_forward_cache.h
index 6dfbcb2..30278e2 100644
--- a/content/public/browser/back_forward_cache.h
+++ b/content/public/browser/back_forward_cache.h
@@ -126,6 +126,9 @@
     TEST_USES_UNLOAD_EVENT,
   };
 
+  // Evict all entries from the BackForwardCache.
+  virtual void Flush() = 0;
+
   // Disables the BackForwardCache so that no documents will be stored/served.
   // This allows tests to "force" not using the BackForwardCache, this can be
   // useful when:
diff --git a/content/public/common/cdm_info.h b/content/public/common/cdm_info.h
index b1eab843..9b23f3c 100644
--- a/content/public/common/cdm_info.h
+++ b/content/public/common/cdm_info.h
@@ -17,32 +17,10 @@
 #include "media/base/content_decryption_module.h"
 #include "media/base/encryption_scheme.h"
 #include "media/base/video_codecs.h"
+#include "media/cdm/cdm_capability.h"
 
 namespace content {
 
-// Capabilites supported by a Content Decryption Module.
-struct CONTENT_EXPORT CdmCapability {
-  CdmCapability();
-  CdmCapability(std::vector<media::VideoCodec> video_codecs,
-                base::flat_set<media::EncryptionScheme> encryption_schemes,
-                base::flat_set<media::CdmSessionType> session_types);
-  CdmCapability(const CdmCapability& other);
-  ~CdmCapability();
-
-  // List of video codecs supported by the CDM (e.g. vp8). This is the set of
-  // codecs that can be decrypted and decoded by the CDM. As this is generic,
-  // not all profiles or levels of the specified codecs may actually be
-  // supported.
-  // TODO(crbug.com/796725) Find a way to include profiles and levels.
-  std::vector<media::VideoCodec> video_codecs;
-
-  // List of encryption schemes supported by the CDM (e.g. cenc).
-  base::flat_set<media::EncryptionScheme> encryption_schemes;
-
-  // List of session types supported by the CDM.
-  base::flat_set<media::CdmSessionType> session_types;
-};
-
 // Represents a Content Decryption Module implementation and its capabilities.
 struct CONTENT_EXPORT CdmInfo {
   enum class Robustness {
@@ -52,7 +30,7 @@
 
   CdmInfo(const std::string& key_system,
           Robustness robustness,
-          base::Optional<CdmCapability> capability,
+          base::Optional<media::CdmCapability> capability,
           bool supports_sub_key_systems,
           const std::string& name,
           const base::Token& guid,
@@ -61,7 +39,7 @@
           const std::string& file_system_id);
   CdmInfo(const std::string& key_system,
           Robustness robustness,
-          base::Optional<CdmCapability> capability);
+          base::Optional<media::CdmCapability> capability);
   CdmInfo(const CdmInfo& other);
   ~CdmInfo();
 
@@ -78,7 +56,7 @@
   // CDM capability, e.g. video codecs, encryption schemes and session types.
   // Optional to allow lazy initialization, i.e. to populate the capability
   // after registration.
-  base::Optional<CdmCapability> capability;
+  base::Optional<media::CdmCapability> capability;
 
   // Whether we also support sub key systems of the `key_system`.
   // A sub key system to a key system is like a sub domain to a domain.
diff --git a/content/public/test/browser_test_utils.cc b/content/public/test/browser_test_utils.cc
index 345cc12..04f0158 100644
--- a/content/public/test/browser_test_utils.cc
+++ b/content/public/test/browser_test_utils.cc
@@ -1534,7 +1534,7 @@
 
         // Some source lines are huge. Elide |source_line| so that it doesn't
         // occupy more than one actual line.
-        std::string source_line = source_lines[line_number - 1].as_string();
+        std::string source_line(source_lines[line_number - 1]);
 
         int max_column_number = 60 - indent.length();
         if (column_number > max_column_number) {
diff --git a/content/public/test/test_web_ui.cc b/content/public/test/test_web_ui.cc
index 0f7b47b..0324e08 100644
--- a/content/public/test/test_web_ui.cc
+++ b/content/public/test/test_web_ui.cc
@@ -87,7 +87,7 @@
 
 void TestWebUI::RegisterMessageCallback(base::StringPiece message,
                                         const MessageCallback& callback) {
-  message_callbacks_[message.as_string()].push_back(callback);
+  message_callbacks_[std::string(message)].push_back(callback);
 }
 
 bool TestWebUI::CanCallJavascript() {
diff --git a/content/renderer/accessibility/render_accessibility_impl.cc b/content/renderer/accessibility/render_accessibility_impl.cc
index 1a7b0e3ad..4741233 100644
--- a/content/renderer/accessibility/render_accessibility_impl.cc
+++ b/content/renderer/accessibility/render_accessibility_impl.cc
@@ -828,6 +828,38 @@
   // Save the page language.
   page_language_ = root.Language().Utf8();
 
+  // Popups have a document lifecycle managed separately from the main document
+  // but we need to return a combined accessibility tree for both.
+  // We ensured layout validity for the main document in the loop above; if a
+  // popup is open, do the same for it.
+  WebDocument popup_document = GetPopupDocument();
+  if (!popup_document.IsNull()) {
+    WebAXObject popup_root_obj = WebAXObject::FromWebDocument(popup_document);
+    if (!popup_root_obj.MaybeUpdateLayoutAndCheckValidity()) {
+      // If a popup is open but we can't ensure its validity, return without
+      // sending an update bundle, the same as we would for a node in the main
+      // document.
+      return;
+    }
+  }
+
+#if DCHECK_IS_ON()
+  // Protect against lifecycle changes in the popup document, if any.
+  // If no popup document, use the main document -- it's harmless to protect it
+  // twice, and some document is needed because this cannot be done in an if
+  // statement because it's scoped.
+  WebDocument popup_or_main_document =
+      popup_document.IsNull() ? document : popup_document;
+  blink::WebDisallowTransitionScope disallow2(&popup_or_main_document);
+#endif
+
+  // Keep track of if the host node for a plugin has been invalidated,
+  // because if so, the plugin subtree will need to be re-serialized.
+  bool invalidate_plugin_subtree = false;
+  if (plugin_tree_source_ && !plugin_host_node_.IsDetached()) {
+    invalidate_plugin_subtree = !serializer_->IsInClientTree(plugin_host_node_);
+  }
+
   // Loop over each event and generate an updated event message.
   for (ui::AXEvent& event : src_events) {
     if (event.event_type == ax::mojom::Event::kLayoutComplete)
@@ -919,38 +951,6 @@
     }
   }
 
-  // Popups have a document lifecycle managed separately from the main document
-  // but we need to return a combined accessibility tree for both.
-  // We ensured layout validity for the main document in the loop above; if a
-  // popup is open, do the same for it.
-  WebDocument popup_document = GetPopupDocument();
-  if (!popup_document.IsNull()) {
-    WebAXObject popup_root_obj = WebAXObject::FromWebDocument(popup_document);
-    if (!popup_root_obj.MaybeUpdateLayoutAndCheckValidity()) {
-      // If a popup is open but we can't ensure its validity, return without
-      // sending an update bundle, the same as we would for a node in the main
-      // document.
-      return;
-    }
-  }
-
-#if DCHECK_IS_ON()
-  // Protect against lifecycle changes in the popup document, if any.
-  // If no popup document, use the main document -- it's harmless to protect it
-  // twice, and some document is needed because this cannot be done in an if
-  // statement because it's scoped.
-  WebDocument popup_or_main_document =
-      popup_document.IsNull() ? document : popup_document;
-  blink::WebDisallowTransitionScope disallow2(&popup_or_main_document);
-#endif
-
-  // Keep track of if the host node for a plugin has been invalidated,
-  // because if so, the plugin subtree will need to be re-serialized.
-  bool invalidate_plugin_subtree = false;
-  if (plugin_tree_source_ && !plugin_host_node_.IsDetached()) {
-    invalidate_plugin_subtree = !serializer_->IsInClientTree(plugin_host_node_);
-  }
-
   // Now serialize all dirty objects. Keep track of IDs serialized
   // so we don't have to serialize the same node twice.
   std::set<int32_t> already_serialized_ids;
diff --git a/content/renderer/render_process_impl.cc b/content/renderer/render_process_impl.cc
index 41234ff..832f6f5a 100644
--- a/content/renderer/render_process_impl.cc
+++ b/content/renderer/render_process_impl.cc
@@ -4,6 +4,7 @@
 
 #include "content/renderer/render_process_impl.h"
 
+#include "base/strings/string_piece.h"
 #include "build/build_config.h"
 
 #if defined(OS_WIN)
@@ -251,7 +252,7 @@
     std::vector<base::StringPiece> flag_list = base::SplitStringPiece(
         js_flags, ",", base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
     for (const auto& flag : flag_list) {
-      v8::V8::SetFlagsFromString(flag.as_string().c_str(), flag.size());
+      v8::V8::SetFlagsFromString(std::string(flag).c_str(), flag.size());
     }
   }
 }
diff --git a/content/shell/browser/shell_devtools_bindings.cc b/content/shell/browser/shell_devtools_bindings.cc
index 226a9e2..ca3c5f6 100644
--- a/content/shell/browser/shell_devtools_bindings.cc
+++ b/content/shell/browser/shell_devtools_bindings.cc
@@ -18,6 +18,7 @@
 #include "base/macros.h"
 #include "base/no_destructor.h"
 #include "base/strings/string_number_conversions.h"
+#include "base/strings/string_piece.h"
 #include "base/strings/stringprintf.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/task/post_task.h"
@@ -381,7 +382,7 @@
                                 message.size());
   if (str_message.length() < kShellMaxMessageChunkSize) {
     CallClientFunction("DevToolsAPI", "dispatchMessage",
-                       base::Value(str_message.as_string()));
+                       base::Value(std::string(str_message)));
   } else {
     size_t total_size = str_message.length();
     for (size_t pos = 0; pos < str_message.length();
@@ -391,7 +392,7 @@
 
       CallClientFunction(
           "DevToolsAPI", "dispatchMessageChunk",
-          base::Value(str_message_chunk.as_string()),
+          base::Value(std::string(str_message_chunk)),
           base::Value(base::NumberToString(pos ? 0 : total_size)));
     }
   }
diff --git a/content/test/data/accessibility/aria/aria-progressbar-expected-win.txt b/content/test/data/accessibility/aria/aria-progressbar-expected-win.txt
index 480ce35..52fadc9 100644
--- a/content/test/data/accessibility/aria/aria-progressbar-expected-win.txt
+++ b/content/test/data/accessibility/aria/aria-progressbar-expected-win.txt
@@ -1,4 +1,4 @@
 ROLE_SYSTEM_DOCUMENT READONLY FOCUSABLE
-++ROLE_SYSTEM_PROGRESSBAR value='3' READONLY xml-roles:progressbar valuetext:3 currentValue=3.00 minimumValue=1.00 maximumValue=37.00
-++ROLE_SYSTEM_PROGRESSBAR value='three' READONLY xml-roles:progressbar valuetext:three currentValue=3.00 minimumValue=1.00 maximumValue=96.00
-++ROLE_SYSTEM_PROGRESSBAR MIXED READONLY xml-roles:progressbar minimumValue=0.00 maximumValue=10.00
+++ROLE_SYSTEM_PROGRESSBAR value='3' READONLY xml-roles:progressbar valuetext:3 currentValue=3 minimumValue=1 maximumValue=37
+++ROLE_SYSTEM_PROGRESSBAR value='three' READONLY xml-roles:progressbar valuetext:three currentValue=3 minimumValue=1 maximumValue=96
+++ROLE_SYSTEM_PROGRESSBAR MIXED READONLY xml-roles:progressbar minimumValue=0 maximumValue=10
diff --git a/content/test/data/accessibility/aria/aria-scrollbar-expected-win.txt b/content/test/data/accessibility/aria/aria-scrollbar-expected-win.txt
index d57613f..b41e2c9 100644
--- a/content/test/data/accessibility/aria/aria-scrollbar-expected-win.txt
+++ b/content/test/data/accessibility/aria/aria-scrollbar-expected-win.txt
@@ -1,3 +1,3 @@
 ROLE_SYSTEM_DOCUMENT READONLY FOCUSABLE ia2_hypertext='<obj0><obj1>'
-++ROLE_SYSTEM_SCROLLBAR value='55' FOCUSABLE IA2_STATE_VERTICAL xml-roles:scrollbar currentValue=55.00
-++ROLE_SYSTEM_SCROLLBAR value='55' FOCUSABLE IA2_STATE_HORIZONTAL xml-roles:scrollbar currentValue=55.00
+++ROLE_SYSTEM_SCROLLBAR value='55' FOCUSABLE IA2_STATE_VERTICAL xml-roles:scrollbar currentValue=55
+++ROLE_SYSTEM_SCROLLBAR value='55' FOCUSABLE IA2_STATE_HORIZONTAL xml-roles:scrollbar currentValue=55
diff --git a/content/test/data/accessibility/aria/aria-scrollbar-node-expected-win.txt b/content/test/data/accessibility/aria/aria-scrollbar-node-expected-win.txt
index 4a6b3da..a374cb4a 100644
--- a/content/test/data/accessibility/aria/aria-scrollbar-node-expected-win.txt
+++ b/content/test/data/accessibility/aria/aria-scrollbar-node-expected-win.txt
@@ -1 +1 @@
-ROLE_SYSTEM_SCROLLBAR value='55' FOCUSABLE IA2_STATE_HORIZONTAL xml-roles:scrollbar currentValue=55.00
+ROLE_SYSTEM_SCROLLBAR value='55' FOCUSABLE IA2_STATE_HORIZONTAL xml-roles:scrollbar currentValue=55
diff --git a/content/test/data/accessibility/aria/aria-separator-expected-win.txt b/content/test/data/accessibility/aria/aria-separator-expected-win.txt
index 7de2852..08db73f9 100644
--- a/content/test/data/accessibility/aria/aria-separator-expected-win.txt
+++ b/content/test/data/accessibility/aria/aria-separator-expected-win.txt
@@ -1,5 +1,5 @@
 ROLE_SYSTEM_DOCUMENT READONLY FOCUSABLE
 ++ROLE_SYSTEM_STATICTEXT name='Before'
 ++ROLE_SYSTEM_SEPARATOR IA2_STATE_HORIZONTAL xml-roles:separator
-++ROLE_SYSTEM_SEPARATOR value='1' FOCUSABLE IA2_STATE_HORIZONTAL xml-roles:separator currentValue=1.00
+++ROLE_SYSTEM_SEPARATOR value='1' FOCUSABLE IA2_STATE_HORIZONTAL xml-roles:separator currentValue=1
 ++ROLE_SYSTEM_STATICTEXT name='After'
diff --git a/content/test/data/accessibility/aria/aria-slider-expected-win.txt b/content/test/data/accessibility/aria/aria-slider-expected-win.txt
index 9269bd20..dff43390 100644
--- a/content/test/data/accessibility/aria/aria-slider-expected-win.txt
+++ b/content/test/data/accessibility/aria/aria-slider-expected-win.txt
@@ -1,2 +1,2 @@
 ROLE_SYSTEM_DOCUMENT READONLY FOCUSABLE ia2_hypertext='<obj0>'
-++ROLE_SYSTEM_SLIDER value='5' IA2_STATE_HORIZONTAL xml-roles:slider currentValue=5.00 minimumValue=1.00 maximumValue=10.00
+++ROLE_SYSTEM_SLIDER value='5' IA2_STATE_HORIZONTAL xml-roles:slider currentValue=5 minimumValue=1 maximumValue=10
diff --git a/content/test/data/accessibility/aria/aria-spinbutton-expected-win.txt b/content/test/data/accessibility/aria/aria-spinbutton-expected-win.txt
index 9b3d44c..c8131e0 100644
--- a/content/test/data/accessibility/aria/aria-spinbutton-expected-win.txt
+++ b/content/test/data/accessibility/aria/aria-spinbutton-expected-win.txt
@@ -1,3 +1,3 @@
 ROLE_SYSTEM_DOCUMENT READONLY FOCUSABLE ia2_hypertext='<obj0><obj1>'
-++ROLE_SYSTEM_SPINBUTTON value='5' FOCUSABLE currentValue=5.00
-++ROLE_SYSTEM_SPINBUTTON value='5' FOCUSABLE currentValue=5.00 minimumValue=1.00 maximumValue=10.00
\ No newline at end of file
+++ROLE_SYSTEM_SPINBUTTON value='5' FOCUSABLE currentValue=5
+++ROLE_SYSTEM_SPINBUTTON value='5' FOCUSABLE currentValue=5 minimumValue=1 maximumValue=10
diff --git a/content/test/data/accessibility/aria/aria-valuemax-expected-win.txt b/content/test/data/accessibility/aria/aria-valuemax-expected-win.txt
index 20b49d4..95ffaa3 100644
--- a/content/test/data/accessibility/aria/aria-valuemax-expected-win.txt
+++ b/content/test/data/accessibility/aria/aria-valuemax-expected-win.txt
@@ -1,5 +1,5 @@
 ROLE_SYSTEM_DOCUMENT READONLY FOCUSABLE
-++ROLE_SYSTEM_PROGRESSBAR value='51' READONLY maximumValue=101.00
-++ROLE_SYSTEM_SCROLLBAR value='52' maximumValue=102.00
-++ROLE_SYSTEM_SLIDER value='53' IA2_STATE_HORIZONTAL maximumValue=103.00
-++ROLE_SYSTEM_SPINBUTTON value='54' maximumValue=104.00
\ No newline at end of file
+++ROLE_SYSTEM_PROGRESSBAR value='51' READONLY maximumValue=101
+++ROLE_SYSTEM_SCROLLBAR value='52' maximumValue=102
+++ROLE_SYSTEM_SLIDER value='53' IA2_STATE_HORIZONTAL maximumValue=103
+++ROLE_SYSTEM_SPINBUTTON value='54' maximumValue=104
diff --git a/content/test/data/accessibility/aria/aria-valuemin-expected-win.txt b/content/test/data/accessibility/aria/aria-valuemin-expected-win.txt
index 617d181..0872c17 100644
--- a/content/test/data/accessibility/aria/aria-valuemin-expected-win.txt
+++ b/content/test/data/accessibility/aria/aria-valuemin-expected-win.txt
@@ -1,5 +1,5 @@
 ROLE_SYSTEM_DOCUMENT READONLY FOCUSABLE
-++ROLE_SYSTEM_PROGRESSBAR value='51' READONLY minimumValue=1.00
-++ROLE_SYSTEM_SCROLLBAR value='52' minimumValue=2.00
-++ROLE_SYSTEM_SLIDER value='53' IA2_STATE_HORIZONTAL minimumValue=3.00
-++ROLE_SYSTEM_SPINBUTTON value='54' minimumValue=4.00
\ No newline at end of file
+++ROLE_SYSTEM_PROGRESSBAR value='51' READONLY minimumValue=1
+++ROLE_SYSTEM_SCROLLBAR value='52' minimumValue=2
+++ROLE_SYSTEM_SLIDER value='53' IA2_STATE_HORIZONTAL minimumValue=3
+++ROLE_SYSTEM_SPINBUTTON value='54' minimumValue=4
diff --git a/content/test/data/accessibility/aria/aria-valuenow-expected-win.txt b/content/test/data/accessibility/aria/aria-valuenow-expected-win.txt
index 688739a..0a1b375 100644
--- a/content/test/data/accessibility/aria/aria-valuenow-expected-win.txt
+++ b/content/test/data/accessibility/aria/aria-valuenow-expected-win.txt
@@ -1,2 +1,2 @@
 ROLE_SYSTEM_DOCUMENT READONLY FOCUSABLE
-++ROLE_SYSTEM_PROGRESSBAR value='3' READONLY xml-roles:progressbar currentValue=3.00
+++ROLE_SYSTEM_PROGRESSBAR value='3' READONLY xml-roles:progressbar currentValue=3
diff --git a/content/test/data/accessibility/aria/aria-valuetext-expected-win.txt b/content/test/data/accessibility/aria/aria-valuetext-expected-win.txt
index 0de0d11d..2ce5c07a 100644
--- a/content/test/data/accessibility/aria/aria-valuetext-expected-win.txt
+++ b/content/test/data/accessibility/aria/aria-valuetext-expected-win.txt
@@ -1,2 +1,2 @@
 ROLE_SYSTEM_DOCUMENT READONLY FOCUSABLE
-++ROLE_SYSTEM_PROGRESSBAR value='three' READONLY xml-roles:progressbar valuetext:three currentValue=3.00 minimumValue=1.00 maximumValue=96.00
+++ROLE_SYSTEM_PROGRESSBAR value='three' READONLY xml-roles:progressbar valuetext:three currentValue=3 minimumValue=1 maximumValue=96
diff --git a/content/test/data/accessibility/event/aria-combo-box-expand-expected-uia-win7.txt b/content/test/data/accessibility/event/aria-combo-box-expand-expected-uia-win7.txt
deleted file mode 100644
index 2cbca25..0000000
--- a/content/test/data/accessibility/event/aria-combo-box-expand-expected-uia-win7.txt
+++ /dev/null
@@ -1,8 +0,0 @@
-AriaProperties changed on role=combobox
-AriaProperties changed on role=option, name=Apple
-AutomationFocusChanged on role=combobox
-AutomationFocusChanged on role=option, name=Apple
-AutomationFocusChanged on role=option, name=Apple
-ExpandCollapseExpandCollapseState changed on role=combobox
-SelectionItemIsSelected changed on role=option, name=Apple
-SelectionItem_ElementSelected on role=option, name=Apple
diff --git a/content/test/data/accessibility/event/aria-combo-box-next-expected-uia-win7.txt b/content/test/data/accessibility/event/aria-combo-box-next-expected-uia-win7.txt
deleted file mode 100644
index 5813e2a..0000000
--- a/content/test/data/accessibility/event/aria-combo-box-next-expected-uia-win7.txt
+++ /dev/null
@@ -1,17 +0,0 @@
-AriaProperties changed on role=option, name=Apple
-AriaProperties changed on role=option, name=Orange
-AutomationFocusChanged on role=option, name=Apple
-AutomationFocusChanged on role=option, name=Orange
-AutomationFocusChanged on role=option, name=Orange
-SelectionItemIsSelected changed on role=option, name=Apple
-SelectionItemIsSelected changed on role=option, name=Orange
-SelectionItem_ElementSelected on role=option, name=Orange
-=== Start Continuation ===
-AriaProperties changed on role=option, name=Banana
-AriaProperties changed on role=option, name=Orange
-AutomationFocusChanged on role=option, name=Banana
-AutomationFocusChanged on role=option, name=Banana
-AutomationFocusChanged on role=option, name=Orange
-SelectionItemIsSelected changed on role=option, name=Banana
-SelectionItemIsSelected changed on role=option, name=Orange
-SelectionItem_ElementSelected on role=option, name=Banana
diff --git a/content/test/data/accessibility/event/listbox-next-expected-uia-win7.txt b/content/test/data/accessibility/event/listbox-next-expected-uia-win7.txt
deleted file mode 100644
index d5397e6..0000000
--- a/content/test/data/accessibility/event/listbox-next-expected-uia-win7.txt
+++ /dev/null
@@ -1,8 +0,0 @@
-AriaProperties changed on role=option, name=Apple
-AriaProperties changed on role=option, name=Orange
-AutomationFocusChanged on role=option, name=Apple
-AutomationFocusChanged on role=option, name=Orange
-AutomationFocusChanged on role=option, name=Orange
-SelectionItemIsSelected changed on role=option, name=Apple
-SelectionItemIsSelected changed on role=option, name=Orange
-SelectionItem_ElementSelected on role=option, name=Orange
diff --git a/content/test/data/accessibility/html/input-number-expected-win.txt b/content/test/data/accessibility/html/input-number-expected-win.txt
index ef0aad1..9325d60b 100644
--- a/content/test/data/accessibility/html/input-number-expected-win.txt
+++ b/content/test/data/accessibility/html/input-number-expected-win.txt
@@ -1,4 +1,4 @@
 ROLE_SYSTEM_DOCUMENT READONLY FOCUSABLE ia2_hypertext='<obj0>'
 ++IA2_ROLE_SECTION ia2_hypertext='<obj0><obj1>'
-++++ROLE_SYSTEM_SPINBUTTON value='1' FOCUSABLE valuetext:1 currentValue=1.00
-++++ROLE_SYSTEM_SPINBUTTON value='6' FOCUSABLE valuetext:6 currentValue=6.00 minimumValue=5.00 maximumValue=10.00
\ No newline at end of file
+++++ROLE_SYSTEM_SPINBUTTON value='1' FOCUSABLE valuetext:1 currentValue=1
+++++ROLE_SYSTEM_SPINBUTTON value='6' FOCUSABLE valuetext:6 currentValue=6 minimumValue=5 maximumValue=10
diff --git a/content/test/data/accessibility/html/input-range-expected-win.txt b/content/test/data/accessibility/html/input-range-expected-win.txt
index 378cbaac..a3828c9 100644
--- a/content/test/data/accessibility/html/input-range-expected-win.txt
+++ b/content/test/data/accessibility/html/input-range-expected-win.txt
@@ -1,3 +1,3 @@
 ROLE_SYSTEM_DOCUMENT READONLY FOCUSABLE ia2_hypertext='<obj0>'
 ++IA2_ROLE_SECTION ia2_hypertext='<obj0>'
-++++ROLE_SYSTEM_SLIDER value='5' FOCUSABLE IA2_STATE_HORIZONTAL xml-roles:slider valuetext:5 currentValue=5.00 minimumValue=1.00 maximumValue=10.00
+++++ROLE_SYSTEM_SLIDER value='5' FOCUSABLE IA2_STATE_HORIZONTAL xml-roles:slider valuetext:5 currentValue=5 minimumValue=1 maximumValue=10
diff --git a/content/test/data/accessibility/html/meter-expected-win.txt b/content/test/data/accessibility/html/meter-expected-win.txt
index 84007fa..0f757c96 100644
--- a/content/test/data/accessibility/html/meter-expected-win.txt
+++ b/content/test/data/accessibility/html/meter-expected-win.txt
@@ -1,3 +1,3 @@
 ROLE_SYSTEM_DOCUMENT READONLY FOCUSABLE
 ++IA2_ROLE_SECTION
-++++ROLE_SYSTEM_PROGRESSBAR value='2' currentValue=2.00 minimumValue=1.00 maximumValue=10.00
+++++ROLE_SYSTEM_PROGRESSBAR value='2' currentValue=2 minimumValue=1 maximumValue=10
diff --git a/content/test/data/accessibility/html/progress-expected-win.txt b/content/test/data/accessibility/html/progress-expected-win.txt
index 9748bac..1641352 100644
--- a/content/test/data/accessibility/html/progress-expected-win.txt
+++ b/content/test/data/accessibility/html/progress-expected-win.txt
@@ -1,4 +1,4 @@
 ROLE_SYSTEM_DOCUMENT READONLY FOCUSABLE
 ++IA2_ROLE_SECTION
-++++ROLE_SYSTEM_PROGRESSBAR value='22' READONLY valuetext:22 currentValue=22.00 minimumValue=0.00 maximumValue=100.00
-++++ROLE_SYSTEM_PROGRESSBAR MIXED READONLY minimumValue=0.00 maximumValue=1.00
+++++ROLE_SYSTEM_PROGRESSBAR value='22' READONLY valuetext:22 currentValue=22 minimumValue=0 maximumValue=100
+++++ROLE_SYSTEM_PROGRESSBAR MIXED READONLY minimumValue=0 maximumValue=1
diff --git a/content/web_test/renderer/blink_test_helpers.cc b/content/web_test/renderer/blink_test_helpers.cc
index 71dd169..7535f7d 100644
--- a/content/web_test/renderer/blink_test_helpers.cc
+++ b/content/web_test/renderer/blink_test_helpers.cc
@@ -144,7 +144,7 @@
 WebURL RewriteFileURLToLocalResource(base::StringPiece resource) {
   // Some web tests use file://// which we resolve as a UNC path. Normalize
   // them to just file:///.
-  std::string result = resource.as_string();
+  std::string result(resource);
   static const size_t kFileLen = sizeof("file:///") - 1;
   while (base::StartsWith(base::ToLowerASCII(result), "file:////",
                           base::CompareCase::SENSITIVE)) {
diff --git a/device/vr/android/arcore/ar_compositor_frame_sink.cc b/device/vr/android/arcore/ar_compositor_frame_sink.cc
index 71b00f302..a54b3b09 100644
--- a/device/vr/android/arcore/ar_compositor_frame_sink.cc
+++ b/device/vr/android/arcore/ar_compositor_frame_sink.cc
@@ -363,10 +363,9 @@
       dom_quad_state->SetAll(
           gfx::Transform(),
           /*quad_layer_rect=*/output_rect,
-          /*visible_quad_layer_rect=*/output_rect, gfx::MaskFilterInfo(),
-          /*clip_rect=*/gfx::Rect(),
-          /*is_clipped=*/false, /*are_contents_opaque=*/false, /*opacity=*/1.f,
-          SkBlendMode::kSrcOver, /*sorting_context_id=*/0);
+          /*visible_layer_rect=*/output_rect, gfx::MaskFilterInfo(),
+          /*clip_rect=*/base::nullopt, /*are_contents_opaque=*/false,
+          /*opacity=*/1.f, SkBlendMode::kSrcOver, /*sorting_context_id=*/0);
 
       viz::SurfaceDrawQuad* dom_quad =
           render_pass->CreateAndAppendDrawQuad<viz::SurfaceDrawQuad>();
@@ -392,10 +391,9 @@
     xr_content_quad_state->SetAll(
         gfx::Transform(),
         /*quad_layer_rect=*/output_rect,
-        /*visible_quad_layer_rect=*/output_rect, gfx::MaskFilterInfo(),
-        /*clip_rect=*/gfx::Rect(),
-        /*is_clipped=*/false, /*are_contents_opaque=*/false, /*opacity=*/1.f,
-        SkBlendMode::kSrcOver, /*sorting_context_id=*/0);
+        /*visible_layer_rect=*/output_rect, gfx::MaskFilterInfo(),
+        /*clip_rect=*/base::nullopt, /*are_contents_opaque=*/false,
+        /*opacity=*/1.f, SkBlendMode::kSrcOver, /*sorting_context_id=*/0);
 
     viz::TextureDrawQuad* xr_content_quad =
         render_pass->CreateAndAppendDrawQuad<viz::TextureDrawQuad>();
@@ -432,10 +430,9 @@
   camera_quad_state->SetAll(
       gfx::Transform(),
       /*quad_layer_rect=*/output_rect,
-      /*visible_quad_layer_rect=*/output_rect, gfx::MaskFilterInfo(),
-      /*clip_rect=*/gfx::Rect(),
-      /*is_clipped=*/false, /*are_contents_opaque=*/true, /*opacity=*/1.f,
-      SkBlendMode::kSrcOver, /*sorting_context_id=*/0);
+      /*visible_layer_rect=*/output_rect, gfx::MaskFilterInfo(),
+      /*clip_rect=*/base::nullopt, /*are_contents_opaque=*/true,
+      /*opacity=*/1.f, SkBlendMode::kSrcOver, /*sorting_context_id=*/0);
 
   viz::TextureDrawQuad* camera_quad =
       render_pass->CreateAndAppendDrawQuad<viz::TextureDrawQuad>();
diff --git a/docs/speed/binary_size/optimization_advice.md b/docs/speed/binary_size/optimization_advice.md
index 66156ee1..8613d23 100644
--- a/docs/speed/binary_size/optimization_advice.md
+++ b/docs/speed/binary_size/optimization_advice.md
@@ -125,9 +125,12 @@
    binary size by 50kb.
  * As of 2019, Chrome for Android (arm32) grows by about 100kb per week.
  * To get a feeling for how large existing features are, refer to the
-   [milestone size breakdowns] and group by "Component".
+   [milestone size breakdowns] and group by "Component" (Googlers only).
+   * For non-googlers, run `//tools/binary_size/supersize archive` on a release
+     build to create a `.size` file, and upload it to [the viewer]
 
-[milestone size breakdowns]: https://storage.googleapis.com/chrome-supersize/index.html
+[milestone size breakdowns]: https://goto.google.com/chrome-supersize
+[the viewer]: https://chrome-supersize.firebaseapp.com/viewer.html
 
 ### Optimizing Translations (Strings)
 
@@ -236,8 +239,6 @@
    * In C++, static objects are created at compile time, but in Java they
      are created by executing code within `<clinit>()`. There is often little
      advantage to initializing class fields statically vs. upon first use.
- * Use `String.format()` instead of concatenation.
-   * Concatenation causes a lot of StringBuilder code to be generated.
  * Try to use default values for fields rather than explicit initialization.
    * E.g. Name booleans such that they start as "false".
    * E.g. Use integer sentinels that have initial state as 0.
@@ -248,11 +249,17 @@
      `onFinished(bool)`.
    * E.g. rather than have `onTextChanged()`, `onDateChanged()`, ..., have a
      single `onChanged()` that assumes everything changed.
- * Ensure unused code is optimized away by ProGuard / R8.
+ * Ensure unused code is optimized away by R8.
+   * See [here][proguard-build-doc] for more info on how Chrome uses ProGuard.
    * Add `@CheckDiscard` to methods or classes that you expect R8 to inline.
    * Add `@RemovableInRelease` to force a method to be a no-op when DCHECKs
      are disabled.
-   * See [here][proguard-build-doc] for more info on how Chrome uses ProGuard.
+   * Use [//third_party/r8/playground][r8-playground] to figure out how various
+     coding patterns are optimized by R8.
+   * Build with `enable_proguard_obfuscation = false` and use
+     `//third_party/android_sdk/public/build-tools/*/dexdump` to see how code was
+     optimized directly in apk / bundle targets.
+     
 
 [proguard-build-doc]: /build/android/docs/java_optimization.md
 [size-trybot]: /tools/binary_size/README.md#Binary-Size-Trybot-android_binary_size
@@ -261,6 +268,7 @@
 [template_bloat_one]: https://bugs.chromium.org/p/chromium/issues/detail?id=716393
 [template_bloat_two]: https://chromium-review.googlesource.com/c/chromium/src/+/2639396
 [supersize-console]: /tools/binary_size/README.md#Usage_console
+[r8-playground]: /third_party/r8/playground
 
 ### Optimizing Third-Party Android Dependencies
 
diff --git a/extensions/browser/api/declarative_net_request/utils.cc b/extensions/browser/api/declarative_net_request/utils.cc
index 691bfb0..f14f506 100644
--- a/extensions/browser/api/declarative_net_request/utils.cc
+++ b/extensions/browser/api/declarative_net_request/utils.cc
@@ -25,6 +25,7 @@
 #include "extensions/browser/api/declarative_net_request/ruleset_matcher.h"
 #include "extensions/browser/api/web_request/web_request_info.h"
 #include "extensions/browser/api/web_request/web_request_resource_type.h"
+#include "extensions/browser/extensions_browser_client.h"
 #include "extensions/common/api/declarative_net_request/constants.h"
 #include "extensions/common/api/declarative_net_request/dnr_manifest_data.h"
 #include "extensions/common/permissions/api_permission.h"
@@ -155,6 +156,7 @@
 
 void ClearRendererCacheOnNavigation() {
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+  extensions::ExtensionsBrowserClient::Get()->ClearBackForwardCache();
   web_cache::WebCacheManager::GetInstance()->ClearCacheOnNavigation();
 }
 
diff --git a/extensions/browser/api/declarative_net_request/utils.h b/extensions/browser/api/declarative_net_request/utils.h
index f2adc1a..afbd1e9 100644
--- a/extensions/browser/api/declarative_net_request/utils.h
+++ b/extensions/browser/api/declarative_net_request/utils.h
@@ -61,7 +61,8 @@
 bool PersistIndexedRuleset(const base::FilePath& path,
                            base::span<const uint8_t> data);
 
-// Helper to clear each renderer's in-memory cache the next time it navigates.
+// Helper to clear any back-forward caches and each renderer's in-memory cache
+// the next time it navigates.
 void ClearRendererCacheOnNavigation();
 
 // Helper to log the |kReadDynamicRulesJSONStatusHistogram| histogram.
diff --git a/extensions/browser/api/guest_view/web_view/web_view_internal_api.cc b/extensions/browser/api/guest_view/web_view/web_view_internal_api.cc
index a8c2b0d..d550fe9 100644
--- a/extensions/browser/api/guest_view/web_view/web_view_internal_api.cc
+++ b/extensions/browser/api/guest_view/web_view/web_view_internal_api.cc
@@ -15,6 +15,8 @@
 #include "base/strings/string_number_conversions.h"
 #include "base/strings/stringprintf.h"
 #include "base/strings/utf_string_conversions.h"
+#include "base/task/thread_pool.h"
+#include "base/threading/thread_task_runner_handle.h"
 #include "components/crash/core/common/crash_key.h"
 #include "content/public/browser/browser_context.h"
 #include "content/public/browser/render_frame_host.h"
@@ -328,6 +330,17 @@
   return RespondNow(Error(GetErrorMessage(capture_result)));
 }
 
+void WebViewInternalCaptureVisibleRegionFunction::GetQuotaLimitHeuristics(
+    QuotaLimitHeuristics* heuristics) const {
+  constexpr base::TimeDelta kSecond = base::TimeDelta::FromSeconds(1);
+  QuotaLimitHeuristic::Config limit = {
+      web_view_internal::MAX_CAPTURE_VISIBLE_REGION_CALLS_PER_SECOND, kSecond};
+
+  heuristics->push_back(std::make_unique<QuotaService::TimedLimit>(
+      limit, std::make_unique<QuotaLimitHeuristic::SingletonBucketMapper>(),
+      "MAX_CAPTURE_VISIBLE_REGION_CALLS_PER_SECOND"));
+}
+
 WebContentsCaptureClient::ScreenshotAccess
 WebViewInternalCaptureVisibleRegionFunction::GetScreenshotAccess(
     content::WebContents* web_contents) const {
@@ -343,8 +356,28 @@
 
 void WebViewInternalCaptureVisibleRegionFunction::OnCaptureSuccess(
     const SkBitmap& bitmap) {
+  base::ThreadPool::PostTask(
+      FROM_HERE, {base::TaskPriority::USER_VISIBLE},
+      base::BindOnce(&WebViewInternalCaptureVisibleRegionFunction::
+                         EncodeBitmapOnWorkerThread,
+                     this, base::ThreadTaskRunnerHandle::Get(), bitmap));
+}
+
+void WebViewInternalCaptureVisibleRegionFunction::EncodeBitmapOnWorkerThread(
+    scoped_refptr<base::TaskRunner> reply_task_runner,
+    const SkBitmap& bitmap) {
   std::string base64_result;
-  if (!EncodeBitmap(bitmap, &base64_result)) {
+  bool success = EncodeBitmap(bitmap, &base64_result);
+  reply_task_runner->PostTask(
+      FROM_HERE, base::BindOnce(&WebViewInternalCaptureVisibleRegionFunction::
+                                    OnBitmapEncodedOnUIThread,
+                                this, success, std::move(base64_result)));
+}
+
+void WebViewInternalCaptureVisibleRegionFunction::OnBitmapEncodedOnUIThread(
+    bool success,
+    std::string base64_result) {
+  if (!success) {
     OnCaptureFailure(FAILURE_REASON_ENCODING_FAILED);
     return;
   }
diff --git a/extensions/browser/api/guest_view/web_view/web_view_internal_api.h b/extensions/browser/api/guest_view/web_view/web_view_internal_api.h
index f91bf469..d9e35c6 100644
--- a/extensions/browser/api/guest_view/web_view/web_view_internal_api.h
+++ b/extensions/browser/api/guest_view/web_view/web_view_internal_api.h
@@ -8,12 +8,17 @@
 #include <stdint.h>
 
 #include "base/macros.h"
+#include "base/memory/scoped_refptr.h"
 #include "extensions/browser/api/execute_code_function.h"
 #include "extensions/browser/api/web_contents_capture_client.h"
 #include "extensions/browser/extension_function.h"
 #include "extensions/browser/guest_view/web_view/web_ui/web_ui_url_fetcher.h"
 #include "extensions/browser/guest_view/web_view/web_view_guest.h"
 
+namespace base {
+class TaskRunner;
+}
+
 // WARNING: WebViewInternal could be loaded in an unblessed context, thus any
 // new APIs must extend WebViewInternalExtensionFunction or
 // WebViewInternalExecuteCodeFunction which do a process ID check to prevent
@@ -44,6 +49,7 @@
 
   // ExtensionFunction:
   ResponseAction Run() override;
+  void GetQuotaLimitHeuristics(QuotaLimitHeuristics* heuristics) const override;
 
  private:
   // extensions::WebContentsCaptureClient:
@@ -53,6 +59,11 @@
   void OnCaptureSuccess(const SkBitmap& bitmap) override;
   void OnCaptureFailure(CaptureResult result) override;
 
+  void EncodeBitmapOnWorkerThread(
+      scoped_refptr<base::TaskRunner> reply_task_runner,
+      const SkBitmap& bitmap);
+  void OnBitmapEncodedOnUIThread(bool success, std::string base64_result);
+
   std::string GetErrorMessage(CaptureResult result);
 
   bool is_guest_transparent_;
diff --git a/extensions/browser/api/web_request/web_request_api_helpers.cc b/extensions/browser/api/web_request/web_request_api_helpers.cc
index acacd66..143a477 100644
--- a/extensions/browser/api/web_request/web_request_api_helpers.cc
+++ b/extensions/browser/api/web_request/web_request_api_helpers.cc
@@ -79,6 +79,7 @@
 using ParsedResponseCookies = std::vector<std::unique_ptr<net::ParsedCookie>>;
 
 void ClearCacheOnNavigationOnUI() {
+  extensions::ExtensionsBrowserClient::Get()->ClearBackForwardCache();
   web_cache::WebCacheManager::GetInstance()->ClearCacheOnNavigation();
 }
 
diff --git a/extensions/browser/api/web_request/web_request_api_helpers.h b/extensions/browser/api/web_request/web_request_api_helpers.h
index a8c302f..d9ef2d2e 100644
--- a/extensions/browser/api/web_request/web_request_api_helpers.h
+++ b/extensions/browser/api/web_request/web_request_api_helpers.h
@@ -494,7 +494,8 @@
                                   net::AuthCredentials* auth_credentials,
                                   IgnoredActions* ignored_actions);
 
-// Triggers clearing each renderer's in-memory cache the next time it navigates.
+// Triggers clearing any back-forward caches and each renderer's in-memory cache
+// the next time it navigates.
 void ClearCacheOnNavigation();
 
 // Converts the |name|, |value| pair of a http header to a HttpHeaders
diff --git a/extensions/browser/app_window/app_window_contents.cc b/extensions/browser/app_window/app_window_contents.cc
index 0b64fc6b..f542a23 100644
--- a/extensions/browser/app_window/app_window_contents.cc
+++ b/extensions/browser/app_window/app_window_contents.cc
@@ -77,9 +77,6 @@
 }
 
 void AppWindowContentsImpl::NativeWindowClosed(bool send_onclosed) {
-  // Return early if this method is called when the render frame is not live.
-  if (!web_contents_->GetMainFrame()->IsRenderFrameLive())
-    return;
   ExtensionWebContentsObserver::GetForWebContents(web_contents())
       ->GetLocalFrame(web_contents_->GetMainFrame())
       ->AppWindowClosed(send_onclosed);
diff --git a/extensions/browser/extension_web_contents_observer.cc b/extensions/browser/extension_web_contents_observer.cc
index c5ea7a0..ae94562 100644
--- a/extensions/browser/extension_web_contents_observer.cc
+++ b/extensions/browser/extension_web_contents_observer.cc
@@ -315,12 +315,6 @@
 
 mojom::LocalFrame* ExtensionWebContentsObserver::GetLocalFrame(
     content::RenderFrameHost* render_frame_host) {
-  // Attempting to get a remote interface before IsRenderFrameLive() will fail,
-  // leaving a broken pipe that will block all further messages. Return nullptr
-  // instead. Callers should try again after RenderFrameCreated().
-  if (!render_frame_host->IsRenderFrameLive())
-    return nullptr;
-
   mojo::AssociatedRemote<mojom::LocalFrame>& remote =
       local_frame_map_[render_frame_host];
   if (!remote.is_bound()) {
diff --git a/extensions/browser/extension_web_contents_observer.h b/extensions/browser/extension_web_contents_observer.h
index f11dcaa..200111dd 100644
--- a/extensions/browser/extension_web_contents_observer.h
+++ b/extensions/browser/extension_web_contents_observer.h
@@ -74,8 +74,7 @@
 
   // Returns mojom::LocalFrame* corresponding |render_frame_host|. It emplaces
   // AssociatedRemote<mojom::LocalFrame> to |local_frame_map_| if the map
-  // doesn't have it. Note that it could return nullptr if |render_frame_host|
-  // is not live.
+  // doesn't have it. Note that it does not return nullptr.
   mojom::LocalFrame* GetLocalFrame(content::RenderFrameHost* render_frame_host);
 
  protected:
diff --git a/extensions/browser/extensions_browser_client.h b/extensions/browser/extensions_browser_client.h
index 0608a3c..216e445c 100644
--- a/extensions/browser/extensions_browser_client.h
+++ b/extensions/browser/extensions_browser_client.h
@@ -287,6 +287,10 @@
                               int embedder_process_id,
                               int view_instance_id) {}
 
+  // Clears the back-forward cache for all active tabs across all browser
+  // contexts.
+  virtual void ClearBackForwardCache() {}
+
   // Attaches the task manager extension tag to |web_contents|, if needed based
   // on |view_type|, so that its corresponding task shows up in the task
   // manager.
diff --git a/extensions/common/api/web_view_internal.json b/extensions/common/api/web_view_internal.json
index 4f062e3..960bfe28 100644
--- a/extensions/common/api/web_view_internal.json
+++ b/extensions/common/api/web_view_internal.json
@@ -9,6 +9,12 @@
     "compiler_options": {
       "implemented_in": "extensions/browser/api/guest_view/web_view/web_view_internal_api.h"
     },
+    "properties": {
+      "MAX_CAPTURE_VISIBLE_REGION_CALLS_PER_SECOND": {
+        "value": 1,
+        "description": "The maximum number of times that $(ref:captureVisibleRegion) can be called per second. $(ref:captureVisibleRegion) is expensive and should not be called too often."
+      }
+    },
     "types": [
       {
         "id": "DataTypeSet",
diff --git a/extensions/common/constants.cc b/extensions/common/constants.cc
index 89987436..e136d60 100644
--- a/extensions/common/constants.cc
+++ b/extensions/common/constants.cc
@@ -127,12 +127,19 @@
 const char kCalendarDemoAppId[] = "fpgfohogebplgnamlafljlcidjedbdeb";
 const char kGMailAppId[] = "pjkljhegncpnkpknbcohdijeoejaedia";
 const char kGoogleDocsDemoAppId[] = "chdaoodbokekbiiphekbfjdmiodccljl";
+const char kGoogleDocsPwaAppId[] = "cepkndkdlbllfhpfhledabdcdbidehkd";
 const char kGoogleDriveAppId[] = "apdfllckaahabafndbhieahigkjlhalf";
+const char kGoogleMeetPwaAppId[] = "dkainijpcknoofiakgccliajhbmlbhji";
 const char kGoogleSheetsDemoAppId[] = "nifkmgcdokhkjghdlgflonppnefddien";
+const char kGoogleSheetsPwaAppId[] = "hcgjdbbnhkmopplfiibmdgghhdhbiidh";
 const char kGoogleSlidesDemoAppId[] = "hdmobeajeoanbanmdlabnbnlopepchip";
 const char kGoogleKeepAppId[] = "hmjkmjkepdijhoojdojkdfohbdgmmhki";
 const char kYoutubeAppId[] = "blpcfgokakmgnkcojhhkbfbldkacnbeo";
 const char kYoutubePwaAppId[] = "agimnkijcaahngcdmfeangaknmldooml";
+const char kSpotifyAppId[] = "pjibgclleladliembfgfagdaldikeohf";
+const char kBeFunkyAppId[] = "fjoomcalbeohjbnlcneddljemclcekeg";
+const char kClipchampAppId[] = "pfepfhbcedkbjdkanpimmmdjfgoddhkg";
+const char kGeForceNowAppId[] = "egmafekfmcnknbdlbfbhafbllplmjlhn";
 
 #if BUILDFLAG(IS_CHROMEOS_ASH)
 // TODO(michaelpg): Deprecate old app IDs before adding new ones to avoid bloat.
diff --git a/extensions/common/constants.h b/extensions/common/constants.h
index 22e93c9..9dce6f9 100644
--- a/extensions/common/constants.h
+++ b/extensions/common/constants.h
@@ -217,12 +217,21 @@
 // The extension id of the demo Google Docs application.
 extern const char kGoogleDocsDemoAppId[];
 
+// The extension id of the Google Docs PWA.
+extern const char kGoogleDocsPwaAppId[];
+
 // The extension id of the Google Drive application.
 extern const char kGoogleDriveAppId[];
 
+// The extension id of the Google Meet PWA.
+extern const char kGoogleMeetPwaAppId[];
+
 // The extension id of the demo Google Sheets application.
 extern const char kGoogleSheetsDemoAppId[];
 
+// The extension id of the Google Sheets PWA.
+extern const char kGoogleSheetsPwaAppId[];
+
 // The extension id of the demo Google Slides application.
 extern const char kGoogleSlidesDemoAppId[];
 
@@ -235,6 +244,18 @@
 // The extension id of the Youtube PWA.
 extern const char kYoutubePwaAppId[];
 
+// The extension id of the Spotify PWA.
+extern const char kSpotifyAppId[];
+
+// The extension id of the BeFunky PWA.
+extern const char kBeFunkyAppId[];
+
+// The extension id of the Clipchamp PWA.
+extern const char kClipchampAppId[];
+
+// The extension id of the GeForce NOW PWA.
+extern const char kGeForceNowAppId[];
+
 #if BUILDFLAG(IS_CHROMEOS_ASH)
 // The extension id of the default Demo Mode Highlights app.
 extern const char kHighlightsAppId[];
diff --git a/extensions/shell/BUILD.gn b/extensions/shell/BUILD.gn
index be52941..ddeea51 100644
--- a/extensions/shell/BUILD.gn
+++ b/extensions/shell/BUILD.gn
@@ -235,6 +235,7 @@
       "//ash/constants",
       "//chromeos/dbus",
       "//chromeos/dbus/audio",
+      "//chromeos/dbus/hermes",
       "//chromeos/dbus/power",
       "//chromeos/disks",
       "//chromeos/login/login_state",
diff --git a/extensions/shell/browser/shell_browser_main_parts.cc b/extensions/shell/browser/shell_browser_main_parts.cc
index db30fd0f..3829d88 100644
--- a/extensions/shell/browser/shell_browser_main_parts.cc
+++ b/extensions/shell/browser/shell_browser_main_parts.cc
@@ -13,6 +13,7 @@
 #include "base/memory/ref_counted.h"
 #include "build/build_config.h"
 #include "build/chromeos_buildflags.h"
+#include "chromeos/dbus/hermes/hermes_clients.h"
 #include "components/keyed_service/content/browser_context_dependency_manager.h"
 #include "components/nacl/common/buildflags.h"
 #include "components/prefs/pref_service.h"
@@ -129,10 +130,12 @@
   chromeos::DBusThreadManager::Initialize();
   dbus::Bus* bus = chromeos::DBusThreadManager::Get()->GetSystemBus();
   if (bus) {
+    chromeos::hermes_clients::Initialize(bus);
     bluez::BluezDBusManager::Initialize(bus);
     chromeos::CrasAudioClient::Initialize(bus);
     chromeos::PowerManagerClient::Initialize(bus);
   } else {
+    chromeos::hermes_clients::InitializeFakes();
     bluez::BluezDBusManager::InitializeFake();
     chromeos::CrasAudioClient::InitializeFake();
     chromeos::PowerManagerClient::InitializeFake();
diff --git a/fuchsia/base/agent_manager.cc b/fuchsia/base/agent_manager.cc
index 3b6aa65c..0a4fbb9 100644
--- a/fuchsia/base/agent_manager.cc
+++ b/fuchsia/base/agent_manager.cc
@@ -8,6 +8,7 @@
 
 #include "base/fuchsia/fuchsia_logging.h"
 #include "base/logging.h"
+#include "base/strings/string_piece.h"
 
 namespace cr_fuchsia {
 
diff --git a/fuchsia/base/fake_component_context.cc b/fuchsia/base/fake_component_context.cc
index 3e523fc..0ead372 100644
--- a/fuchsia/base/fake_component_context.cc
+++ b/fuchsia/base/fake_component_context.cc
@@ -11,6 +11,7 @@
 #include "base/check.h"
 #include "base/notreached.h"
 #include "base/run_loop.h"
+#include "base/strings/string_piece.h"
 #include "fuchsia/base/agent_impl.h"
 
 namespace cr_fuchsia {
diff --git a/fuchsia/base/frame_test_util.cc b/fuchsia/base/frame_test_util.cc
index f087bf2..a3daec6 100644
--- a/fuchsia/base/frame_test_util.cc
+++ b/fuchsia/base/frame_test_util.cc
@@ -3,6 +3,7 @@
 // found in the LICENSE file.
 
 #include "fuchsia/base/frame_test_util.h"
+#include "base/strings/string_piece.h"
 
 #include "base/json/json_reader.h"
 #include "base/run_loop.h"
diff --git a/fuchsia/base/test_navigation_listener.cc b/fuchsia/base/test_navigation_listener.cc
index 37a3655c..50380915 100644
--- a/fuchsia/base/test_navigation_listener.cc
+++ b/fuchsia/base/test_navigation_listener.cc
@@ -11,6 +11,7 @@
 #include "base/bind.h"
 #include "base/fuchsia/fuchsia_logging.h"
 #include "base/run_loop.h"
+#include "base/strings/string_piece.h"
 #include "base/strings/stringprintf.h"
 #include "fuchsia/base/mem_buffer_util.h"
 
diff --git a/fuchsia/base/url_request_rewrite_test_util.cc b/fuchsia/base/url_request_rewrite_test_util.cc
index 6a0ace8..d641805 100644
--- a/fuchsia/base/url_request_rewrite_test_util.cc
+++ b/fuchsia/base/url_request_rewrite_test_util.cc
@@ -3,6 +3,7 @@
 // found in the LICENSE file.
 
 #include "fuchsia/base/url_request_rewrite_test_util.h"
+#include "base/strings/string_piece.h"
 
 #include "fuchsia/base/string_util.h"
 
diff --git a/fuchsia/runners/cast/pending_cast_component.cc b/fuchsia/runners/cast/pending_cast_component.cc
index c725017..f67ec439 100644
--- a/fuchsia/runners/cast/pending_cast_component.cc
+++ b/fuchsia/runners/cast/pending_cast_component.cc
@@ -7,6 +7,7 @@
 #include "base/bind.h"
 #include "base/check.h"
 #include "base/fuchsia/fuchsia_logging.h"
+#include "base/strings/string_piece.h"
 #include "fuchsia/base/agent_manager.h"
 
 PendingCastComponent::PendingCastComponent(
diff --git a/google_apis/gaia/gaia_auth_consumer.h b/google_apis/gaia/gaia_auth_consumer.h
index b38ca493..7bd3847 100644
--- a/google_apis/gaia/gaia_auth_consumer.h
+++ b/google_apis/gaia/gaia_auth_consumer.h
@@ -65,26 +65,23 @@
   };
 
   // Possible server responses to a token revocation request.
-  // Used in UMA, do not delete or reorder values.
   enum class TokenRevocationStatus {
     // Token revocation succeeded.
-    kSuccess = 0,
+    kSuccess,
     // Network connection was canceled, no response was received.
-    kConnectionCanceled = 1,
+    kConnectionCanceled,
     // Network connection failed, no response was received.
-    kConnectionFailed = 2,
+    kConnectionFailed,
     // Network connection timed out, no response was received.
-    kConnectionTimeout = 3,
+    kConnectionTimeout,
     // The token is unknown or invalid.
-    kInvalidToken = 4,
+    kInvalidToken,
     // The request was malformed.
-    kInvalidRequest = 5,
+    kInvalidRequest,
     // Internal server error.
-    kServerError = 6,
+    kServerError,
     // Other error.
-    kUnknownError = 7,
-
-    kMaxValue = kUnknownError
+    kUnknownError,
   };
 
   enum class ReAuthProofTokenStatus : int {
diff --git a/gpu/command_buffer/service/shared_image_backing_d3d.cc b/gpu/command_buffer/service/shared_image_backing_d3d.cc
index 1f98a72..893f4d19 100644
--- a/gpu/command_buffer/service/shared_image_backing_d3d.cc
+++ b/gpu/command_buffer/service/shared_image_backing_d3d.cc
@@ -4,7 +4,6 @@
 
 #include "gpu/command_buffer/service/shared_image_backing_d3d.h"
 
-#include "base/memory/ptr_util.h"
 #include "base/trace_event/memory_dump_manager.h"
 #include "components/viz/common/resources/resource_format_utils.h"
 #include "components/viz/common/resources/resource_sizes.h"
@@ -18,353 +17,26 @@
 
 namespace {
 
-bool SupportsVideoFormat(DXGI_FORMAT dxgi_format) {
-  switch (dxgi_format) {
-    case DXGI_FORMAT_NV12:
-    case DXGI_FORMAT_P010:
-    case DXGI_FORMAT_B8G8R8A8_UNORM:
-    case DXGI_FORMAT_R10G10B10A2_UNORM:
-    case DXGI_FORMAT_R16G16B16A16_FLOAT:
-      return true;
-    default:
-      return false;
-  }
-}
-
-size_t NumPlanes(DXGI_FORMAT dxgi_format) {
-  switch (dxgi_format) {
-    case DXGI_FORMAT_NV12:
-    case DXGI_FORMAT_P010:
-      return 2;
-    case DXGI_FORMAT_B8G8R8A8_UNORM:
-    case DXGI_FORMAT_R10G10B10A2_UNORM:
-    case DXGI_FORMAT_R16G16B16A16_FLOAT:
-      return 1;
-    default:
-      NOTREACHED();
-      return 0;
-  }
-}
-
-viz::ResourceFormat PlaneFormat(DXGI_FORMAT dxgi_format, size_t plane) {
-  DCHECK_LT(plane, NumPlanes(dxgi_format));
-  switch (dxgi_format) {
-    // TODO(crbug.com/1011555): P010 formats are not fully supported by Skia.
-    // Treat them the same as NV12 for the time being.
-    case DXGI_FORMAT_NV12:
-    case DXGI_FORMAT_P010:
-      // Y plane is accessed as R8 and UV plane is accessed as RG88 in D3D.
-      return plane == 0 ? viz::RED_8 : viz::RG_88;
-    case DXGI_FORMAT_B8G8R8A8_UNORM:
-      return viz::BGRA_8888;
-    case DXGI_FORMAT_R10G10B10A2_UNORM:
-      return viz::RGBA_1010102;
-    case DXGI_FORMAT_R16G16B16A16_FLOAT:
-      return viz::RGBA_F16;
-    default:
-      NOTREACHED();
-      return viz::BGRA_8888;
-  }
-}
-
-gfx::Size PlaneSize(DXGI_FORMAT dxgi_format,
-                    const gfx::Size& size,
-                    size_t plane) {
-  DCHECK_LT(plane, NumPlanes(dxgi_format));
-  switch (dxgi_format) {
-    case DXGI_FORMAT_NV12:
-    case DXGI_FORMAT_P010:
-      // Y plane is full size and UV plane is accessed as half size in D3D.
-      return plane == 0 ? size : gfx::Size(size.width() / 2, size.height() / 2);
-    case DXGI_FORMAT_B8G8R8A8_UNORM:
-    case DXGI_FORMAT_R10G10B10A2_UNORM:
-    case DXGI_FORMAT_R16G16B16A16_FLOAT:
-      return size;
-    default:
-      NOTREACHED();
-      return gfx::Size();
-  }
-}
-
-class ScopedRestoreTexture {
+class ScopedRestoreTexture2D {
  public:
-  ScopedRestoreTexture(gl::GLApi* api, GLenum target)
-      : api_(api), target_(target) {
-    DCHECK(target == GL_TEXTURE_2D || target == GL_TEXTURE_EXTERNAL_OES);
+  explicit ScopedRestoreTexture2D(gl::GLApi* api) : api_(api) {
     GLint binding = 0;
-    api->glGetIntegervFn(target == GL_TEXTURE_2D
-                             ? GL_TEXTURE_BINDING_2D
-                             : GL_TEXTURE_BINDING_EXTERNAL_OES,
-                         &binding);
+    api->glGetIntegervFn(GL_TEXTURE_BINDING_2D, &binding);
     prev_binding_ = binding;
   }
 
-  ~ScopedRestoreTexture() { api_->glBindTextureFn(target_, prev_binding_); }
+  ~ScopedRestoreTexture2D() {
+    api_->glBindTextureFn(GL_TEXTURE_2D, prev_binding_);
+  }
 
  private:
   gl::GLApi* const api_;
-  const GLenum target_;
   GLuint prev_binding_ = 0;
-  DISALLOW_COPY_AND_ASSIGN(ScopedRestoreTexture);
+  DISALLOW_COPY_AND_ASSIGN(ScopedRestoreTexture2D);
 };
 
-scoped_refptr<gles2::TexturePassthrough> CreateGLTexture(
-    viz::ResourceFormat format,
-    const gfx::Size& size,
-    const gfx::ColorSpace& color_space,
-    Microsoft::WRL::ComPtr<ID3D11Texture2D> d3d11_texture,
-    Microsoft::WRL::ComPtr<IDXGISwapChain1> swap_chain = nullptr,
-    GLenum texture_target = GL_TEXTURE_2D,
-    unsigned array_slice = 0u,
-    unsigned plane_index = 0u) {
-  gl::GLApi* const api = gl::g_current_gl_context;
-  ScopedRestoreTexture scoped_restore(api, texture_target);
-
-  GLuint service_id = 0;
-  api->glGenTexturesFn(1, &service_id);
-  api->glBindTextureFn(texture_target, service_id);
-
-  // The GL internal format can differ from the underlying swap chain or texture
-  // format e.g. RGBA or RGB instead of BGRA or RED/RG for NV12 texture planes.
-  // See EGL_ANGLE_d3d_texture_client_buffer spec for format restrictions.
-  const auto internal_format = viz::GLInternalFormat(format);
-  const auto data_type = viz::GLDataType(format);
-  auto image = base::MakeRefCounted<gl::GLImageD3D>(
-      size, internal_format, data_type, color_space, d3d11_texture, array_slice,
-      plane_index, swap_chain);
-  DCHECK_EQ(image->GetDataFormat(), viz::GLDataFormat(format));
-  if (!image->Initialize()) {
-    DLOG(ERROR) << "GLImageD3D::Initialize failed";
-    api->glDeleteTexturesFn(1, &service_id);
-    return nullptr;
-  }
-  if (!image->BindTexImage(texture_target)) {
-    DLOG(ERROR) << "GLImageD3D::BindTexImage failed";
-    api->glDeleteTexturesFn(1, &service_id);
-    return nullptr;
-  }
-
-  auto texture = base::MakeRefCounted<gles2::TexturePassthrough>(
-      service_id, texture_target);
-  texture->SetLevelImage(texture_target, 0, image.get());
-  GLint texture_memory_size = 0;
-  api->glGetTexParameterivFn(texture_target, GL_MEMORY_SIZE_ANGLE,
-                             &texture_memory_size);
-  texture->SetEstimatedSize(texture_memory_size);
-
-  return texture;
-}
-
 }  // anonymous namespace
 
-SharedImageBackingD3D::SharedState::SharedState(
-    base::win::ScopedHandle shared_handle,
-    Microsoft::WRL::ComPtr<IDXGIKeyedMutex> dxgi_keyed_mutex)
-    : shared_handle_(std::move(shared_handle)),
-      dxgi_keyed_mutex_(std::move(dxgi_keyed_mutex)) {}
-
-SharedImageBackingD3D::SharedState::~SharedState() {
-  DCHECK(!acquired_for_d3d12_);
-  DCHECK_EQ(acquired_for_d3d11_count_, 0);
-  shared_handle_.Close();
-}
-
-bool SharedImageBackingD3D::SharedState::BeginAccessD3D12(
-    uint64_t* acquire_key) {
-  if (!dxgi_keyed_mutex_) {
-    DLOG(ERROR) << "D3D12 access not supported without keyed mutex";
-    return false;
-  }
-  if (acquired_for_d3d12_ || acquired_for_d3d11_count_ > 0) {
-    DLOG(ERROR) << "Recursive BeginAccess not supported";
-    return false;
-  }
-  *acquire_key = acquire_key_;
-  acquire_key_++;
-  acquired_for_d3d12_ = true;
-  return true;
-}
-
-void SharedImageBackingD3D::SharedState::EndAccessD3D12() {
-  acquired_for_d3d12_ = false;
-}
-
-bool SharedImageBackingD3D::SharedState::BeginAccessD3D11() {
-  // Nop for shared images that are created without keyed mutex (D3D11 only).
-  if (!dxgi_keyed_mutex_)
-    return true;
-
-  if (acquired_for_d3d12_) {
-    DLOG(ERROR) << "Recursive BeginAccess not supported";
-    return false;
-  }
-  if (acquired_for_d3d11_count_ > 0) {
-    acquired_for_d3d11_count_++;
-    return true;
-  }
-  const HRESULT hr = dxgi_keyed_mutex_->AcquireSync(acquire_key_, INFINITE);
-  if (FAILED(hr)) {
-    DLOG(ERROR) << "Unable to acquire the keyed mutex " << std::hex << hr;
-    return false;
-  }
-  acquire_key_++;
-  acquired_for_d3d11_count_++;
-  return true;
-}
-
-void SharedImageBackingD3D::SharedState::EndAccessD3D11() {
-  // Nop for shared images that are created without keyed mutex (D3D11 only).
-  if (!dxgi_keyed_mutex_)
-    return;
-
-  DCHECK_GT(acquired_for_d3d11_count_, 0);
-  acquired_for_d3d11_count_--;
-  if (acquired_for_d3d11_count_ == 0) {
-    const HRESULT hr = dxgi_keyed_mutex_->ReleaseSync(acquire_key_);
-    if (FAILED(hr))
-      DLOG(ERROR) << "Unable to release the keyed mutex " << std::hex << hr;
-  }
-}
-
-HANDLE SharedImageBackingD3D::SharedState::GetSharedHandle() const {
-  return shared_handle_.Get();
-}
-
-// static
-std::unique_ptr<SharedImageBackingD3D>
-SharedImageBackingD3D::CreateFromSwapChainBuffer(
-    const Mailbox& mailbox,
-    viz::ResourceFormat format,
-    const gfx::Size& size,
-    const gfx::ColorSpace& color_space,
-    GrSurfaceOrigin surface_origin,
-    SkAlphaType alpha_type,
-    uint32_t usage,
-    Microsoft::WRL::ComPtr<ID3D11Texture2D> d3d11_texture,
-    Microsoft::WRL::ComPtr<IDXGISwapChain1> swap_chain,
-    size_t buffer_index) {
-  auto gl_texture =
-      CreateGLTexture(format, size, color_space, d3d11_texture, swap_chain);
-  if (!gl_texture) {
-    DLOG(ERROR) << "Failed to create GL texture";
-    return nullptr;
-  }
-  return base::WrapUnique(new SharedImageBackingD3D(
-      mailbox, format, size, color_space, surface_origin, alpha_type, usage,
-      std::move(d3d11_texture), std::move(gl_texture), std::move(swap_chain),
-      buffer_index));
-}
-
-// static
-std::unique_ptr<SharedImageBackingD3D>
-SharedImageBackingD3D::CreateFromSharedHandle(
-    const Mailbox& mailbox,
-    viz::ResourceFormat format,
-    const gfx::Size& size,
-    const gfx::ColorSpace& color_space,
-    GrSurfaceOrigin surface_origin,
-    SkAlphaType alpha_type,
-    uint32_t usage,
-    Microsoft::WRL::ComPtr<ID3D11Texture2D> d3d11_texture,
-    base::win::ScopedHandle shared_handle) {
-  DCHECK(shared_handle.IsValid());
-  // Keyed mutexes are required for Dawn interop but are not used for XR
-  // composition where fences are used instead.
-  Microsoft::WRL::ComPtr<IDXGIKeyedMutex> dxgi_keyed_mutex;
-  d3d11_texture.As(&dxgi_keyed_mutex);
-  DCHECK(!(usage & SHARED_IMAGE_USAGE_WEBGPU) || dxgi_keyed_mutex);
-
-  auto shared_state = base::MakeRefCounted<SharedState>(
-      std::move(shared_handle), std::move(dxgi_keyed_mutex));
-
-  // Creating the GL texture doesn't require exclusive access to the underlying
-  // D3D11 texture.
-  auto gl_texture = CreateGLTexture(format, size, color_space, d3d11_texture);
-  if (!gl_texture) {
-    DLOG(ERROR) << "Failed to create GL texture";
-    return nullptr;
-  }
-
-  return base::WrapUnique(new SharedImageBackingD3D(
-      mailbox, format, size, color_space, surface_origin, alpha_type, usage,
-      std::move(d3d11_texture), std::move(gl_texture), /*swap_chain=*/nullptr,
-      /*buffer_index=*/0, std::move(shared_state)));
-}
-
-std::unique_ptr<SharedImageBackingD3D>
-SharedImageBackingD3D::CreateFromGLTexture(
-    const Mailbox& mailbox,
-    viz::ResourceFormat format,
-    const gfx::Size& size,
-    const gfx::ColorSpace& color_space,
-    GrSurfaceOrigin surface_origin,
-    SkAlphaType alpha_type,
-    uint32_t usage,
-    Microsoft::WRL::ComPtr<ID3D11Texture2D> d3d11_texture,
-    scoped_refptr<gles2::TexturePassthrough> gl_texture) {
-  return base::WrapUnique(new SharedImageBackingD3D(
-      mailbox, format, size, color_space, surface_origin, alpha_type, usage,
-      std::move(d3d11_texture), std::move(gl_texture)));
-}
-
-// static
-std::vector<std::unique_ptr<SharedImageBackingD3D>>
-SharedImageBackingD3D::CreateFromVideoTexture(
-    base::span<const Mailbox> mailboxes,
-    DXGI_FORMAT dxgi_format,
-    const gfx::Size& size,
-    uint32_t usage,
-    Microsoft::WRL::ComPtr<ID3D11Texture2D> d3d11_texture,
-    unsigned array_slice,
-    base::win::ScopedHandle shared_handle) {
-  DCHECK(SupportsVideoFormat(dxgi_format));
-  DCHECK_EQ(mailboxes.size(), NumPlanes(dxgi_format));
-
-  // Shared handle and keyed mutex are required for Dawn interop.
-  Microsoft::WRL::ComPtr<IDXGIKeyedMutex> dxgi_keyed_mutex;
-  d3d11_texture.As(&dxgi_keyed_mutex);
-  DCHECK(!(usage & gpu::SHARED_IMAGE_USAGE_WEBGPU) ||
-         (shared_handle.IsValid() && dxgi_keyed_mutex));
-
-  // Share the same keyed mutex state for all the plane backings.
-  auto shared_state = base::MakeRefCounted<SharedState>(
-      std::move(shared_handle), std::move(dxgi_keyed_mutex));
-
-  std::vector<std::unique_ptr<SharedImageBackingD3D>> shared_images(
-      NumPlanes(dxgi_format));
-  for (size_t plane_index = 0; plane_index < shared_images.size();
-       plane_index++) {
-    const auto& mailbox = mailboxes[plane_index];
-
-    const auto plane_format = PlaneFormat(dxgi_format, plane_index);
-    const auto plane_size = PlaneSize(dxgi_format, size, plane_index);
-
-    // Shared image does not need to store the colorspace since it is already
-    // stored on the VideoFrame which is provided upon presenting the overlay.
-    // To prevent the developer from mistakenly using it, provide the invalid
-    // value from default-construction.
-    constexpr gfx::ColorSpace kInvalidColorSpace;
-
-    auto gl_texture = CreateGLTexture(
-        plane_format, plane_size, kInvalidColorSpace, d3d11_texture,
-        /*swap_chain=*/nullptr, GL_TEXTURE_EXTERNAL_OES, array_slice,
-        plane_index);
-    if (!gl_texture) {
-      DLOG(ERROR) << "Failed to create GL texture";
-      return {};
-    }
-
-    shared_images[plane_index] = base::WrapUnique(new SharedImageBackingD3D(
-        mailbox, plane_format, plane_size, kInvalidColorSpace,
-        kTopLeft_GrSurfaceOrigin, kPremul_SkAlphaType, usage, d3d11_texture,
-        std::move(gl_texture), /*swap_chain=*/nullptr, /*buffer_index=*/0,
-        shared_state));
-    shared_images[plane_index]->SetCleared();
-  }
-
-  return shared_images;
-}
-
 SharedImageBackingD3D::SharedImageBackingD3D(
     const Mailbox& mailbox,
     viz::ResourceFormat format,
@@ -373,11 +45,13 @@
     GrSurfaceOrigin surface_origin,
     SkAlphaType alpha_type,
     uint32_t usage,
-    Microsoft::WRL::ComPtr<ID3D11Texture2D> d3d11_texture,
-    scoped_refptr<gles2::TexturePassthrough> gl_texture,
     Microsoft::WRL::ComPtr<IDXGISwapChain1> swap_chain,
+    scoped_refptr<gles2::TexturePassthrough> texture,
+    scoped_refptr<gl::GLImage> image,
     size_t buffer_index,
-    scoped_refptr<SharedState> shared_state)
+    Microsoft::WRL::ComPtr<ID3D11Texture2D> d3d11_texture,
+    base::win::ScopedHandle shared_handle,
+    Microsoft::WRL::ComPtr<IDXGIKeyedMutex> dxgi_keyed_mutex)
     : ClearTrackingSharedImageBacking(mailbox,
                                       format,
                                       size,
@@ -385,23 +59,28 @@
                                       surface_origin,
                                       alpha_type,
                                       usage,
-                                      gl_texture->estimated_size(),
+                                      texture->estimated_size(),
                                       false /* is_thread_safe */),
-      d3d11_texture_(std::move(d3d11_texture)),
-      gl_texture_(std::move(gl_texture)),
       swap_chain_(std::move(swap_chain)),
+      texture_(std::move(texture)),
+      image_(std::move(image)),
       buffer_index_(buffer_index),
-      shared_state_(std::move(shared_state)) {
-  DCHECK(gl_texture_);
+      d3d11_texture_(std::move(d3d11_texture)),
+      shared_handle_(std::move(shared_handle)),
+      dxgi_keyed_mutex_(std::move(dxgi_keyed_mutex)) {
+  DCHECK(texture_);
 }
 
 SharedImageBackingD3D::~SharedImageBackingD3D() {
   if (!have_context())
-    gl_texture_->MarkContextLost();
-  gl_texture_ = nullptr;
-  shared_state_ = nullptr;
-  swap_chain_.Reset();
+    texture_->MarkContextLost();
+  texture_ = nullptr;
+  swap_chain_ = nullptr;
   d3d11_texture_.Reset();
+  dxgi_keyed_mutex_.Reset();
+  keyed_mutex_acquire_key_ = 0;
+  keyed_mutex_acquired_ = false;
+  shared_handle_.Close();
 
 #if BUILDFLAG(USE_DAWN)
   external_image_ = nullptr;
@@ -415,7 +94,7 @@
 
 bool SharedImageBackingD3D::ProduceLegacyMailbox(
     MailboxManager* mailbox_manager) {
-  mailbox_manager->ProduceTexture(mailbox(), gl_texture_.get());
+  mailbox_manager->ProduceTexture(mailbox(), texture_.get());
   return true;
 }
 
@@ -435,7 +114,7 @@
 
   // Persistently open the shared handle by caching it on this backing.
   if (!external_image_) {
-    DCHECK(base::win::HandleTraits::IsHandleValid(GetSharedHandle()));
+    DCHECK(shared_handle_.IsValid());
 
     const viz::ResourceFormat viz_resource_format = format();
     const WGPUTextureFormat wgpu_format =
@@ -457,7 +136,7 @@
     dawn_native::d3d12::ExternalImageDescriptorDXGISharedHandle
         externalImageDesc;
     externalImageDesc.cTextureDescriptor = &texture_descriptor;
-    externalImageDesc.sharedHandle = GetSharedHandle();
+    externalImageDesc.sharedHandle = shared_handle_.Get();
 
     external_image_ = dawn_native::d3d12::ExternalImageDXGI::Create(
         device, &externalImageDesc);
@@ -484,38 +163,65 @@
   // various GPU dumps.
   auto client_guid = GetSharedImageGUIDForTracing(mailbox());
   base::trace_event::MemoryAllocatorDumpGuid service_guid =
-      gl::GetGLTextureServiceGUIDForTracing(gl_texture_->service_id());
+      gl::GetGLTextureServiceGUIDForTracing(texture_->service_id());
   pmd->CreateSharedGlobalAllocatorDump(service_guid);
 
   int importance = 2;  // This client always owns the ref.
   pmd->AddOwnershipEdge(client_guid, service_guid, importance);
 
   // Swap chain textures only have one level backed by an image.
-  GetGLImage()->OnMemoryDump(pmd, client_tracing_id, dump_name);
+  image_->OnMemoryDump(pmd, client_tracing_id, dump_name);
 }
 
 bool SharedImageBackingD3D::BeginAccessD3D12(uint64_t* acquire_key) {
-  return shared_state_->BeginAccessD3D12(acquire_key);
+  if (keyed_mutex_acquired_) {
+    DLOG(ERROR) << "Recursive BeginAccess not supported";
+    return false;
+  }
+  *acquire_key = keyed_mutex_acquire_key_;
+  keyed_mutex_acquire_key_++;
+  keyed_mutex_acquired_ = true;
+  return true;
 }
 
 void SharedImageBackingD3D::EndAccessD3D12() {
-  shared_state_->EndAccessD3D12();
+  keyed_mutex_acquired_ = false;
 }
 
 bool SharedImageBackingD3D::BeginAccessD3D11() {
-  return shared_state_->BeginAccessD3D11();
+  if (dxgi_keyed_mutex_) {
+    if (keyed_mutex_acquired_) {
+      DLOG(ERROR) << "Recursive BeginAccess not supported";
+      return false;
+    }
+    const HRESULT hr =
+        dxgi_keyed_mutex_->AcquireSync(keyed_mutex_acquire_key_, INFINITE);
+    if (FAILED(hr)) {
+      DLOG(ERROR) << "Unable to acquire the keyed mutex " << std::hex << hr;
+      return false;
+    }
+    keyed_mutex_acquire_key_++;
+    keyed_mutex_acquired_ = true;
+  }
+  return true;
 }
-
 void SharedImageBackingD3D::EndAccessD3D11() {
-  shared_state_->EndAccessD3D11();
+  if (dxgi_keyed_mutex_) {
+    const HRESULT hr = dxgi_keyed_mutex_->ReleaseSync(keyed_mutex_acquire_key_);
+    if (FAILED(hr)) {
+      DLOG(ERROR) << "Unable to release the keyed mutex " << std::hex << hr;
+      return;
+    }
+    keyed_mutex_acquired_ = false;
+  }
 }
 
 HANDLE SharedImageBackingD3D::GetSharedHandle() const {
-  return shared_state_->GetSharedHandle();
+  return shared_handle_.Get();
 }
 
 gl::GLImage* SharedImageBackingD3D::GetGLImage() const {
-  return gl_texture_->GetLevelImage(gl_texture_->target(), 0u);
+  return image_.get();
 }
 
 bool SharedImageBackingD3D::PresentSwapChain() {
@@ -538,12 +244,10 @@
   }
 
   gl::GLApi* const api = gl::g_current_gl_context;
+  ScopedRestoreTexture2D scoped_restore(api);
 
-  DCHECK_EQ(gl_texture_->target(), static_cast<unsigned>(GL_TEXTURE_2D));
-  ScopedRestoreTexture scoped_restore(api, GL_TEXTURE_2D);
-
-  api->glBindTextureFn(GL_TEXTURE_2D, gl_texture_->service_id());
-  if (!GetGLImage()->BindTexImage(GL_TEXTURE_2D)) {
+  api->glBindTextureFn(GL_TEXTURE_2D, texture_->service_id());
+  if (!image_->BindTexImage(GL_TEXTURE_2D)) {
     DLOG(ERROR) << "GLImage::BindTexImage failed";
     return false;
   }
@@ -559,7 +263,7 @@
                                                    MemoryTypeTracker* tracker) {
   TRACE_EVENT0("gpu", "SharedImageBackingD3D::ProduceGLTexturePassthrough");
   return std::make_unique<SharedImageRepresentationGLTexturePassthroughD3D>(
-      manager, this, tracker, gl_texture_);
+      manager, this, tracker, texture_);
 }
 
 std::unique_ptr<SharedImageRepresentationSkia>
diff --git a/gpu/command_buffer/service/shared_image_backing_d3d.h b/gpu/command_buffer/service/shared_image_backing_d3d.h
index 5af24d9..b6bdb07 100644
--- a/gpu/command_buffer/service/shared_image_backing_d3d.h
+++ b/gpu/command_buffer/service/shared_image_backing_d3d.h
@@ -42,7 +42,7 @@
 class GPU_GLES2_EXPORT SharedImageBackingD3D
     : public ClearTrackingSharedImageBacking {
  public:
-  static std::unique_ptr<SharedImageBackingD3D> CreateFromSwapChainBuffer(
+  SharedImageBackingD3D(
       const Mailbox& mailbox,
       viz::ResourceFormat format,
       const gfx::Size& size,
@@ -50,42 +50,13 @@
       GrSurfaceOrigin surface_origin,
       SkAlphaType alpha_type,
       uint32_t usage,
-      Microsoft::WRL::ComPtr<ID3D11Texture2D> d3d11_texture,
       Microsoft::WRL::ComPtr<IDXGISwapChain1> swap_chain,
-      size_t buffer_index);
-
-  static std::unique_ptr<SharedImageBackingD3D> CreateFromSharedHandle(
-      const Mailbox& mailbox,
-      viz::ResourceFormat format,
-      const gfx::Size& size,
-      const gfx::ColorSpace& color_space,
-      GrSurfaceOrigin surface_origin,
-      SkAlphaType alpha_type,
-      uint32_t usage,
+      scoped_refptr<gles2::TexturePassthrough> texture,
+      scoped_refptr<gl::GLImage> image,
+      size_t buffer_index,
       Microsoft::WRL::ComPtr<ID3D11Texture2D> d3d11_texture,
-      base::win::ScopedHandle shared_handle);
-
-  // TODO(sunnyps): Remove this after migrating DXVA decoder to EGLImage.
-  static std::unique_ptr<SharedImageBackingD3D> CreateFromGLTexture(
-      const Mailbox& mailbox,
-      viz::ResourceFormat format,
-      const gfx::Size& size,
-      const gfx::ColorSpace& color_space,
-      GrSurfaceOrigin surface_origin,
-      SkAlphaType alpha_type,
-      uint32_t usage,
-      Microsoft::WRL::ComPtr<ID3D11Texture2D> d3d11_texture,
-      scoped_refptr<gles2::TexturePassthrough> gl_texture);
-
-  static std::vector<std::unique_ptr<SharedImageBackingD3D>>
-  CreateFromVideoTexture(
-      base::span<const Mailbox> mailboxes,
-      DXGI_FORMAT dxgi_format,
-      const gfx::Size& size,
-      uint32_t usage,
-      Microsoft::WRL::ComPtr<ID3D11Texture2D> d3d11_texture,
-      unsigned array_slice,
-      base::win::ScopedHandle shared_handle = base::win::ScopedHandle());
+      base::win::ScopedHandle shared_handle,
+      Microsoft::WRL::ComPtr<IDXGIKeyedMutex> dxgi_keyed_mutex);
 
   ~SharedImageBackingD3D() override;
 
@@ -129,64 +100,29 @@
       scoped_refptr<SharedContextState> context_state) override;
 
  private:
-  class SharedState : public base::RefCountedThreadSafe<SharedState> {
-   public:
-    explicit SharedState(
-        base::win::ScopedHandle shared_handle = base::win::ScopedHandle(),
-        Microsoft::WRL::ComPtr<IDXGIKeyedMutex> dxgi_keyed_mutex = nullptr);
-
-    bool BeginAccessD3D11();
-    void EndAccessD3D11();
-
-    bool BeginAccessD3D12(uint64_t* acquire_key);
-    void EndAccessD3D12();
-
-    HANDLE GetSharedHandle() const;
-
-   private:
-    friend class base::RefCountedThreadSafe<SharedState>;
-    ~SharedState();
-
-    // If |d3d11_texture_| has a keyed mutex, it will be stored in
-    // |dxgi_keyed_mutex_|. The keyed mutex is used to synchronize D3D11 and
-    // D3D12 Chromium components. |dxgi_keyed_mutex_| is the D3D11 side of the
-    // keyed mutex. To create the corresponding D3D12 interface, pass the handle
-    // stored in |shared_handle_| to ID3D12Device::OpenSharedHandle. Only one
-    // component is allowed to read/write to the texture at a time.
-    // |acquire_key_| is incremented on every Acquire/Release usage.
-    base::win::ScopedHandle shared_handle_;
-    Microsoft::WRL::ComPtr<IDXGIKeyedMutex> dxgi_keyed_mutex_;
-    uint64_t acquire_key_ = 0;
-    bool acquired_for_d3d12_ = false;
-    int acquired_for_d3d11_count_ = 0;
-  };
-
-  SharedImageBackingD3D(
-      const Mailbox& mailbox,
-      viz::ResourceFormat format,
-      const gfx::Size& size,
-      const gfx::ColorSpace& color_space,
-      GrSurfaceOrigin surface_origin,
-      SkAlphaType alpha_type,
-      uint32_t usage,
-      Microsoft::WRL::ComPtr<ID3D11Texture2D> d3d11_texture,
-      scoped_refptr<gles2::TexturePassthrough> gl_texture,
-      Microsoft::WRL::ComPtr<IDXGISwapChain1> swap_chain = nullptr,
-      size_t buffer_index = 0,
-      scoped_refptr<SharedState> shared_state =
-          base::MakeRefCounted<SharedState>());
-
   uint32_t GetAllowedDawnUsages() const;
 
+  Microsoft::WRL::ComPtr<IDXGISwapChain1> swap_chain_;
+  scoped_refptr<gles2::TexturePassthrough> texture_;
+  scoped_refptr<gl::GLImage> image_;
+  const size_t buffer_index_;
+
   // Texture could be nullptr if an empty backing is needed for testing.
   Microsoft::WRL::ComPtr<ID3D11Texture2D> d3d11_texture_;
-  scoped_refptr<gles2::TexturePassthrough> gl_texture_;
 
-  Microsoft::WRL::ComPtr<IDXGISwapChain1> swap_chain_;
-
-  const size_t buffer_index_;
-
-  scoped_refptr<SharedState> shared_state_;
+  // If d3d11_texture_ has a keyed mutex, it will be stored in
+  // dxgi_keyed_mutex. The keyed mutex is used to synchronize
+  // D3D11 and D3D12 Chromium components.
+  // dxgi_keyed_mutex_ is the D3D11 side of the keyed mutex.
+  // To create the corresponding D3D12 interface, pass the handle
+  // stored in shared_handle_ to ID3D12Device::OpenSharedHandle.
+  // Only one component is allowed to read/write to the texture
+  // at a time. keyed_mutex_acquire_key_ is incremented on every
+  // Acquire/Release usage.
+  base::win::ScopedHandle shared_handle_;
+  Microsoft::WRL::ComPtr<IDXGIKeyedMutex> dxgi_keyed_mutex_;
+  uint64_t keyed_mutex_acquire_key_ = 0;
+  bool keyed_mutex_acquired_ = false;
 
   // If external_image_ exists, it means Dawn produced the D3D12 side of the
   // D3D11 texture created by ID3D12Device::OpenSharedHandle.
diff --git a/gpu/command_buffer/service/shared_image_backing_factory_d3d.cc b/gpu/command_buffer/service/shared_image_backing_factory_d3d.cc
index e2a8c22..02225c6 100644
--- a/gpu/command_buffer/service/shared_image_backing_factory_d3d.cc
+++ b/gpu/command_buffer/service/shared_image_backing_factory_d3d.cc
@@ -18,6 +18,24 @@
 
 namespace {
 
+class ScopedRestoreTexture2D {
+ public:
+  explicit ScopedRestoreTexture2D(gl::GLApi* api) : api_(api) {
+    GLint binding = 0;
+    api->glGetIntegervFn(GL_TEXTURE_BINDING_2D, &binding);
+    prev_binding_ = binding;
+  }
+
+  ~ScopedRestoreTexture2D() {
+    api_->glBindTextureFn(GL_TEXTURE_2D, prev_binding_);
+  }
+
+ private:
+  gl::GLApi* const api_;
+  GLuint prev_binding_ = 0;
+  DISALLOW_COPY_AND_ASSIGN(ScopedRestoreTexture2D);
+};
+
 bool ClearBackBuffer(Microsoft::WRL::ComPtr<IDXGISwapChain1>& swap_chain,
                      Microsoft::WRL::ComPtr<ID3D11Device>& d3d11_device) {
   Microsoft::WRL::ComPtr<ID3D11Texture2D> d3d11_texture;
@@ -90,6 +108,80 @@
          gl::DirectCompositionSurfaceWin::IsSwapChainTearingSupported();
 }
 
+std::unique_ptr<SharedImageBacking> SharedImageBackingFactoryD3D::MakeBacking(
+    const Mailbox& mailbox,
+    viz::ResourceFormat format,
+    const gfx::Size& size,
+    const gfx::ColorSpace& color_space,
+    GrSurfaceOrigin surface_origin,
+    SkAlphaType alpha_type,
+    uint32_t usage,
+    Microsoft::WRL::ComPtr<IDXGISwapChain1> swap_chain,
+    size_t buffer_index,
+    Microsoft::WRL::ComPtr<ID3D11Texture2D> d3d11_texture,
+    base::win::ScopedHandle shared_handle) {
+  gl::GLApi* const api = gl::g_current_gl_context;
+  ScopedRestoreTexture2D scoped_restore(api);
+
+  const GLenum target = GL_TEXTURE_2D;
+  GLuint service_id = 0;
+  api->glGenTexturesFn(1, &service_id);
+  api->glBindTextureFn(target, service_id);
+  api->glTexParameteriFn(target, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+  api->glTexParameteriFn(target, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+  api->glTexParameteriFn(target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+  api->glTexParameteriFn(target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+
+  Microsoft::WRL::ComPtr<IDXGIKeyedMutex> dxgi_keyed_mutex;
+
+  if (swap_chain) {
+    DCHECK(!d3d11_texture);
+    DCHECK(!shared_handle.IsValid());
+    const HRESULT hr =
+        swap_chain->GetBuffer(buffer_index, IID_PPV_ARGS(&d3d11_texture));
+    if (FAILED(hr)) {
+      DLOG(ERROR) << "GetBuffer failed with error " << std::hex;
+      return nullptr;
+    }
+  } else if (shared_handle.IsValid()) {
+    // Keyed mutexes are required for Dawn interop but are not used
+    // for XR composition where fences are used instead.
+    d3d11_texture.As(&dxgi_keyed_mutex);
+  }
+  DCHECK(d3d11_texture);
+
+  // The GL internal format can differ from the underlying swap chain format
+  // e.g. RGBA8 or RGB8 instead of BGRA8.
+  const GLenum internal_format = viz::GLInternalFormat(format);
+  const GLenum data_type = viz::GLDataType(format);
+  const GLenum data_format = viz::GLDataFormat(format);
+  auto image = base::MakeRefCounted<gl::GLImageD3D>(
+      size, internal_format, data_type, d3d11_texture, swap_chain);
+  DCHECK_EQ(image->GetDataFormat(), data_format);
+  if (!image->Initialize()) {
+    DLOG(ERROR) << "GLImageD3D::Initialize failed";
+    return nullptr;
+  }
+  if (!image->BindTexImage(target)) {
+    DLOG(ERROR) << "GLImageD3D::BindTexImage failed";
+    return nullptr;
+  }
+
+  scoped_refptr<gles2::TexturePassthrough> texture =
+      base::MakeRefCounted<gles2::TexturePassthrough>(service_id, target);
+  texture->SetLevelImage(target, 0, image.get());
+  GLint texture_memory_size = 0;
+  api->glGetTexParameterivFn(target, GL_MEMORY_SIZE_ANGLE,
+                             &texture_memory_size);
+  texture->SetEstimatedSize(texture_memory_size);
+
+  return std::make_unique<SharedImageBackingD3D>(
+      mailbox, format, size, color_space, surface_origin, alpha_type, usage,
+      std::move(swap_chain), std::move(texture), std::move(image), buffer_index,
+      std::move(d3d11_texture), std::move(shared_handle),
+      std::move(dxgi_keyed_mutex));
+}
+
 SharedImageBackingFactoryD3D::SwapChainBackings
 SharedImageBackingFactoryD3D::CreateSwapChain(
     const Mailbox& front_buffer_mailbox,
@@ -171,30 +263,18 @@
   if (!ClearBackBuffer(swap_chain, d3d11_device_))
     return {nullptr, nullptr};
 
-  Microsoft::WRL::ComPtr<ID3D11Texture2D> back_buffer_texture;
-  hr = swap_chain->GetBuffer(0, IID_PPV_ARGS(&back_buffer_texture));
-  if (FAILED(hr)) {
-    DLOG(ERROR) << "GetBuffer failed with error " << std::hex;
-    return {nullptr, nullptr};
-  }
-  auto back_buffer_backing = SharedImageBackingD3D::CreateFromSwapChainBuffer(
+  auto back_buffer_backing = MakeBacking(
       back_buffer_mailbox, format, size, color_space, surface_origin,
-      alpha_type, usage, std::move(back_buffer_texture), swap_chain,
-      /*buffer_index=*/0);
+      alpha_type, usage, swap_chain, 0 /* buffer_index */,
+      nullptr /* d3d11_texture */, base::win::ScopedHandle());
   if (!back_buffer_backing)
     return {nullptr, nullptr};
   back_buffer_backing->SetCleared();
 
-  Microsoft::WRL::ComPtr<ID3D11Texture2D> front_buffer_texture;
-  hr = swap_chain->GetBuffer(1, IID_PPV_ARGS(&front_buffer_texture));
-  if (FAILED(hr)) {
-    DLOG(ERROR) << "GetBuffer failed with error " << std::hex;
-    return {nullptr, nullptr};
-  }
-  auto front_buffer_backing = SharedImageBackingD3D::CreateFromSwapChainBuffer(
+  auto front_buffer_backing = MakeBacking(
       front_buffer_mailbox, format, size, color_space, surface_origin,
-      alpha_type, usage, std::move(front_buffer_texture), swap_chain,
-      /*buffer_index=*/1);
+      alpha_type, usage, swap_chain, 1 /* buffer_index */,
+      nullptr /* d3d11_texture */, base::win::ScopedHandle());
   if (!front_buffer_backing)
     return {nullptr, nullptr};
   front_buffer_backing->SetCleared();
@@ -265,12 +345,14 @@
                 << std::hex << hr;
     return nullptr;
   }
+
   // Put the shared handle into an RAII object as quickly as possible to
   // ensure we do not leak it.
   base::win::ScopedHandle scoped_shared_handle(shared_handle);
-  return SharedImageBackingD3D::CreateFromSharedHandle(
-      mailbox, format, size, color_space, surface_origin, alpha_type, usage,
-      std::move(d3d11_texture), std::move(scoped_shared_handle));
+
+  return MakeBacking(mailbox, format, size, color_space, surface_origin,
+                     alpha_type, usage, nullptr, 0, std::move(d3d11_texture),
+                     std::move(scoped_shared_handle));
 }
 
 std::unique_ptr<SharedImageBacking>
@@ -345,10 +427,11 @@
     return nullptr;
   }
 
-  auto backing = SharedImageBackingD3D::CreateFromSharedHandle(
-      mailbox, viz::GetResourceFormat(format), size, color_space,
-      surface_origin, alpha_type, usage, std::move(d3d11_texture),
-      std::move(handle.dxgi_handle));
+  auto backing =
+      MakeBacking(mailbox, viz::GetResourceFormat(format), size, color_space,
+                  surface_origin, alpha_type, usage, /*swap_chain=*/nullptr,
+                  /*buffer_index=*/0, std::move(d3d11_texture),
+                  std::move(handle.dxgi_handle));
   if (backing)
     backing->SetCleared();
   return backing;
diff --git a/gpu/command_buffer/service/shared_image_backing_factory_d3d_unittest.cc b/gpu/command_buffer/service/shared_image_backing_factory_d3d_unittest.cc
index c84072b..356d812 100644
--- a/gpu/command_buffer/service/shared_image_backing_factory_d3d_unittest.cc
+++ b/gpu/command_buffer/service/shared_image_backing_factory_d3d_unittest.cc
@@ -33,15 +33,6 @@
 #include <dawn_native/DawnNative.h>
 #endif  // BUILDFLAG(USE_DAWN)
 
-#define SCOPED_GL_CLEANUP_VAR(api, func, var)            \
-  base::ScopedClosureRunner delete_##var(base::BindOnce( \
-      [](gl::GLApi* api, GLuint var) { api->gl##func##Fn(var); }, api, var))
-
-#define SCOPED_GL_CLEANUP_PTR(api, func, n, var)                           \
-  base::ScopedClosureRunner delete_##var(base::BindOnce(                   \
-      [](gl::GLApi* api, GLuint var) { api->gl##func##Fn(n, &var); }, api, \
-      var))
-
 namespace gpu {
 namespace {
 
@@ -461,8 +452,6 @@
     }
   }
 
-  void RunVideoTest(bool use_shared_handle);
-
   scoped_refptr<SharedContextState> context_state_;
 };
 
@@ -1049,248 +1038,5 @@
 }
 #endif  // BUILDFLAG(USE_DAWN)
 
-void SharedImageBackingFactoryD3DTest::RunVideoTest(bool use_shared_handle) {
-  if (!IsD3DSharedImageSupported())
-    return;
-
-  Microsoft::WRL::ComPtr<ID3D11Device> d3d11_device =
-      shared_image_factory_->GetDeviceForTesting();
-
-  const gfx::Size size(32, 32);
-
-  const unsigned kYFillValue = 0x12;
-  const unsigned kUFillValue = 0x23;
-  const unsigned kVFillValue = 0x34;
-
-  const size_t kYPlaneSize = size.width() * size.height();
-
-  std::vector<unsigned char> video_data;
-  video_data.resize(kYPlaneSize * 3 / 2);
-  memset(video_data.data(), kYFillValue, kYPlaneSize);
-  for (size_t i = 0; i < kYPlaneSize / 2; i += 2) {
-    video_data[kYPlaneSize + i] = kUFillValue;
-    video_data[kYPlaneSize + i + 1] = kVFillValue;
-  }
-
-  D3D11_SUBRESOURCE_DATA data = {};
-  data.pSysMem = static_cast<const void*>(video_data.data());
-  data.SysMemPitch = static_cast<UINT>(size.width());
-
-  CD3D11_TEXTURE2D_DESC desc(DXGI_FORMAT_NV12, size.width(), size.height(), 1,
-                             1, D3D11_BIND_SHADER_RESOURCE);
-  if (use_shared_handle) {
-    desc.MiscFlags = D3D11_RESOURCE_MISC_SHARED_NTHANDLE |
-                     D3D11_RESOURCE_MISC_SHARED_KEYEDMUTEX;
-  }
-
-  Microsoft::WRL::ComPtr<ID3D11Texture2D> d3d11_texture;
-  HRESULT hr = d3d11_device->CreateTexture2D(&desc, &data, &d3d11_texture);
-  ASSERT_TRUE(SUCCEEDED(hr));
-
-  uint32_t usage =
-      gpu::SHARED_IMAGE_USAGE_VIDEO_DECODE | gpu::SHARED_IMAGE_USAGE_GLES2 |
-      gpu::SHARED_IMAGE_USAGE_RASTER | gpu::SHARED_IMAGE_USAGE_DISPLAY |
-      gpu::SHARED_IMAGE_USAGE_SCANOUT;
-
-  base::win::ScopedHandle shared_handle;
-  if (use_shared_handle) {
-    Microsoft::WRL::ComPtr<IDXGIResource1> dxgi_resource;
-    hr = d3d11_texture.As(&dxgi_resource);
-    ASSERT_TRUE(SUCCEEDED(hr));
-
-    HANDLE handle;
-    hr = dxgi_resource->CreateSharedHandle(
-        nullptr, DXGI_SHARED_RESOURCE_READ | DXGI_SHARED_RESOURCE_WRITE,
-        nullptr, &handle);
-    ASSERT_TRUE(SUCCEEDED(hr));
-
-    shared_handle.Set(handle);
-    ASSERT_TRUE(shared_handle.IsValid());
-
-    usage |= gpu::SHARED_IMAGE_USAGE_WEBGPU;
-  }
-
-  const gpu::Mailbox mailboxes[] = {gpu::Mailbox::GenerateForSharedImage(),
-                                    gpu::Mailbox::GenerateForSharedImage()};
-
-  auto shared_image_backings = SharedImageBackingD3D::CreateFromVideoTexture(
-      mailboxes, DXGI_FORMAT_NV12, size, usage, d3d11_texture,
-      /*array_slice=*/0, std::move(shared_handle));
-  ASSERT_EQ(shared_image_backings.size(), 2u);
-
-  const gfx::Size plane_sizes[] = {
-      size, gfx::Size(size.width() / 2, size.height() / 2)};
-  const viz::ResourceFormat plane_formats[] = {viz::RED_8, viz::RG_88};
-
-  std::vector<std::unique_ptr<SharedImageRepresentationFactoryRef>>
-      shared_image_refs;
-  for (size_t i = 0; i < shared_image_backings.size(); i++) {
-    auto& backing = shared_image_backings[i];
-
-    EXPECT_EQ(backing->mailbox(), mailboxes[i]);
-    EXPECT_EQ(backing->size(), plane_sizes[i]);
-    EXPECT_EQ(backing->format(), plane_formats[i]);
-    EXPECT_EQ(backing->color_space(), gfx::ColorSpace());
-    EXPECT_EQ(backing->surface_origin(), kTopLeft_GrSurfaceOrigin);
-    EXPECT_EQ(backing->alpha_type(), kPremul_SkAlphaType);
-    EXPECT_EQ(backing->usage(), usage);
-    EXPECT_TRUE(backing->IsCleared());
-
-    shared_image_refs.push_back(shared_image_manager_.Register(
-        std::move(backing), memory_type_tracker_.get()));
-  }
-
-  // Setup GL shaders, framebuffers, uniforms, etc.
-  static const char* kVideoFragmentShaderSrc =
-      "#extension GL_OES_EGL_image_external : require\n"
-      "precision mediump float;\n"
-      "uniform samplerExternalOES u_texture_y;\n"
-      "uniform samplerExternalOES u_texture_uv;\n"
-      "varying vec2 v_texCoord;\n"
-      "void main() {\n"
-      "  gl_FragColor.r = texture2D(u_texture_y, v_texCoord).r;\n"
-      "  gl_FragColor.gb = texture2D(u_texture_uv, v_texCoord).rg;\n"
-      "  gl_FragColor.a = 1.0;\n"
-      "}\n";
-
-  gl::GLApi* api = gl::g_current_gl_context;
-
-  GLint status = 0;
-  GLuint vertex_shader = api->glCreateShaderFn(GL_VERTEX_SHADER);
-  SCOPED_GL_CLEANUP_VAR(api, DeleteShader, vertex_shader);
-  ASSERT_NE(vertex_shader, 0u);
-  api->glShaderSourceFn(vertex_shader, 1, &kVertexShaderSrc, nullptr);
-  api->glCompileShaderFn(vertex_shader);
-  api->glGetShaderivFn(vertex_shader, GL_COMPILE_STATUS, &status);
-  ASSERT_NE(status, 0);
-
-  GLuint fragment_shader = api->glCreateShaderFn(GL_FRAGMENT_SHADER);
-  SCOPED_GL_CLEANUP_VAR(api, DeleteShader, fragment_shader);
-  ASSERT_NE(fragment_shader, 0u);
-  api->glShaderSourceFn(fragment_shader, 1, &kVideoFragmentShaderSrc, nullptr);
-  api->glCompileShaderFn(fragment_shader);
-  api->glGetShaderivFn(fragment_shader, GL_COMPILE_STATUS, &status);
-  ASSERT_NE(status, 0);
-
-  GLuint program = api->glCreateProgramFn();
-  ASSERT_NE(program, 0u);
-  SCOPED_GL_CLEANUP_VAR(api, DeleteProgram, program);
-  api->glAttachShaderFn(program, vertex_shader);
-  api->glAttachShaderFn(program, fragment_shader);
-  api->glLinkProgramFn(program);
-  api->glGetProgramivFn(program, GL_LINK_STATUS, &status);
-  ASSERT_NE(status, 0);
-
-  GLint vertex_location = api->glGetAttribLocationFn(program, "a_position");
-  ASSERT_NE(vertex_location, -1);
-
-  GLint y_texture_location =
-      api->glGetUniformLocationFn(program, "u_texture_y");
-  ASSERT_NE(y_texture_location, -1);
-
-  GLint uv_texture_location =
-      api->glGetUniformLocationFn(program, "u_texture_uv");
-  ASSERT_NE(uv_texture_location, -1);
-
-  GLuint fbo, renderbuffer = 0u;
-  api->glGenFramebuffersEXTFn(1, &fbo);
-  ASSERT_NE(fbo, 0u);
-  SCOPED_GL_CLEANUP_PTR(api, DeleteFramebuffersEXT, 1, fbo);
-  api->glBindFramebufferEXTFn(GL_FRAMEBUFFER, fbo);
-
-  api->glGenRenderbuffersEXTFn(1, &renderbuffer);
-  ASSERT_NE(renderbuffer, 0u);
-  SCOPED_GL_CLEANUP_PTR(api, DeleteRenderbuffersEXT, 1, renderbuffer);
-  api->glBindRenderbufferEXTFn(GL_RENDERBUFFER, renderbuffer);
-
-  api->glRenderbufferStorageEXTFn(GL_RENDERBUFFER, GL_RGBA8_OES, size.width(),
-                                  size.height());
-  api->glFramebufferRenderbufferEXTFn(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
-                                      GL_RENDERBUFFER, renderbuffer);
-  ASSERT_EQ(api->glCheckFramebufferStatusEXTFn(GL_FRAMEBUFFER),
-            static_cast<unsigned>(GL_FRAMEBUFFER_COMPLETE));
-
-  // Set the clear color to green.
-  api->glViewportFn(0, 0, size.width(), size.height());
-  api->glClearColorFn(0.0f, 1.0f, 0.0f, 1.0f);
-  api->glClearFn(GL_COLOR_BUFFER_BIT);
-
-  GLuint vbo = 0u;
-  api->glGenBuffersARBFn(1, &vbo);
-  ASSERT_NE(vbo, 0u);
-  SCOPED_GL_CLEANUP_PTR(api, DeleteBuffersARB, 1, vbo);
-  api->glBindBufferFn(GL_ARRAY_BUFFER, vbo);
-  static const float vertices[] = {
-      1.0f, 1.0f, -1.0f, 1.0f,  -1.0f, -1.0f,
-      1.0f, 1.0f, -1.0f, -1.0f, 1.0f,  -1.0f,
-  };
-  api->glBufferDataFn(GL_ARRAY_BUFFER, sizeof(vertices), vertices,
-                      GL_STATIC_DRAW);
-
-  ASSERT_EQ(api->glGetErrorFn(), static_cast<GLenum>(GL_NO_ERROR));
-
-  // Create the representations for the planes, get the texture ids, bind to
-  // samplers, and draw.
-  {
-    auto y_texture =
-        shared_image_representation_factory_->ProduceGLTexturePassthrough(
-            mailboxes[0]);
-    ASSERT_NE(y_texture, nullptr);
-
-    auto uv_texture =
-        shared_image_representation_factory_->ProduceGLTexturePassthrough(
-            mailboxes[1]);
-    ASSERT_NE(uv_texture, nullptr);
-
-    auto y_texture_access = y_texture->BeginScopedAccess(
-        GL_SHARED_IMAGE_ACCESS_MODE_READ_CHROMIUM,
-        SharedImageRepresentation::AllowUnclearedAccess::kNo);
-    ASSERT_NE(y_texture_access, nullptr);
-
-    auto uv_texture_access = uv_texture->BeginScopedAccess(
-        GL_SHARED_IMAGE_ACCESS_MODE_READ_CHROMIUM,
-        SharedImageRepresentation::AllowUnclearedAccess::kNo);
-    ASSERT_NE(uv_texture_access, nullptr);
-
-    api->glActiveTextureFn(GL_TEXTURE0);
-    api->glBindTextureFn(GL_TEXTURE_EXTERNAL_OES,
-                         y_texture->GetTextureBase()->service_id());
-    ASSERT_EQ(api->glGetErrorFn(), static_cast<GLenum>(GL_NO_ERROR));
-
-    api->glActiveTextureFn(GL_TEXTURE1);
-    api->glBindTextureFn(GL_TEXTURE_EXTERNAL_OES,
-                         uv_texture->GetTextureBase()->service_id());
-    ASSERT_EQ(api->glGetErrorFn(), static_cast<GLenum>(GL_NO_ERROR));
-
-    api->glUseProgramFn(program);
-
-    api->glEnableVertexAttribArrayFn(vertex_location);
-    api->glVertexAttribPointerFn(vertex_location, 2, GL_FLOAT, GL_FALSE, 0, 0);
-
-    api->glUniform1iFn(y_texture_location, 0);
-    api->glUniform1iFn(uv_texture_location, 1);
-
-    api->glDrawArraysFn(GL_TRIANGLES, 0, 6);
-    ASSERT_EQ(api->glGetErrorFn(), static_cast<GLenum>(GL_NO_ERROR));
-
-    GLubyte pixel_color[4];
-    api->glReadPixelsFn(size.width() / 2, size.height() / 2, 1, 1, GL_RGBA,
-                        GL_UNSIGNED_BYTE, pixel_color);
-    EXPECT_EQ(kYFillValue, pixel_color[0]);
-    EXPECT_EQ(kUFillValue, pixel_color[1]);
-    EXPECT_EQ(kVFillValue, pixel_color[2]);
-    EXPECT_EQ(0xff, pixel_color[3]);
-  }
-  // TODO(dawn:551): Test Dawn access after multi-planar support lands in Dawn.
-}
-
-TEST_F(SharedImageBackingFactoryD3DTest, CreateFromVideoTexture) {
-  RunVideoTest(/*use_shared_handle=*/false);
-}
-
-TEST_F(SharedImageBackingFactoryD3DTest, CreateFromVideoTextureSharedHandle) {
-  RunVideoTest(/*use_shared_handle=*/true);
-}
-
 }  // anonymous namespace
 }  // namespace gpu
diff --git a/infra/config/generated/cr-buildbucket.cfg b/infra/config/generated/cr-buildbucket.cfg
index 00ba0a4..9cbb381a 100644
--- a/infra/config/generated/cr-buildbucket.cfg
+++ b/infra/config/generated/cr-buildbucket.cfg
@@ -33671,6 +33671,84 @@
   }
 }
 buckets {
+  name: "reclient"
+  acls {
+    role: WRITER
+    group: "google/luci-task-force@google.com"
+  }
+  acls {
+    group: "all"
+  }
+  acls {
+    role: SCHEDULER
+    group: "project-chromium-ci-schedulers"
+  }
+  swarming {
+    builders {
+      name: "Linux Builder Re-Client Staging"
+      swarming_host: "chromium-swarm.appspot.com"
+      swarming_tags: "vpython:native-python-wrapper"
+      dimensions: "builderless:1"
+      dimensions: "cores:8"
+      dimensions: "cpu:x86-64"
+      dimensions: "os:Ubuntu-16.04"
+      dimensions: "pool:luci.chromium.ci"
+      dimensions: "ssd:0"
+      exe {
+        cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build"
+        cipd_version: "refs/heads/master"
+        cmd: "recipes"
+      }
+      properties: "{\"$build/reclient\":{\"instance\":\"rbe-chromium-trusted\",\"metrics_project\":\"chromium-reclient-metrics\"},\"$kitchen\":{\"devshell\":true,\"emulate_gce\":true,\"git_auth\":true},\"$recipe_engine/isolated\":{\"server\":\"https://isolateserver.appspot.com\"},\"builder_group\":\"chromium.reclient.fyi\",\"recipe\":\"chromium\"}"
+      execution_timeout_secs: 10800
+      build_numbers: YES
+      service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
+      experiments {
+        key: "chromium.resultdb.result_sink"
+        value: 100
+      }
+      experiments {
+        key: "chromium.resultdb.result_sink.gtests_local"
+        value: 100
+      }
+      experiments {
+        key: "chromium.resultdb.result_sink.junit_tests"
+        value: 100
+      }
+      experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 100
+      }
+      experiments {
+        key: "luci.use_realms"
+        value: 100
+      }
+      resultdb {
+        enable: true
+        bq_exports {
+          project: "luci-resultdb"
+          dataset: "chromium"
+          table: "ci_test_results"
+          test_results {}
+        }
+        bq_exports {
+          project: "luci-resultdb"
+          dataset: "chromium"
+          table: "gpu_ci_test_results"
+          test_results {
+            predicate {
+              test_id_regexp: "ninja://(chrome/test:|content/test:fuchsia_)telemetry_gpu_integration_test/.+"
+            }
+          }
+        }
+        history_options {
+          use_invocation_timestamp: true
+        }
+      }
+    }
+  }
+}
+buckets {
   name: "try"
   acls {
     role: WRITER
@@ -34567,7 +34645,7 @@
       swarming_host: "chromium-swarm.appspot.com"
       swarming_tags: "vpython:native-python-wrapper"
       dimensions: "builderless:1"
-      dimensions: "cores:32"
+      dimensions: "cores:16"
       dimensions: "cpu:x86-64"
       dimensions: "os:Ubuntu-16.04"
       dimensions: "pool:luci.chromium.try"
diff --git a/infra/config/generated/luci-milo.cfg b/infra/config/generated/luci-milo.cfg
index abb4a68..be6c4a9a 100644
--- a/infra/config/generated/luci-milo.cfg
+++ b/infra/config/generated/luci-milo.cfg
@@ -1937,9 +1937,6 @@
     name: "buildbucket/luci.chromium.try/android-marshmallow-arm64-rel"
   }
   builders {
-    name: "buildbucket/luci.chromium.try/android-marshmallow-arm64-rel-dual-coverage"
-  }
-  builders {
     name: "buildbucket/luci.chromium.try/android-pie-arm64-coverage-experimental-rel"
   }
   builders {
@@ -2051,9 +2048,6 @@
     name: "buildbucket/luci.chromium.try/linux-rel-compilator"
   }
   builders {
-    name: "buildbucket/luci.chromium.try/linux-rel-dual-coverage"
-  }
-  builders {
     name: "buildbucket/luci.chromium.try/linux-rel-orchestrator"
   }
   builders {
@@ -9819,6 +9813,302 @@
   }
 }
 consoles {
+  id: "chromium.reclient.fyi"
+  name: "chromium.reclient.fyi"
+  repo_url: "https://chromium.googlesource.com/chromium/src"
+  refs: "regexp:refs/heads/master"
+  manifest_name: "REVISION"
+  builders {
+    name: "buildbucket/luci.chromium.reclient/Linux Builder Re-Client Staging"
+    category: "rbe|linux"
+    short_name: "rcs"
+  }
+  header {
+    oncalls {
+      name: "Chromium"
+      url: "https://chrome-ops-rotation-proxy.appspot.com/current/oncallator:chrome-build-sheriff"
+    }
+    oncalls {
+      name: "Android"
+      url: "https://chrome-ops-rotation-proxy.appspot.com/current/oncallator:chrome-android-sheriff"
+    }
+    oncalls {
+      name: "iOS"
+      url: "https://chrome-ops-rotation-proxy.appspot.com/current/oncallator:chrome-ios"
+    }
+    oncalls {
+      name: "ChromeOS"
+      url: "https://chrome-ops-rotation-proxy.appspot.com/current/oncallator:chromeos-gardeners"
+    }
+    oncalls {
+      name: "GPU"
+      url: "https://chrome-ops-rotation-proxy.appspot.com/current/oncallator:chrome-gpu-pixel-wrangler"
+    }
+    oncalls {
+      name: "ANGLE"
+      url: "https://chrome-ops-rotation-proxy.appspot.com/current/grotation:angle-wrangler"
+    }
+    oncalls {
+      name: "Perf"
+      url: "https://chrome-ops-rotation-proxy.appspot.com/current/grotation:chromium-perf-regression-sheriff"
+    }
+    oncalls {
+      name: "Perfbot"
+      url: "https://chrome-ops-rotation-proxy.appspot.com/current/grotation:chromium-perf-bot-sheriff"
+    }
+    oncalls {
+      name: "Trooper"
+      url: "https://chrome-ops-rotation-proxy.appspot.com/current/oncallator:chrome-ops-client-infra"
+      show_primary_secondary_labels: true
+    }
+    links {
+      name: "Builds"
+      links {
+        text: "continuous"
+        url: "https://commondatastorage.googleapis.com/chromium-browser-snapshots/index.html"
+        alt: "Continuous browser snapshots"
+      }
+      links {
+        text: "symbols"
+        url: "https://www.chromium.org/developers/how-tos/debugging-on-windows"
+        alt: "Windows Symbols"
+      }
+      links {
+        text: "status"
+        url: "https://chromium-status.appspot.com/"
+        alt: "Current tree status"
+      }
+    }
+    links {
+      name: "Dashboards"
+      links {
+        text: "perf"
+        url: "https://chromeperf.appspot.com/"
+        alt: "Chrome perf dashboard"
+      }
+      links {
+        text: "flake-portal"
+        url: "https://analysis.chromium.org/p/chromium/flake-portal"
+        alt: "New flake portal"
+      }
+      links {
+        text: "legacy-flakiness"
+        url: "https://test-results.appspot.com/dashboards/flakiness_dashboard.html"
+        alt: "Legacy flakiness dashboard"
+      }
+    }
+    links {
+      name: "Chromium"
+      links {
+        text: "source"
+        url: "https://chromium.googlesource.com/chromium/src"
+        alt: "Chromium source code repository"
+      }
+      links {
+        text: "reviews"
+        url: "https://chromium-review.googlesource.com"
+        alt: "Chromium code review tool"
+      }
+      links {
+        text: "bugs"
+        url: "https://crbug.com"
+        alt: "Chromium bug tracker"
+      }
+      links {
+        text: "coverage"
+        url: "https://analysis.chromium.org/p/chromium/coverage"
+        alt: "Chromium code coverage dashboard"
+      }
+      links {
+        text: "dev"
+        url: "https://dev.chromium.org/Home"
+        alt: "Chromium developer home page"
+      }
+      links {
+        text: "support"
+        url: "https://support.google.com/chrome/#topic=7438008"
+        alt: "Google Chrome help center"
+      }
+    }
+    links {
+      name: "Consoles"
+      links {
+        text: "android"
+        url: "/p/chromium/g/chromium.android"
+        alt: "Chromium Android console"
+      }
+      links {
+        text: "clang"
+        url: "/p/chromium/g/chromium.clang"
+        alt: "Chromium Clang console"
+      }
+      links {
+        text: "dawn"
+        url: "/p/chromium/g/chromium.dawn"
+        alt: "Chromium Dawn console"
+      }
+      links {
+        text: "fuzz"
+        url: "/p/chromium/g/chromium.fuzz"
+        alt: "Chromium Fuzz console"
+      }
+      links {
+        text: "fyi"
+        url: "/p/chromium/g/chromium.fyi"
+        alt: "Chromium FYI console"
+      }
+      links {
+        text: "gpu"
+        url: "/p/chromium/g/chromium.gpu"
+        alt: "Chromium GPU console"
+      }
+      links {
+        text: "perf"
+        url: "/p/chrome/g/chrome.perf/console"
+        alt: "Chromium Perf console"
+      }
+      links {
+        text: "perf.fyi"
+        url: "/p/chrome/g/chrome.perf.fyi/console"
+        alt: "Chromium Perf FYI console"
+      }
+      links {
+        text: "angle"
+        url: "/p/chromium/g/chromium.angle"
+        alt: "Chromium ANGLE console"
+      }
+      links {
+        text: "swangle"
+        url: "/p/chromium/g/chromium.swangle"
+        alt: "Chromium SWANGLE console"
+      }
+      links {
+        text: "webrtc"
+        url: "/p/chromium/g/chromium.webrtc"
+        alt: "Chromium WebRTC console"
+      }
+      links {
+        text: "chromiumos"
+        url: "/p/chromium/g/chromium.chromiumos"
+        alt: "ChromiumOS console"
+      }
+    }
+    links {
+      name: "Branch Consoles"
+      links {
+        text: "m86"
+        url: "/p/chromium-m86/g/main/console"
+      }
+      links {
+        text: "m88"
+        url: "/p/chromium-m88/g/main/console"
+      }
+      links {
+        text: "m89"
+        url: "/p/chromium-m89/g/main/console"
+      }
+      links {
+        text: "m90"
+        url: "/p/chromium-m90/g/main/console"
+      }
+      links {
+        text: "m91"
+        url: "/p/chromium-m91/g/main/console"
+      }
+      links {
+        text: "trunk"
+        url: "/p/chromium/g/main/console"
+        alt: "Trunk (ToT) console"
+      }
+    }
+    links {
+      name: "Tryservers"
+      links {
+        text: "android"
+        url: "/p/chromium/g/tryserver.chromium.android/builders"
+        alt: "Android"
+      }
+      links {
+        text: "angle"
+        url: "/p/chromium/g/tryserver.chromium.angle/builders"
+        alt: "Angle"
+      }
+      links {
+        text: "blink"
+        url: "/p/chromium/g/tryserver.blink/builders"
+        alt: "Blink"
+      }
+      links {
+        text: "chrome"
+        url: "/p/chrome/g/tryserver.chrome/builders"
+        alt: "Chrome"
+      }
+      links {
+        text: "chromiumos"
+        url: "/p/chromium/g/tryserver.chromium.chromiumos/builders"
+        alt: "ChromiumOS"
+      }
+      links {
+        text: "linux"
+        url: "/p/chromium/g/tryserver.chromium.linux/builders"
+        alt: "Linux"
+      }
+      links {
+        text: "mac"
+        url: "/p/chromium/g/tryserver.chromium.mac/builders"
+        alt: "Mac"
+      }
+      links {
+        text: "swangle"
+        url: "/p/chromium/g/tryserver.chromium.swangle/builders"
+        alt: "SWANGLE"
+      }
+      links {
+        text: "win"
+        url: "/p/chromium/g/tryserver.chromium.win/builders"
+        alt: "Win"
+      }
+    }
+    links {
+      name: "Navigate"
+      links {
+        text: "about"
+        url: "http://dev.chromium.org/developers/testing/chromium-build-infrastructure/tour-of-the-chromium-buildbot"
+        alt: "Tour of the console"
+      }
+      links {
+        text: "customize"
+        url: "https://chromium.googlesource.com/chromium/src/+/refs/heads/master/infra/config/generated/luci-milo.cfg"
+        alt: "Customize this console"
+      }
+    }
+    console_groups {
+      title {
+        text: "Tree Closers"
+        url: "https://chromium-status.appspot.com/"
+      }
+      console_ids: "chromium/chromium"
+      console_ids: "chromium/chromium.win"
+      console_ids: "chromium/chromium.mac"
+      console_ids: "chromium/chromium.linux"
+      console_ids: "chromium/chromium.chromiumos"
+      console_ids: "chrome/chrome"
+      console_ids: "chromium/chromium.memory"
+      console_ids: "chromium/chromium.gpu"
+    }
+    console_groups {
+      console_ids: "chromium/chromium.android"
+      console_ids: "chrome/chrome.perf"
+      console_ids: "chromium/chromium.gpu.fyi"
+      console_ids: "chromium/chromium.angle"
+      console_ids: "chromium/chromium.swangle"
+      console_ids: "chromium/chromium.fuzz"
+    }
+    tree_status_host: "chromium-status.appspot.com"
+  }
+  include_experimental_builds: true
+}
+consoles {
   id: "chromium.swangle"
   name: "chromium.swangle"
   repo_url: "https://chromium.googlesource.com/chromium/src"
diff --git a/infra/config/generated/luci-scheduler.cfg b/infra/config/generated/luci-scheduler.cfg
index dce6c2e..6b0a42d7 100644
--- a/infra/config/generated/luci-scheduler.cfg
+++ b/infra/config/generated/luci-scheduler.cfg
@@ -1637,6 +1637,16 @@
   }
 }
 job {
+  id: "Linux Builder Re-Client Staging"
+  realm: "reclient"
+  acl_sets: "reclient"
+  buildbucket {
+    server: "cr-buildbucket.appspot.com"
+    bucket: "luci.chromium.reclient"
+    builder: "Linux Builder Re-Client Staging"
+  }
+}
+job {
   id: "Linux CFI"
   realm: "ci"
   acl_sets: "ci"
@@ -7082,6 +7092,7 @@
   triggers: "linux-archive-rel-goma-rbe-latest"
   triggers: "mac-archive-rel-goma-rbe-canary"
   triggers: "mac-archive-rel-goma-rbe-latest"
+  triggers: "Linux Builder Re-Client Staging"
   triggers: "WebRTC Chromium Android Builder"
   triggers: "WebRTC Chromium Linux Builder"
   triggers: "WebRTC Chromium Mac Builder"
@@ -7146,6 +7157,16 @@
   }
 }
 acl_sets {
+  name: "reclient"
+  acls {
+    role: OWNER
+    granted_to: "group:project-chromium-admins"
+  }
+  acls {
+    granted_to: "group:all"
+  }
+}
+acl_sets {
   name: "webrtc"
   acls {
     role: OWNER
diff --git a/infra/config/generated/realms.cfg b/infra/config/generated/realms.cfg
index 2dd4b210..71ed93ca 100644
--- a/infra/config/generated/realms.cfg
+++ b/infra/config/generated/realms.cfg
@@ -222,6 +222,25 @@
   }
 }
 realms {
+  name: "reclient"
+  bindings {
+    role: "role/buildbucket.builderServiceAccount"
+    principals: "user:chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
+  }
+  bindings {
+    role: "role/buildbucket.owner"
+    principals: "group:google/luci-task-force@google.com"
+  }
+  bindings {
+    role: "role/buildbucket.reader"
+    principals: "group:all"
+  }
+  bindings {
+    role: "role/buildbucket.triggerer"
+    principals: "group:project-chromium-ci-schedulers"
+  }
+}
+realms {
   name: "try"
   bindings {
     role: "role/buildbucket.builderServiceAccount"
diff --git a/infra/config/main.star b/infra/config/main.star
index 82acf94..0a6264f 100755
--- a/infra/config/main.star
+++ b/infra/config/main.star
@@ -128,6 +128,7 @@
 branches.exec("//subprojects/codesearch/subproject.star")
 branches.exec("//subprojects/findit/subproject.star")
 branches.exec("//subprojects/goma/subproject.star")
+branches.exec("//subprojects/reclient/subproject.star")
 branches.exec("//subprojects/webrtc/subproject.star")
 
 branches.exec("//generators/cq-builders-md.star")
diff --git a/infra/config/subprojects/README.md b/infra/config/subprojects/README.md
index 3b9fbfa..d70575a 100644
--- a/infra/config/subprojects/README.md
+++ b/infra/config/subprojects/README.md
@@ -6,6 +6,7 @@
 * codesearch - Builders that test codesearch with chromium.
 * findit - Builders that are used by the Sheriff-o-Matic findit service.
 * goma - Builders that test the use of goma by chromium builders.
+* reclient - Builders that test the use of reclient by chromium builders.
 * webrtc - Builders that test the integration of WebRTC with chromium.
 
 Each subproject contains a subproject.star that is the entry point for its
diff --git a/infra/config/subprojects/chromium/try.star b/infra/config/subprojects/chromium/try.star
index 150a5ed..9b622e6 100644
--- a/infra/config/subprojects/chromium/try.star
+++ b/infra/config/subprojects/chromium/try.star
@@ -391,9 +391,8 @@
 try_.chromium_android_builder(
     name = "android-marshmallow-arm64-rel-dual-coverage",
     builderless = True,
-    cores = branches.value(for_main = 32, for_branches = 16),
+    cores = 16,
     goma_jobs = goma.jobs.J300,
-    main_list_view = "try",
     ssd = True,
     use_java_coverage = True,
     tryjob = try_.job(experiment_percentage = 3),
@@ -1234,11 +1233,11 @@
     os = os.LINUX_XENIAL_OR_BIONIC,
 )
 
+# Experimental builder to check dual coverage on linux platform.
 try_.chromium_linux_builder(
     name = "linux-rel-dual-coverage",
     builderless = True,
     goma_jobs = goma.jobs.J150,
-    main_list_view = "try",
     tryjob = try_.job(experiment_percentage = 3),
     coverage_test_types = ["unit", "overall"],
     use_clang_coverage = True,
diff --git a/infra/config/subprojects/reclient/OWNERS b/infra/config/subprojects/reclient/OWNERS
new file mode 100644
index 0000000..29a7a4c
--- /dev/null
+++ b/infra/config/subprojects/reclient/OWNERS
@@ -0,0 +1,5 @@
+abdelaal@google.com
+foox@google.com
+kousikk@google.com
+msavigny@google.com
+rubensf@google.com
\ No newline at end of file
diff --git a/infra/config/subprojects/reclient/README.md b/infra/config/subprojects/reclient/README.md
new file mode 100644
index 0000000..202039bc
--- /dev/null
+++ b/infra/config/subprojects/reclient/README.md
@@ -0,0 +1,4 @@
+Contains definitions for LUCI entities that test new releases of the reclient
+binaries used by chromium builders.
+
+* reclient.star - Builders that test staging and test versions of reclient.
\ No newline at end of file
diff --git a/infra/config/subprojects/reclient/reclient.star b/infra/config/subprojects/reclient/reclient.star
new file mode 100644
index 0000000..953104a
--- /dev/null
+++ b/infra/config/subprojects/reclient/reclient.star
@@ -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.
+
+load("//lib/builders.star", "cpu", "os")
+load("//lib/ci.star", "ci")
+load("//lib/consoles.star", "consoles")
+load("//console-header.star", "HEADER")
+
+luci.bucket(
+    name = "reclient",
+    acls = [
+        acl.entry(
+            roles = acl.BUILDBUCKET_READER,
+            groups = "all",
+        ),
+        acl.entry(
+            roles = acl.BUILDBUCKET_TRIGGERER,
+            groups = "project-chromium-ci-schedulers",
+        ),
+        acl.entry(
+            roles = acl.BUILDBUCKET_OWNER,
+            groups = "google/luci-task-force@google.com",
+        ),
+    ],
+)
+
+ci.defaults.set(
+    bucket = "reclient",
+    build_numbers = True,
+    builder_group = "chromium.reclient.fyi",
+    configure_kitchen = True,
+    cores = 8,
+    cpu = cpu.X86_64,
+    executable = "recipe:chromium",
+    execution_timeout = 3 * time.hour,
+    goma_backend = None,
+    kitchen_emulate_gce = True,
+    os = os.LINUX_DEFAULT,
+    pool = "luci.chromium.ci",
+    service_account = (
+        "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
+    ),
+    swarming_tags = ["vpython:native-python-wrapper"],
+    triggered_by = ["chromium-gitiles-trigger"],
+)
+
+consoles.console_view(
+    name = "chromium.reclient.fyi",
+    header = HEADER,
+    include_experimental_builds = True,
+    repo = "https://chromium.googlesource.com/chromium/src",
+)
+
+def fyi_reclient_staging_builder(
+        *,
+        name,
+        reclient_instance = "rbe-chromium-trusted",
+        **kwargs):
+    return ci.builder(
+        name = name,
+        reclient_instance = reclient_instance,
+        console_view_entry = consoles.console_view_entry(
+            category = "rbe|linux",
+            short_name = "rcs",
+        ),
+        **kwargs
+    )
+
+fyi_reclient_staging_builder(
+    name = "Linux Builder Re-Client Staging",
+)
diff --git a/infra/config/subprojects/reclient/subproject.star b/infra/config/subprojects/reclient/subproject.star
new file mode 100644
index 0000000..9c50211
--- /dev/null
+++ b/infra/config/subprojects/reclient/subproject.star
@@ -0,0 +1,5 @@
+# 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.
+
+exec("./reclient.star")
diff --git a/ios/chrome/app/BUILD.gn b/ios/chrome/app/BUILD.gn
index 1e0954eb..fae2dba0 100644
--- a/ios/chrome/app/BUILD.gn
+++ b/ios/chrome/app/BUILD.gn
@@ -315,7 +315,6 @@
     "//ios/public/provider/chrome/browser/mailto",
     "//ios/public/provider/chrome/browser/signin",
     "//ios/public/provider/chrome/browser/user_feedback",
-    "//ios/testing/perf:startup",
     "//ios/web/common:features",
     "//ios/web/public/webui",
     "//net",
@@ -367,7 +366,6 @@
     "//ios/chrome/app/startup:startup_basic",
     "//ios/chrome/browser/crash_report",
     "//ios/chrome/common",
-    "//ios/testing/perf:startup",
   ]
   if (ios_enable_sandbox_dump) {
     deps += [ "//ios/chrome/app/startup:sandbox_dump" ]
diff --git a/ios/chrome/app/application_delegate/app_state.mm b/ios/chrome/app/application_delegate/app_state.mm
index 224205e..01785a4 100644
--- a/ios/chrome/app/application_delegate/app_state.mm
+++ b/ios/chrome/app/application_delegate/app_state.mm
@@ -36,7 +36,6 @@
 #include "ios/chrome/browser/crash_report/features.h"
 #import "ios/chrome/browser/device_sharing/device_sharing_manager.h"
 #include "ios/chrome/browser/feature_engagement/tracker_factory.h"
-#import "ios/chrome/browser/geolocation/omnibox_geolocation_config.h"
 #import "ios/chrome/browser/main/browser.h"
 #import "ios/chrome/browser/signin/authentication_service.h"
 #import "ios/chrome/browser/signin/authentication_service_factory.h"
diff --git a/ios/chrome/app/application_delegate/app_state_unittest.mm b/ios/chrome/app/application_delegate/app_state_unittest.mm
index e858a2a..8902958e 100644
--- a/ios/chrome/app/application_delegate/app_state_unittest.mm
+++ b/ios/chrome/app/application_delegate/app_state_unittest.mm
@@ -27,7 +27,6 @@
 #include "ios/chrome/browser/browser_state/test_chrome_browser_state.h"
 #include "ios/chrome/browser/chrome_url_constants.h"
 #import "ios/chrome/browser/device_sharing/device_sharing_manager.h"
-#import "ios/chrome/browser/geolocation/omnibox_geolocation_config.h"
 #import "ios/chrome/browser/main/browser.h"
 #import "ios/chrome/browser/main/test_browser.h"
 #include "ios/chrome/browser/ntp_snippets/ios_chrome_content_suggestions_service_factory.h"
diff --git a/ios/chrome/app/chrome_exe_main.mm b/ios/chrome/app/chrome_exe_main.mm
index 32086e8..85445e4 100644
--- a/ios/chrome/app/chrome_exe_main.mm
+++ b/ios/chrome/app/chrome_exe_main.mm
@@ -10,7 +10,6 @@
 #include "ios/chrome/app/startup/ios_chrome_main.h"
 #include "ios/chrome/app/startup/ios_enable_sandbox_dump_buildflags.h"
 #include "ios/chrome/browser/crash_report/crash_helper.h"
-#include "ios/testing/perf/startupLoggers.h"
 
 #if BUILDFLAG(IOS_ENABLE_SANDBOX_DUMP)
 #include "ios/chrome/app/startup/sandbox_dump.h"  // nogncheck
@@ -71,7 +70,6 @@
 
 int main(int argc, char* argv[]) {
   IOSChromeMain::InitStartTime();
-  startup_loggers::RegisterAppStartTime();
 
 #if BUILDFLAG(IOS_ENABLE_SANDBOX_DUMP)
   // Dumps the sandbox if needed. This must be called as soon as possible,
diff --git a/ios/chrome/app/main_application_delegate.mm b/ios/chrome/app/main_application_delegate.mm
index b0e266ec..9809c12 100644
--- a/ios/chrome/app/main_application_delegate.mm
+++ b/ios/chrome/app/main_application_delegate.mm
@@ -26,7 +26,6 @@
 #import "ios/chrome/browser/ui/main/scene_state.h"
 #include "ios/public/provider/chrome/browser/chrome_browser_provider.h"
 #include "ios/public/provider/chrome/browser/signin/chrome_identity_service.h"
-#import "ios/testing/perf/startupLoggers.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
 #error "This file requires ARC support."
@@ -122,8 +121,6 @@
     didFinishLaunchingWithOptions:(NSDictionary*)launchOptions {
   self.didFinishLaunching = YES;
 
-  startup_loggers::RegisterAppDidFinishLaunchingTime();
-
   _mainController.window = self.window;
 
   BOOL inBackground =
@@ -169,7 +166,6 @@
     self.sceneState.activationLevel = SceneActivationLevelForegroundActive;
   }
 
-  startup_loggers::RegisterAppDidBecomeActiveTime();
   if (_appState.initStage <= InitStageSafeMode)
     return;
 
diff --git a/ios/chrome/app/strings/ios_chromium_strings.grd b/ios/chrome/app/strings/ios_chromium_strings.grd
index 12c49935..27df413 100644
--- a/ios/chrome/app/strings/ios_chromium_strings.grd
+++ b/ios/chrome/app/strings/ios_chromium_strings.grd
@@ -259,10 +259,10 @@
       <message name="IDS_IOS_GOOGLE_SERVICES_SETTINGS_SYNC_CHROME_DATA" desc="Turns off and on all the sync data (like bookmarks, history and others). [iOS only]">
         Sync Your Chromium Data
       </message>
-      <message name="IDS_IOS_INCOGNITO_REAUTH_SET_UP_SYSTEM_DIALOG_REASON" desc="Text for system biometric authentication dialog to explain why authentication is required when the user attempts to enable the setting to protect incognito tabs with biometric auth. [iOS only]">
+      <message name="IDS_IOS_INCOGNITO_REAUTH_SET_UP_SYSTEM_DIALOG_REASON" desc="Text for system biometric authentication dialog to explain why authentication is required when the user attempts to enable the setting to protect Incognito tabs with biometric auth. [iOS only]">
        Let Chromium lock your Incognito tabs.
       </message>
-      <message name="IDS_IOS_INCOGNITO_REAUTH_SETTING_NAME" desc="Name of the Setting to protect incognito tabs with biometric authentication, e.g. Face ID or Touch ID. [iOS only]">
+      <message name="IDS_IOS_INCOGNITO_REAUTH_SETTING_NAME" desc="Name of the Setting to protect Incognito tabs with biometric authentication, e.g. Face ID or Touch ID. [iOS only]">
        Lock Incognito Tabs When You Close Chromium
       </message>
       <message name="IDS_IOS_LOCATION_MODAL_DESCRIPTION" desc="The description of the location permissions modal [Length: unlimited] [iOS only].">
diff --git a/ios/chrome/app/strings/ios_google_chrome_strings.grd b/ios/chrome/app/strings/ios_google_chrome_strings.grd
index a06c84d8..a64887d 100644
--- a/ios/chrome/app/strings/ios_google_chrome_strings.grd
+++ b/ios/chrome/app/strings/ios_google_chrome_strings.grd
@@ -259,10 +259,10 @@
       <message name="IDS_IOS_GOOGLE_SERVICES_SETTINGS_SYNC_CHROME_DATA" desc="Turns off and on all the sync data (like bookmarks, history and others). [iOS only]">
         Sync Your Chrome Data
       </message>
-      <message name="IDS_IOS_INCOGNITO_REAUTH_SET_UP_SYSTEM_DIALOG_REASON" desc="Text for system biometric authentication dialog to explain why authentication is required when the user attempts to enable the setting to protect incognito tabs with biometric auth. [iOS only]">
+      <message name="IDS_IOS_INCOGNITO_REAUTH_SET_UP_SYSTEM_DIALOG_REASON" desc="Text for system biometric authentication dialog to explain why authentication is required when the user attempts to enable the setting to protect Incognito tabs with biometric auth. [iOS only]">
        Let Chrome lock your Incognito tabs.
       </message>
-      <message name="IDS_IOS_INCOGNITO_REAUTH_SETTING_NAME" desc="Name of the Setting to protect incognito tabs with biometric authentication, e.g. Face ID or Touch ID. [iOS only]">
+      <message name="IDS_IOS_INCOGNITO_REAUTH_SETTING_NAME" desc="Name of the Setting to protect Incognito tabs with biometric authentication, e.g. Face ID or Touch ID. [iOS only]">
        Lock Incognito Tabs When You Close Chrome
       </message>
       <message name="IDS_IOS_LOCATION_MODAL_DESCRIPTION" desc="The description of the location permissions modal [Length: unlimited] [iOS only].">
diff --git a/ios/chrome/app/strings/ios_strings.grd b/ios/chrome/app/strings/ios_strings.grd
index ecfa303..92edee7 100644
--- a/ios/chrome/app/strings/ios_strings.grd
+++ b/ios/chrome/app/strings/ios_strings.grd
@@ -363,9 +363,15 @@
       <message name="IDS_IOS_AUTOFILL_SET_UP_SCREENLOCK_CONTENT" desc="Message informing the user that in order to use the password, a screen lock needs to be set up on the device. This is shown as an alert message after the user tries to view or copy the password from a settings page. [CHAR_LIMIT=100]">
         To use passwords, you must first set a passcode on your device.
       </message>
-      <message name="IDS_IOS_CONSISTENCY_PROMO_SKIP" desc="Dismisses the sign-in promo on the web">
+      <message name="IDS_IOS_CONSISTENCY_PROMO_ADD_ACCOUNT" desc="Button to add an account to the device.">
+        Add account to device
+      </message>
+      <message name="IDS_IOS_CONSISTENCY_PROMO_CHOOSE_ACCOUNT" desc="Presents the list of account on the device and invites the user to choose an account to sign-in.">
+        Choose an account
+      </message>
+      <message name="IDS_IOS_CONSISTENCY_PROMO_SKIP" desc="Dismisses the sign-in promo on the web.">
         Skip
-        </message>
+      </message>
       <message name="IDS_IOS_COPY_LINK_ACTION_TITLE" desc="Title of the action used to copy a link to the pasteboard. [iOS only]">
         Copy Link
       </message>
@@ -637,7 +643,7 @@
       <message name="IDS_IOS_CONTENT_CONTEXT_OPENIMAGENEWTAB" desc="The name of the Open Image in New Tab command in the content area context menu">
         Open Image in New Tab
       </message>
-      <message name="IDS_IOS_CONTENT_CONTEXT_OPENLINKNEWINCOGNITOTAB" desc="The iOS menu item for opening a link in a new incognito tab. Shorter than the desktop version [Length: 25em] [iOS only]">
+      <message name="IDS_IOS_CONTENT_CONTEXT_OPENLINKNEWINCOGNITOTAB" desc="The iOS menu item for opening a link in a new Incognito tab. Shorter than the desktop version [Length: 25em] [iOS only]">
         Open in New Incognito Tab
       </message>
       <message name="IDS_IOS_CONTENT_CONTEXT_OPENLINKNEWTAB" desc="The iOS menu item for opening a link in another tab. Shorter than the desktop version [iOS only]">
@@ -972,19 +978,19 @@
       <message name="IDS_IOS_ICON_SEARCH" desc="Accessibility label for the search icon [iOS only].">
         Search
       </message>
-      <message name="IDS_IOS_INCOGNITO_REAUTH_SYSTEM_DIALOG_REASON" desc="Text for system biometric authentication dialog to explain why authentication is required when the user attempts to authenticate to access incognito tabs. [iOS only]">
+      <message name="IDS_IOS_INCOGNITO_REAUTH_SYSTEM_DIALOG_REASON" desc="Text for system biometric authentication dialog to explain why authentication is required when the user attempts to authenticate to access Incognito tabs. [iOS only]">
         Use <ph name="BIOMETRIC_AUTHENITCATION_TYPE">$1<ex>Face ID</ex></ph> to unlock your Incognito tabs.
       </message>
-      <message name="IDS_IOS_INCOGNITO_REAUTH_GO_TO_NORMAL_TABS" desc="Button to go to non-incognito tabs on a screen that blocks incognito tabs until biometric authetication. [iOS only]">
+      <message name="IDS_IOS_INCOGNITO_REAUTH_GO_TO_NORMAL_TABS" desc="Button to go to non-Incognito tabs on a screen that blocks Incognito tabs until biometric authetication. [iOS only]">
        See Other Tabs
       </message>
-      <message name="IDS_IOS_INCOGNITO_REAUTH_UNLOCK_BUTTON" desc="Button to initiate biometric authentication on a screen that blocks incognito tabs until biometric authetication. [iOS only]">
+      <message name="IDS_IOS_INCOGNITO_REAUTH_UNLOCK_BUTTON" desc="Button to initiate biometric authentication on a screen that blocks Incognito tabs until biometric authetication. [iOS only]">
        Unlock With <ph name="BIOMETRIC_AUTHENITCATION_TYPE">$1<ex>Face ID</ex></ph>
       </message>
-      <message name="IDS_IOS_INCOGNITO_REAUTH_UNLOCK_BUTTON_VOICEOVER_LABEL" desc="Voiceover label on the button that starts biometric authentication on a screen that blocks incognito tabs until biometric authetication. [iOS only]">
+      <message name="IDS_IOS_INCOGNITO_REAUTH_UNLOCK_BUTTON_VOICEOVER_LABEL" desc="Voiceover label on the button that starts biometric authentication on a screen that blocks Incognito tabs until biometric authetication. [iOS only]">
        Unlock Incognito Tabs With <ph name="BIOMETRIC_AUTHENITCATION_TYPE">$1<ex>Face ID</ex></ph>
       </message>
-      <message name="IDS_IOS_INCOGNITO_REAUTH_SET_UP_PASSCODE_HINT" desc="Explanatory text for the setting to protect incognito tabs with biometric authentication, e.g. Face ID or Touch ID. Appears when the user has no Face ID, Touch ID, or passcode set up, and therefore the feature cannot be enabled. [iOS only]">
+      <message name="IDS_IOS_INCOGNITO_REAUTH_SET_UP_PASSCODE_HINT" desc="Explanatory text for the setting to protect Incognito tabs with biometric authentication, e.g. Face ID or Touch ID. Appears when the user has no Face ID, Touch ID, or passcode set up, and therefore the feature cannot be enabled. [iOS only]">
         To lock your Incognito tabs, set up Touch ID, Face ID, or a Passcode.
       </message>
       <message name="IDS_IOS_INCOGNITO_REAUTH_PASSCODE" desc="The passcode (pin code or password) used instead of biometric authentication, e.g. Face ID or Touch ID, for device authentication. [iOS only]">
@@ -1213,10 +1219,10 @@
       <message name="IDS_IOS_NET_EXPORT_NO_EMAIL_ACCOUNTS_ALERT_TITLE" desc="The title of the alert informing a user with no email accounts that they need to configure an email account to send net-export data. [Length: 20em]">
         No Email Accounts
       </message>
-      <message name="IDS_IOS_NEW_INCOGNITO_TAB_IPH_PROMOTION_TEXT" desc="Text for the New Incognito Tab Tip in-product help promotion, explaining that incognito tabs can be used to browse privately. [iOS only]" meaning="The user has never created an incognito tab before. A bubble is displayed pointing to the tools menu button and informing users about how to use incognito mode.">
-        To browse privately, open an incognito tab
+      <message name="IDS_IOS_NEW_INCOGNITO_TAB_IPH_PROMOTION_TEXT" desc="Text for the New Incognito Tab Tip in-product help promotion, explaining that Incognito tabs can be used to browse privately. [iOS only]" meaning="The user has never created an Incognito tab before. A bubble is displayed pointing to the tools menu button and informing users about how to use Incognito mode.">
+        To browse privately, open an Incognito tab
       </message>
-      <message name="IDS_IOS_NEW_INCOGNITO_TAB_PAGE" desc="Label for text saying the user is on the new incognito tab page">
+      <message name="IDS_IOS_NEW_INCOGNITO_TAB_PAGE" desc="Label for text saying the user is on the new Incognito tab page">
         New Incognito Tab Page
       </message>
       <message name="IDS_IOS_NEW_TAB_IPH_PROMOTION_TEXT" desc="Text for the New Tab Tip in-product help promotion, explaining that new tabs can be added and switched between. [iOS only]" meaning="The user has never created a new tab, and a bubble is displayed pointing to the tab switcher to inform users how to create tabs and switch between them.">
@@ -1380,12 +1386,12 @@
       <message name="IDS_IOS_OPTIONS_PRIVACY_COOKIES_BLOCK_THIRD_PARTY_COOKIES_DETAIL" desc="Option detail to block third party cookies">
         Features on some sites may break
       </message>
-      <message name="IDS_IOS_OPTIONS_PRIVACY_COOKIES_BLOCK_THIRD_PARTY_COOKIES_INCOGNITO_DESCRIPTION" desc="Option description to block third party cookies in incognito">
+      <message name="IDS_IOS_OPTIONS_PRIVACY_COOKIES_BLOCK_THIRD_PARTY_COOKIES_INCOGNITO_DESCRIPTION" desc="Option description to block third party cookies in Incognito">
         Sites can use cookies to improve your browsing experience, for example, to keep you signed in or to remember items in your shopping cart.
 
-While in incognito, sites can't use cookies to see your browsing activity across different sites, for example, to personalize ads.
+While in Incognito, sites can't use cookies to see your browsing activity across different sites, for example, to personalize ads.
       </message>
-      <message name="IDS_IOS_OPTIONS_PRIVACY_COOKIES_BLOCK_THIRD_PARTY_COOKIES_INCOGNITO_TITLE" desc="Option title to block third party cookies in incognito">
+      <message name="IDS_IOS_OPTIONS_PRIVACY_COOKIES_BLOCK_THIRD_PARTY_COOKIES_INCOGNITO_TITLE" desc="Option title to block third party cookies in Incognito">
         Block Third-Party Cookies in Incognito
       </message>
       <message name="IDS_IOS_OPTIONS_PRIVACY_COOKIES_BLOCK_THIRD_PARTY_COOKIES_TITLE" desc="Option title to block third party cookies">
@@ -2426,7 +2432,7 @@
       <message name="IDS_IOS_TAB_GRID_INCOGNITO_TABS_EMPTY_MESSAGE" desc="Message shown in the Incognito Tabs page when the grid is empty, explaining that the user can open a new tab.">
         To browse the web privately, add a new tab
       </message>
-      <message name="IDS_IOS_TAB_GRID_INCOGNITO_TABS_EMPTY_TITLE" desc="Title shown above a message on the incognito tab switcher when there are no incognito tabs. [iOS only]">
+      <message name="IDS_IOS_TAB_GRID_INCOGNITO_TABS_EMPTY_TITLE" desc="Title shown above a message on the Incognito tab switcher when there are no Incognito tabs. [iOS only]">
         You'll find your Incognito tabs here
       </message>
       <message name="IDS_IOS_TAB_GRID_INCOGNITO_TABS_UNAVAILABLE_MESSAGE" desc="Message shown in the Incognito Tabs page of the tab grid when the IncognitoModeAvailability enterprise policy is set to disabled. [iOS only]">
@@ -2465,10 +2471,10 @@
       <message name="IDS_IOS_TAB_GRID_REGULAR_TABS_UNAVAILABLE_TITLE" desc="Title shown above a message in the Regular Tabs page of the tab grid when the IncognitoModeAvailability enterprise policy is set to forced. [iOS only]">
         Only Incognito Mode is Available
       </message>
-      <message name="IDS_IOS_TAB_GRID_CREATE_NEW_INCOGNITO_TAB" desc="The accessibility label for the button that creates new incognito tabs. [iOS only]">
-        Create new incognito tab.
+      <message name="IDS_IOS_TAB_GRID_CREATE_NEW_INCOGNITO_TAB" desc="The accessibility label for the button that creates new Incognito tabs. [iOS only]">
+        Create new Incognito tab.
       </message>
-      <message name="IDS_IOS_TAB_GRID_CREATE_NEW_TAB" desc="The accessibility label for the button that creates new regular tabs (as opposed to incognito tabs). [iOS only]">
+      <message name="IDS_IOS_TAB_GRID_CREATE_NEW_TAB" desc="The accessibility label for the button that creates new regular tabs (as opposed to Incognito tabs). [iOS only]">
         Create new tab.
       </message>
       <message name="IDS_IOS_TAB_STRIP_ENTER_TAB_SWITCHER" desc="The accessibility label of the tab strip button to enter the tab switcher. The tab switcher is a view showing the opened tabs, and allows users to switch, close, and open new tabs. [iOS only]">
@@ -2480,10 +2486,10 @@
       <message name="IDS_IOS_TAB_SWITCHER_CLOSE_TAB" desc="The accessibility label for the closing of a selected tab. [iOS only]">
         Close tab.
       </message>
-      <message name="IDS_IOS_TAB_SWITCHER_CREATE_NEW_INCOGNITO_TAB" desc="The accessibility label for the button that creates new incognito tabs. [iOS only]">
-        Create new incognito tab.
+      <message name="IDS_IOS_TAB_SWITCHER_CREATE_NEW_INCOGNITO_TAB" desc="The accessibility label for the button that creates new Incognito tabs. [iOS only]">
+        Create new Incognito tab.
       </message>
-      <message name="IDS_IOS_TAB_SWITCHER_CREATE_NEW_TAB" desc="The accessibility label for the button that creates new regular tabs (as opposed to incognito tabs). [iOS only]">
+      <message name="IDS_IOS_TAB_SWITCHER_CREATE_NEW_TAB" desc="The accessibility label for the button that creates new regular tabs (as opposed to Incognito tabs). [iOS only]">
         Create new tab.
       </message>
       <message name="IDS_IOS_TAB_SWITCHER_ENABLE_SYNC_TITLE" desc="Title label to enable sync for user. [Length: 30em] [iOS only]">
@@ -2498,10 +2504,10 @@
       <message name="IDS_IOS_TAB_SWITCHER_HEADER_OTHER_DEVICES_TABS" desc="Label shown to select the Other Devices tabs panel, as opposed to the Incognito and Non Incognito panels. [Length: 17em] [iOS only]">
         Other Devices
       </message>
-      <message name="IDS_IOS_TAB_SWITCHER_NO_LOCAL_INCOGNITO_TABS" desc="Label explaining that the user can open a new incognito tab. [iOS only]">
-        Open an incognito tab to browse the web privately.
+      <message name="IDS_IOS_TAB_SWITCHER_NO_LOCAL_INCOGNITO_TABS" desc="Label explaining that the user can open a new Incognito tab. [iOS only]">
+        Open an Incognito tab to browse the web privately.
       </message>
-      <message name="IDS_IOS_TAB_SWITCHER_NO_LOCAL_INCOGNITO_TABS_PROMO" desc="Title explaining that there are no opened incognito tabs. [iOS only]">
+      <message name="IDS_IOS_TAB_SWITCHER_NO_LOCAL_INCOGNITO_TABS_PROMO" desc="Title explaining that there are no opened Incognito tabs. [iOS only]">
         No Incognito Tabs
       </message>
       <message name="IDS_IOS_TAB_SWITCHER_NO_LOCAL_NON_INCOGNITO_TABS" desc="Label explaining that the user can open a new tab. [iOS only]">
@@ -2561,7 +2567,7 @@
       <message name="IDS_IOS_TOOLS_MENU_BOOKMARKS" desc="The iOS menu item for opening the bookmarks manager [Length: 15em] [iOS only]">
         Bookmarks
       </message>
-      <message name="IDS_IOS_TOOLS_MENU_CLOSE_ALL_INCOGNITO_TABS" desc="The iOS menu item for closing all incognito tabs [iOS only]" meaning="[Length: unlimited]">
+      <message name="IDS_IOS_TOOLS_MENU_CLOSE_ALL_INCOGNITO_TABS" desc="The iOS menu item for closing all Incognito tabs [iOS only]" meaning="[Length: unlimited]">
         Close All Incognito Tabs
       </message>
       <message name="IDS_IOS_TOOLS_MENU_CLOSE_ALL_TABS" desc="The iOS menu item for closing all tabs [iOS only]" meaning="[Length: unlimited]">
@@ -2588,10 +2594,10 @@
       <message name="IDS_IOS_TOOLS_MENU_HISTORY" desc="The Tools menu item for opening the navigation history menu">
         History
       </message>
-<message name="IDS_IOS_TOOLS_MENU_NEW_INCOGNITO_SEARCH" desc="The iOS menu item for opening a new incognito tab and doing a new search in that tab." meaning="Do a new search in a new incognito tab">
+<message name="IDS_IOS_TOOLS_MENU_NEW_INCOGNITO_SEARCH" desc="The iOS menu item for opening a new Incognito tab and doing a new search in that tab." meaning="Do a new search in a new Incognito tab">
 Incognito Search
 </message>
-      <message name="IDS_IOS_TOOLS_MENU_NEW_INCOGNITO_TAB" desc="The iOS menu item for opening a new incognito tab [iOS only]" meaning="Tools menu entry to create a new Incognito tab. [Length: Unlimited]">
+      <message name="IDS_IOS_TOOLS_MENU_NEW_INCOGNITO_TAB" desc="The iOS menu item for opening a new Incognito tab [iOS only]" meaning="Tools menu entry to create a new Incognito tab. [Length: Unlimited]">
         New Incognito Tab
       </message>
 <message name="IDS_IOS_TOOLS_MENU_NEW_SEARCH" desc="The iOS menu item for opening a new tab and doing a new search in that tab." meaning="Do a new search in a new tab">
@@ -2810,7 +2816,7 @@
       <message name="IDS_IOS_BOOKMARK_CONTEXT_MENU_OPEN" desc="Text on the bookmarks context menu to open all selected bookmarks">
         Open All
       </message>
-      <message name="IDS_IOS_BOOKMARK_CONTEXT_MENU_OPEN_INCOGNITO" desc="Text on the bookmarks context menu to open selected bookmark in incognito">
+      <message name="IDS_IOS_BOOKMARK_CONTEXT_MENU_OPEN_INCOGNITO" desc="Text on the bookmarks context menu to open selected bookmark in Incognito">
         Open in Incognito
       </message>
       <message name="IDS_IOS_BOOKMARK_CONTEXT_MENU_MOVE" desc="Text on the bookmarks context menu to move selected bookmark">
diff --git a/ios/chrome/app/strings/ios_strings_grd/IDS_IOS_CONSISTENCY_PROMO_ADD_ACCOUNT.png.sha1 b/ios/chrome/app/strings/ios_strings_grd/IDS_IOS_CONSISTENCY_PROMO_ADD_ACCOUNT.png.sha1
new file mode 100644
index 0000000..4415beae
--- /dev/null
+++ b/ios/chrome/app/strings/ios_strings_grd/IDS_IOS_CONSISTENCY_PROMO_ADD_ACCOUNT.png.sha1
@@ -0,0 +1 @@
+bc9b4d98934afbe5872572f273e736a261fd9ad9
\ No newline at end of file
diff --git a/ios/chrome/app/strings/ios_strings_grd/IDS_IOS_CONSISTENCY_PROMO_CHOOSE_ACCOUNT.png.sha1 b/ios/chrome/app/strings/ios_strings_grd/IDS_IOS_CONSISTENCY_PROMO_CHOOSE_ACCOUNT.png.sha1
new file mode 100644
index 0000000..4415beae
--- /dev/null
+++ b/ios/chrome/app/strings/ios_strings_grd/IDS_IOS_CONSISTENCY_PROMO_CHOOSE_ACCOUNT.png.sha1
@@ -0,0 +1 @@
+bc9b4d98934afbe5872572f273e736a261fd9ad9
\ No newline at end of file
diff --git a/ios/chrome/browser/chrome_switches.cc b/ios/chrome/browser/chrome_switches.cc
index d0e3a68..a43cbb9 100644
--- a/ios/chrome/browser/chrome_switches.cc
+++ b/ios/chrome/browser/chrome_switches.cc
@@ -39,6 +39,9 @@
 const char kEnableThirdPartyKeyboardWorkaround[] =
     "enable-third-party-keyboard-workaround";
 
+// Enabled the NTP Discover feed.
+const char kEnableDiscoverFeed[] = "enable-discover-feed";
+
 // Installs the URLBlocklist and URLAllowlist handlers.
 const char kInstallURLBlocklistHandlers[] = "install-url-blocklist-handlers";
 
diff --git a/ios/chrome/browser/chrome_switches.h b/ios/chrome/browser/chrome_switches.h
index 6978c33d..8649287 100644
--- a/ios/chrome/browser/chrome_switches.h
+++ b/ios/chrome/browser/chrome_switches.h
@@ -17,6 +17,7 @@
 extern const char kEnableIOSHandoffToOtherDevices[];
 extern const char kEnableSpotlightActions[];
 extern const char kEnableThirdPartyKeyboardWorkaround[];
+extern const char kEnableDiscoverFeed[];
 extern const char kInstallURLBlocklistHandlers[];
 
 extern const char kUserAgent[];
diff --git a/ios/chrome/browser/geolocation/BUILD.gn b/ios/chrome/browser/geolocation/BUILD.gn
index 52bc5d9f..d16f974b 100644
--- a/ios/chrome/browser/geolocation/BUILD.gn
+++ b/ios/chrome/browser/geolocation/BUILD.gn
@@ -5,12 +5,8 @@
 source_set("geolocation") {
   configs += [ "//build/config/compiler:enable_arc" ]
   sources = [
-    "omnibox_geolocation_config.h",
-    "omnibox_geolocation_config.mm",
     "omnibox_geolocation_controller.h",
     "omnibox_geolocation_controller.mm",
-    "omnibox_geolocation_tab_helper.h",
-    "omnibox_geolocation_tab_helper.mm",
   ]
   deps = [
     "//base",
diff --git a/ios/chrome/browser/geolocation/omnibox_geolocation_config.h b/ios/chrome/browser/geolocation/omnibox_geolocation_config.h
deleted file mode 100644
index 7c381d9..0000000
--- a/ios/chrome/browser/geolocation/omnibox_geolocation_config.h
+++ /dev/null
@@ -1,24 +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 IOS_CHROME_BROWSER_GEOLOCATION_OMNIBOX_GEOLOCATION_CONFIG_H_
-#define IOS_CHROME_BROWSER_GEOLOCATION_OMNIBOX_GEOLOCATION_CONFIG_H_
-
-#import <Foundation/Foundation.h>
-
-class GURL;
-
-// Implements configuration for using geolocation for Omnibox queries.
-@interface OmniboxGeolocationConfig : NSObject
-
-// Returns singleton object for this class.
-+ (OmniboxGeolocationConfig*)sharedInstance;
-
-// Returns YES if and only if |url| has a domain that is allowed for
-// geolocation use in Omnibox queries.
-- (BOOL)URLHasEligibleDomain:(const GURL&)url;
-
-@end
-
-#endif  // IOS_CHROME_BROWSER_GEOLOCATION_OMNIBOX_GEOLOCATION_CONFIG_H_
diff --git a/ios/chrome/browser/geolocation/omnibox_geolocation_config.mm b/ios/chrome/browser/geolocation/omnibox_geolocation_config.mm
deleted file mode 100644
index e28dfec..0000000
--- a/ios/chrome/browser/geolocation/omnibox_geolocation_config.mm
+++ /dev/null
@@ -1,91 +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 "ios/chrome/browser/geolocation/omnibox_geolocation_config.h"
-
-#include <set>
-#include <string>
-
-#include "base/check.h"
-#include "base/mac/foundation_util.h"
-#include "base/strings/sys_string_conversions.h"
-#include "url/gurl.h"
-
-#if !defined(__has_feature) || !__has_feature(objc_arc)
-#error "This file requires ARC support."
-#endif
-
-namespace {
-
-NSString* const kEligibleDomainsKey = @"EligibleDomains";
-
-}  // namespace
-
-@interface OmniboxGeolocationConfig () {
-  // List of domains eligible for Omnibox geolocation.
-  std::set<std::string> _eligibleDomains;
-}
-
-// Loads |_eligibleDomains| from config file |plistFileName|.
-- (void)loadConfigFile:(NSString*)plistFileName;
-
-@end
-
-@implementation OmniboxGeolocationConfig
-
-+ (OmniboxGeolocationConfig*)sharedInstance {
-  static OmniboxGeolocationConfig* instance =
-      [[OmniboxGeolocationConfig alloc] init];
-  return instance;
-}
-
-- (instancetype)init {
-  self = [super init];
-  if (self) {
-    [self loadConfigFile:@"OmniboxGeolocation"];
-  }
-  return self;
-}
-
-- (BOOL)URLHasEligibleDomain:(const GURL&)url {
-  // Here we iterate through the eligible domains and check url.DomainIs() for
-  // each domain rather than extracting url.host() and checking the domain for
-  // membership in |eligibleDomains_|, because GURL::DomainIs() is more robust
-  // and contains logic that we want to reuse.
-  for (const auto& eligibleDomain : _eligibleDomains) {
-    if (url.DomainIs(eligibleDomain.c_str()))
-      return YES;
-  }
-  return NO;
-}
-
-#pragma mark - Private
-
-- (void)loadConfigFile:(NSString*)plistFileName {
-  NSString* path = [[NSBundle mainBundle] pathForResource:plistFileName
-                                                   ofType:@"plist"
-                                              inDirectory:@"gm-config/ANY"];
-  NSDictionary* configData = [NSDictionary dictionaryWithContentsOfFile:path];
-  if (!configData) {
-    // The plist is not packaged with Chromium builds.  This is not an error, so
-    // simply return early, since no domains are eligible for geolocation.
-    return;
-  }
-
-  NSArray* eligibleDomains = base::mac::ObjCCastStrict<NSArray>(
-      [configData objectForKey:kEligibleDomainsKey]);
-  if (eligibleDomains) {
-    for (id item in eligibleDomains) {
-      NSString* domain = base::mac::ObjCCastStrict<NSString>(item);
-      if ([domain length]) {
-        _eligibleDomains.insert(
-            base::SysNSStringToUTF8([domain lowercaseString]));
-      }
-    }
-  }
-  // Make sure that if a plist exists, it contains at least one eligible domain.
-  DCHECK(!_eligibleDomains.empty());
-}
-
-@end
diff --git a/ios/chrome/browser/geolocation/omnibox_geolocation_controller.h b/ios/chrome/browser/geolocation/omnibox_geolocation_controller.h
index ae2a0a5..a3751e4 100644
--- a/ios/chrome/browser/geolocation/omnibox_geolocation_controller.h
+++ b/ios/chrome/browser/geolocation/omnibox_geolocation_controller.h
@@ -27,12 +27,6 @@
 // authorization is not yet determined.
 - (void)triggerSystemPrompt;
 
-// Notifies the receiver that the browser finished loading the page for
-// |webState|. |loadSuccess| whether the web state loaded successfully.
-// |webState| can't be null.
-- (void)finishPageLoadForWebState:(web::WebState*)webState
-                      loadSuccess:(BOOL)loadSuccess;
-
 // Marks the user as new without triggering the iOS system prompt to authorize
 // the use of location
 - (void)systemPromptSkippedForNewUser;
diff --git a/ios/chrome/browser/geolocation/omnibox_geolocation_controller.mm b/ios/chrome/browser/geolocation/omnibox_geolocation_controller.mm
index bd1103d0..9bc29a6 100644
--- a/ios/chrome/browser/geolocation/omnibox_geolocation_controller.mm
+++ b/ios/chrome/browser/geolocation/omnibox_geolocation_controller.mm
@@ -10,7 +10,6 @@
 #include "components/google/core/common/google_util.h"
 #import "ios/chrome/app/tests_hook.h"
 #include "ios/chrome/browser/browser_state/chrome_browser_state.h"
-#import "ios/chrome/browser/geolocation/omnibox_geolocation_config.h"
 #include "ios/web/public/navigation/navigation_item.h"
 #import "ios/web/public/navigation/navigation_manager.h"
 #import "ios/web/public/web_state.h"
@@ -46,29 +45,19 @@
 
 }  // anonymous namespace
 
-@interface OmniboxGeolocationController () <CLLocationManagerDelegate> {
-  CLLocationManager* _locationManager;
-
-  // Records whether we have deliberately presented the system prompt, so that
-  // we can record the user's action in
-  // locationManagerDidChangeAuthorization:.
-  BOOL _systemPrompt;
-
-  // Records whether we are prompting for a new user, so that we can record the
-  // user's action to the right histogram (either
-  // kGeolocationAuthorizationActionExistingUser or
-  // kGeolocationAuthorizationActionNewUser).
-  BOOL _newUser;
-}
+@interface OmniboxGeolocationController () <CLLocationManagerDelegate>
 
 @property(nonatomic, strong) CLLocationManager* locationManager;
 
-// Returns YES if and only if |url| specifies a page for which we will prompt
-// the user to authorize the use of geolocation for Omnibox queries.
-- (BOOL)URLIsAuthorizationPromptingURL:(const GURL&)url;
+// Records whether we are prompting for a new user, so that we can record the
+// user's action to the right histogram (either
+// kGeolocationAuthorizationActionExistingUser or
+// kGeolocationAuthorizationActionNewUser).
+@property(nonatomic, assign) BOOL newUser;
 
-// Records |authorizationAction|.
-- (void)recordAuthorizationAction:(AuthorizationAction)authorizationAction;
+// Whether the permission was undefined or not. Used to choose whether to log
+// the permission or not.
+@property(nonatomic, assign) BOOL permissionWasUndefined;
 
 @end
 
@@ -85,6 +74,8 @@
   if (self) {
     _locationManager = [[CLLocationManager alloc] init];
     [_locationManager setDelegate:self];
+    _permissionWasUndefined = CLLocationManager.authorizationStatus ==
+                              kCLAuthorizationStatusNotDetermined;
   }
   return self;
 }
@@ -92,42 +83,23 @@
 - (void)triggerSystemPrompt {
   if (self.locationServicesEnabled && CLLocationManager.authorizationStatus ==
                                           kCLAuthorizationStatusNotDetermined) {
-    _systemPrompt = YES;
+    self.newUser = YES;
 
     // Turn on location updates, so that iOS will prompt the user.
-    [self requestPermission];
-    _newUser = YES;
+    [self.locationManager requestWhenInUseAuthorization];
   }
 }
 
-- (void)finishPageLoadForWebState:(web::WebState*)webState
-                      loadSuccess:(BOOL)loadSuccess {
-  // Don't ask for permission on page loads.
-}
-
 - (void)systemPromptSkippedForNewUser {
-  _newUser = YES;
+  self.newUser = YES;
 }
 
 #pragma mark - Private
 
-- (BOOL)URLIsAuthorizationPromptingURL:(const GURL&)url {
-  // Per PRD: "Show a modal dialog upon reaching google.com or a search results
-  // page..." However, we only want to do this for domains where we will send
-  // location.
-  return (google_util::IsGoogleHomePageUrl(url) ||
-          google_util::IsGoogleSearchUrl(url)) &&
-         [[OmniboxGeolocationConfig sharedInstance] URLHasEligibleDomain:url];
-}
-
-// Requests the authorization to use location.
-- (void)requestPermission {
-  [self.locationManager requestWhenInUseAuthorization];
-}
-
 - (void)recordAuthorizationAction:(AuthorizationAction)authorizationAction {
-  if (_newUser) {
-    _newUser = NO;
+  self.permissionWasUndefined = NO;
+  if (self.newUser) {
+    self.newUser = NO;
 
     UMA_HISTOGRAM_ENUMERATION(kGeolocationAuthorizationActionNewUser,
                               authorizationAction, kAuthorizationActionCount);
@@ -148,7 +120,7 @@
 
 - (void)locationManagerDidChangeAuthorization:
     (CLLocationManager*)locationManager {
-  if (_systemPrompt) {
+  if (self.permissionWasUndefined) {
     switch (CLLocationManager.authorizationStatus) {
       case kCLAuthorizationStatusNotDetermined:
         // We may get a spurious notification about a transition to
@@ -159,15 +131,11 @@
 
       case kCLAuthorizationStatusRestricted:
       case kCLAuthorizationStatusDenied:
-        _systemPrompt = NO;
-
         [self recordAuthorizationAction:kAuthorizationActionPermanentlyDenied];
         break;
 
       case kCLAuthorizationStatusAuthorizedAlways:
       case kCLAuthorizationStatusAuthorizedWhenInUse:
-        _systemPrompt = NO;
-
         [self recordAuthorizationAction:kAuthorizationActionAuthorized];
         break;
     }
diff --git a/ios/chrome/browser/geolocation/omnibox_geolocation_tab_helper.h b/ios/chrome/browser/geolocation/omnibox_geolocation_tab_helper.h
deleted file mode 100644
index 6d84a1c..0000000
--- a/ios/chrome/browser/geolocation/omnibox_geolocation_tab_helper.h
+++ /dev/null
@@ -1,45 +0,0 @@
-// Copyright 2020 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef IOS_CHROME_BROWSER_GEOLOCATION_OMNIBOX_GEOLOCATION_TAB_HELPER_H_
-#define IOS_CHROME_BROWSER_GEOLOCATION_OMNIBOX_GEOLOCATION_TAB_HELPER_H_
-
-#include "ios/web/public/web_state_observer.h"
-#import "ios/web/public/web_state_user_data.h"
-
-#if !defined(__has_feature) || !__has_feature(objc_arc)
-#error "This file requires ARC support."
-#endif
-
-// A tab helper which handles the current device location for omnibox search
-// queries.
-class OmniboxGeolocationTabHelper
-    : public web::WebStateObserver,
-      public web::WebStateUserData<OmniboxGeolocationTabHelper> {
- public:
-  // Not copyable or moveable
-  OmniboxGeolocationTabHelper(const OmniboxGeolocationTabHelper&) = delete;
-  OmniboxGeolocationTabHelper& operator=(const OmniboxGeolocationTabHelper&) =
-      delete;
-  ~OmniboxGeolocationTabHelper() override;
-
- private:
-  friend class web::WebStateUserData<OmniboxGeolocationTabHelper>;
-
-  explicit OmniboxGeolocationTabHelper(web::WebState* web_state);
-
-  // web::WebStateObserver
-  void DidStartNavigation(web::WebState* web_state,
-                          web::NavigationContext* navigation_context) override;
-  void PageLoaded(
-      web::WebState* web_state,
-      web::PageLoadCompletionStatus load_completion_status) override;
-  void WebStateDestroyed(web::WebState* web_state) override;
-
-  web::WebState* web_state_ = nullptr;
-
-  WEB_STATE_USER_DATA_KEY_DECL();
-};
-
-#endif  // IOS_CHROME_BROWSER_GEOLOCATION_OMNIBOX_GEOLOCATION_TAB_HELPER_H_
diff --git a/ios/chrome/browser/geolocation/omnibox_geolocation_tab_helper.mm b/ios/chrome/browser/geolocation/omnibox_geolocation_tab_helper.mm
deleted file mode 100644
index 59923c527..0000000
--- a/ios/chrome/browser/geolocation/omnibox_geolocation_tab_helper.mm
+++ /dev/null
@@ -1,63 +0,0 @@
-// Copyright 2020 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#import "ios/chrome/browser/geolocation/omnibox_geolocation_tab_helper.h"
-
-#import "ios/chrome/browser/browser_state/chrome_browser_state.h"
-#import "ios/chrome/browser/geolocation/omnibox_geolocation_controller.h"
-#import "ios/web/public/navigation/navigation_context.h"
-#import "ios/web/public/navigation/navigation_item.h"
-#import "ios/web/public/navigation/navigation_manager.h"
-
-#if !defined(__has_feature) || !__has_feature(objc_arc)
-#error "This file requires ARC support."
-#endif
-
-OmniboxGeolocationTabHelper::OmniboxGeolocationTabHelper(
-    web::WebState* web_state)
-    : web_state_(web_state) {
-  web_state_->AddObserver(this);
-}
-
-OmniboxGeolocationTabHelper::~OmniboxGeolocationTabHelper() {
-  if (web_state_) {
-    web_state_->RemoveObserver(this);
-    web_state_ = nullptr;
-  }
-}
-
-void OmniboxGeolocationTabHelper::DidStartNavigation(
-    web::WebState* web_state,
-    web::NavigationContext* navigation_context) {
-  DCHECK(web_state->GetNavigationManager());
-  web::NavigationItem* navigation_item =
-      web_state->GetNavigationManager()->GetPendingItem();
-
-  if (!navigation_item) {
-    // Pending item may not exist due to the bug in //ios/web layer.
-    // TODO(crbug.com/899827): remove this early return once GetPendingItem()
-    // always return valid object inside WebStateObserver::DidStartNavigation()
-    // callback.
-    //
-    // Note that GetLastCommittedItem() returns null if navigation manager does
-    // not have committed items (which is normal situation).
-    return;
-  }
-}
-
-void OmniboxGeolocationTabHelper::PageLoaded(
-    web::WebState* web_state,
-    web::PageLoadCompletionStatus load_completion_status) {
-  [[OmniboxGeolocationController sharedInstance]
-      finishPageLoadForWebState:web_state
-                    loadSuccess:(load_completion_status ==
-                                 web::PageLoadCompletionStatus::SUCCESS)];
-}
-
-void OmniboxGeolocationTabHelper::WebStateDestroyed(web::WebState* web_state) {
-  web_state->RemoveObserver(this);
-  web_state_ = nullptr;
-}
-
-WEB_STATE_USER_DATA_KEY_IMPL(OmniboxGeolocationTabHelper)
diff --git a/ios/chrome/browser/ntp/new_tab_page_tab_helper.h b/ios/chrome/browser/ntp/new_tab_page_tab_helper.h
index 14690869..5bafb376 100644
--- a/ios/chrome/browser/ntp/new_tab_page_tab_helper.h
+++ b/ios/chrome/browser/ntp/new_tab_page_tab_helper.h
@@ -85,7 +85,7 @@
   web::WebState* web_state_ = nullptr;
 
   // |YES| if the current tab helper is active.
-  BOOL active_;
+  BOOL active_ = NO;
 
   // |YES| if the NTP's underlying ios/web page is still loading.
   BOOL ignore_load_requests_ = NO;
diff --git a/ios/chrome/browser/prerender/preload_controller.mm b/ios/chrome/browser/prerender/preload_controller.mm
index 8d41505..e6dd50a 100644
--- a/ios/chrome/browser/prerender/preload_controller.mm
+++ b/ios/chrome/browser/prerender/preload_controller.mm
@@ -447,12 +447,6 @@
   HistoryTabHelper::FromWebState(webState.get())
       ->SetDelayHistoryServiceNotification(false);
 
-  if (!webState->IsLoading()) {
-    [[OmniboxGeolocationController sharedInstance]
-        finishPageLoadForWebState:webState.get()
-                      loadSuccess:YES];
-  }
-
   return webState;
 }
 
diff --git a/ios/chrome/browser/tabs/tab_helper_util.mm b/ios/chrome/browser/tabs/tab_helper_util.mm
index 7c53553..b7f4562 100644
--- a/ios/chrome/browser/tabs/tab_helper_util.mm
+++ b/ios/chrome/browser/tabs/tab_helper_util.mm
@@ -30,7 +30,6 @@
 #import "ios/chrome/browser/download/ar_quick_look_tab_helper.h"
 #include "ios/chrome/browser/favicon/favicon_service_factory.h"
 #import "ios/chrome/browser/find_in_page/find_tab_helper.h"
-#import "ios/chrome/browser/geolocation/omnibox_geolocation_tab_helper.h"
 #include "ios/chrome/browser/history/history_service_factory.h"
 #include "ios/chrome/browser/history/history_tab_helper.h"
 #include "ios/chrome/browser/history/top_sites_factory.h"
@@ -207,8 +206,6 @@
     InfobarBadgeTabHelper::CreateForWebState(web_state);
   }
 
-  OmniboxGeolocationTabHelper::CreateForWebState(web_state);
-
   if (base::FeatureList::IsEnabled(kSharedHighlightingIOS)) {
     LinkToTextTabHelper::CreateForWebState(web_state);
   }
diff --git a/ios/chrome/browser/ui/authentication/signin/consistency_promo_signin/BUILD.gn b/ios/chrome/browser/ui/authentication/signin/consistency_promo_signin/BUILD.gn
index 208763f..479e6d7b 100644
--- a/ios/chrome/browser/ui/authentication/signin/consistency_promo_signin/BUILD.gn
+++ b/ios/chrome/browser/ui/authentication/signin/consistency_promo_signin/BUILD.gn
@@ -18,6 +18,7 @@
     "//ios/chrome/browser/ui/authentication",
     "//ios/chrome/browser/ui/authentication/signin:signin_protected",
     "//ios/chrome/browser/ui/authentication/signin/consistency_promo_signin/bottom_sheet",
+    "//ios/chrome/browser/ui/authentication/signin/consistency_promo_signin/consistency_account_chooser",
     "//ios/chrome/browser/ui/authentication/signin/consistency_promo_signin/consistency_default_account",
     "//ios/chrome/browser/ui/authentication/signin/consistency_promo_signin/consistency_signin_error",
     "//ios/chrome/browser/ui/image_util",
diff --git a/ios/chrome/browser/ui/authentication/signin/consistency_promo_signin/consistency_account_chooser/BUILD.gn b/ios/chrome/browser/ui/authentication/signin/consistency_promo_signin/consistency_account_chooser/BUILD.gn
new file mode 100644
index 0000000..b743858
--- /dev/null
+++ b/ios/chrome/browser/ui/authentication/signin/consistency_promo_signin/consistency_account_chooser/BUILD.gn
@@ -0,0 +1,36 @@
+# Copyright 2020 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import("//build/config/chrome_build.gni")
+
+source_set("consistency_account_chooser") {
+  configs += [ "//build/config/compiler:enable_arc" ]
+  sources = [
+    "consistency_account_chooser_consumer.h",
+    "consistency_account_chooser_coordinator.h",
+    "consistency_account_chooser_coordinator.mm",
+    "consistency_account_chooser_mediator.h",
+    "consistency_account_chooser_mediator.mm",
+    "consistency_account_chooser_view_controller.h",
+    "consistency_account_chooser_view_controller.mm",
+    "identity_item_configurator.h",
+    "identity_item_configurator.mm",
+  ]
+  deps = [
+    "//ios/chrome/app/strings",
+    "//ios/chrome/browser",
+    "//ios/chrome/browser/signin",
+    "//ios/chrome/browser/ui:feature_flags",
+    "//ios/chrome/browser/ui/authentication",
+    "//ios/chrome/browser/ui/authentication/cells",
+    "//ios/chrome/browser/ui/authentication/signin/consistency_promo_signin/bottom_sheet",
+    "//ios/chrome/browser/ui/coordinators:chrome_coordinators",
+    "//ios/chrome/browser/ui/table_view",
+    "//ios/chrome/browser/ui/table_view:utils",
+    "//ios/chrome/common/ui/colors",
+    "//ios/public/provider/chrome/browser",
+    "//ios/public/provider/chrome/browser/signin",
+    "//ui/base",
+  ]
+}
diff --git a/ios/chrome/browser/ui/authentication/signin/consistency_promo_signin/consistency_account_chooser/consistency_account_chooser_consumer.h b/ios/chrome/browser/ui/authentication/signin/consistency_promo_signin/consistency_account_chooser/consistency_account_chooser_consumer.h
new file mode 100644
index 0000000..3cf629b
--- /dev/null
+++ b/ios/chrome/browser/ui/authentication/signin/consistency_promo_signin/consistency_account_chooser/consistency_account_chooser_consumer.h
@@ -0,0 +1,23 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef IOS_CHROME_BROWSER_UI_AUTHENTICATION_SIGNIN_CONSISTENCY_PROMO_SIGNIN_CONSISTENCY_ACCOUNT_CHOOSER_CONSISTENCY_ACCOUNT_CHOOSER_CONSUMER_H_
+#define IOS_CHROME_BROWSER_UI_AUTHENTICATION_SIGNIN_CONSISTENCY_PROMO_SIGNIN_CONSISTENCY_ACCOUNT_CHOOSER_CONSISTENCY_ACCOUNT_CHOOSER_CONSUMER_H_
+
+#import <Foundation/Foundation.h>
+
+@class IdentityItemConfigurator;
+
+// Consumer for consistency default account.
+@protocol ConsistencyAccountChooserConsumer <NSObject>
+
+// Invoked when all identities have to be reloaded.
+- (void)reloadAllIdentities;
+// Invoked when an identity has to be updated.
+- (void)reloadIdentityForIdentityItemConfigurator:
+    (IdentityItemConfigurator*)configurator;
+
+@end
+
+#endif  // IOS_CHROME_BROWSER_UI_AUTHENTICATION_SIGNIN_CONSISTENCY_PROMO_SIGNIN_CONSISTENCY_ACCOUNT_CHOOSER_CONSISTENCY_ACCOUNT_CHOOSER_CONSUMER_H_
diff --git a/ios/chrome/browser/ui/authentication/signin/consistency_promo_signin/consistency_account_chooser/consistency_account_chooser_coordinator.h b/ios/chrome/browser/ui/authentication/signin/consistency_promo_signin/consistency_account_chooser/consistency_account_chooser_coordinator.h
new file mode 100644
index 0000000..bccb299
--- /dev/null
+++ b/ios/chrome/browser/ui/authentication/signin/consistency_promo_signin/consistency_account_chooser/consistency_account_chooser_coordinator.h
@@ -0,0 +1,38 @@
+// 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_AUTHENTICATION_SIGNIN_CONSISTENCY_PROMO_SIGNIN_CONSISTENCY_ACCOUNT_CHOOSER_CONSISTENCY_ACCOUNT_CHOOSER_COORDINATOR_H_
+#define IOS_CHROME_BROWSER_UI_AUTHENTICATION_SIGNIN_CONSISTENCY_PROMO_SIGNIN_CONSISTENCY_ACCOUNT_CHOOSER_CONSISTENCY_ACCOUNT_CHOOSER_COORDINATOR_H_
+
+#import "ios/chrome/browser/ui/coordinators/chrome_coordinator.h"
+
+@class ConsistencyAccountChooserCoordinator;
+@class ChromeIdentity;
+
+// Delegate for ConsistencyAccountChooserCoordinator.
+@protocol ConsistencyAccountChooserCoordinatorDelegate <NSObject>
+
+// Invoked when the user selected an identity.
+- (void)consistencyAccountChooserCoordinatorChromeIdentitySelected:
+    (ConsistencyAccountChooserCoordinator*)coordinator;
+
+@end
+
+// This coordinator presents an entry point to the Chrome sign-in flow with the
+// default account available on the device.
+@interface ConsistencyAccountChooserCoordinator : ChromeCoordinator
+
+// Identity selected by the user.
+@property(nonatomic, strong, readonly) ChromeIdentity* selectedIdentity;
+@property(nonatomic, strong, readonly) UIViewController* viewController;
+@property(nonatomic, weak) id<ConsistencyAccountChooserCoordinatorDelegate>
+    delegate;
+
+- (void)start NS_UNAVAILABLE;
+// Starts the coordinator with the selected identity.
+- (void)startWithSelectedIdentity:(ChromeIdentity*)selectedIdentity;
+
+@end
+
+#endif  // IOS_CHROME_BROWSER_UI_AUTHENTICATION_SIGNIN_CONSISTENCY_PROMO_SIGNIN_CONSISTENCY_ACCOUNT_CHOOSER_CONSISTENCY_ACCOUNT_CHOOSER_COORDINATOR_H_
diff --git a/ios/chrome/browser/ui/authentication/signin/consistency_promo_signin/consistency_account_chooser/consistency_account_chooser_coordinator.mm b/ios/chrome/browser/ui/authentication/signin/consistency_promo_signin/consistency_account_chooser/consistency_account_chooser_coordinator.mm
new file mode 100644
index 0000000..ff7d29e3
--- /dev/null
+++ b/ios/chrome/browser/ui/authentication/signin/consistency_promo_signin/consistency_account_chooser/consistency_account_chooser_coordinator.mm
@@ -0,0 +1,90 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#import "ios/chrome/browser/ui/authentication/signin/consistency_promo_signin/consistency_account_chooser/consistency_account_chooser_coordinator.h"
+
+#import "base/strings/sys_string_conversions.h"
+#import "ios/chrome/browser/ui/authentication/signin/consistency_promo_signin/consistency_account_chooser/consistency_account_chooser_mediator.h"
+#import "ios/chrome/browser/ui/authentication/signin/consistency_promo_signin/consistency_account_chooser/consistency_account_chooser_view_controller.h"
+#import "ios/chrome/browser/ui/authentication/signin/signin_coordinator.h"
+#import "ios/chrome/browser/ui/table_view/table_view_utils.h"
+#import "ios/chrome/browser/ui/ui_feature_flags.h"
+#import "ios/public/provider/chrome/browser/chrome_browser_provider.h"
+#import "ios/public/provider/chrome/browser/signin/chrome_identity.h"
+#import "ios/public/provider/chrome/browser/signin/chrome_identity_service.h"
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
+@interface ConsistencyAccountChooserCoordinator () <
+    ConsistencyAccountChooserViewControllerActionDelegate>
+
+@property(nonatomic, strong)
+    ConsistencyAccountChooserViewController* accountChooserViewController;
+
+@property(nonatomic, strong) ConsistencyAccountChooserMediator* mediator;
+@property(nonatomic, strong) SigninCoordinator* addAccountSigninCoordinator;
+
+@end
+
+@implementation ConsistencyAccountChooserCoordinator
+
+- (void)startWithSelectedIdentity:(ChromeIdentity*)selectedIdentity {
+  [super start];
+  self.mediator = [[ConsistencyAccountChooserMediator alloc]
+      initWithSelectedIdentity:selectedIdentity];
+  UITableViewStyle style = base::FeatureList::IsEnabled(kSettingsRefresh)
+                               ? ChromeTableViewStyle()
+                               : UITableViewStylePlain;
+  self.accountChooserViewController =
+      [[ConsistencyAccountChooserViewController alloc] initWithStyle:style];
+  self.accountChooserViewController.modelDelegate = self.mediator;
+  self.mediator.consumer = self.accountChooserViewController;
+  self.accountChooserViewController.actionDelegate = self;
+  [self.accountChooserViewController view];
+}
+
+#pragma mark - Properties
+
+- (UIViewController*)viewController {
+  return self.accountChooserViewController;
+}
+
+- (ChromeIdentity*)selectedIdentity {
+  return self.mediator.selectedIdentity;
+}
+
+#pragma mark - ConsistencyAccountChooserViewControllerPresentationDelegate
+
+- (void)consistencyAccountChooserViewController:
+            (ConsistencyAccountChooserViewController*)viewController
+                    didSelectIdentityWithGaiaID:(NSString*)gaiaID {
+  ios::ChromeIdentityService* identityService =
+      ios::GetChromeBrowserProvider()->GetChromeIdentityService();
+  ChromeIdentity* identity =
+      identityService->GetIdentityWithGaiaID(base::SysNSStringToUTF8(gaiaID));
+  DCHECK(identity);
+  self.mediator.selectedIdentity = identity;
+  [self.delegate
+      consistencyAccountChooserCoordinatorChromeIdentitySelected:self];
+}
+
+- (void)consistencyAccountChooserViewControllerDidTapOnAddAccount:
+    (ConsistencyAccountChooserViewController*)viewController {
+  self.addAccountSigninCoordinator = [SigninCoordinator
+      addAccountCoordinatorWithBaseViewController:self.viewController
+                                          browser:self.browser
+                                      accessPoint:signin_metrics::AccessPoint::
+                                                      ACCESS_POINT_WEB_SIGNIN];
+  __weak __typeof(self) weakSelf = self;
+  self.addAccountSigninCoordinator.signinCompletion =
+      ^(SigninCoordinatorResult result, SigninCompletionInfo* info) {
+        [weakSelf.addAccountSigninCoordinator stop];
+        weakSelf.addAccountSigninCoordinator = nil;
+      };
+  [self.addAccountSigninCoordinator start];
+}
+
+@end
diff --git a/ios/chrome/browser/ui/authentication/signin/consistency_promo_signin/consistency_account_chooser/consistency_account_chooser_mediator.h b/ios/chrome/browser/ui/authentication/signin/consistency_promo_signin/consistency_account_chooser/consistency_account_chooser_mediator.h
new file mode 100644
index 0000000..1ab1292
--- /dev/null
+++ b/ios/chrome/browser/ui/authentication/signin/consistency_promo_signin/consistency_account_chooser/consistency_account_chooser_mediator.h
@@ -0,0 +1,31 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef IOS_CHROME_BROWSER_UI_AUTHENTICATION_SIGNIN_CONSISTENCY_PROMO_SIGNIN_CONSISTENCY_ACCOUNT_CHOOSER_CONSISTENCY_ACCOUNT_CHOOSER_MEDIATOR_H_
+#define IOS_CHROME_BROWSER_UI_AUTHENTICATION_SIGNIN_CONSISTENCY_PROMO_SIGNIN_CONSISTENCY_ACCOUNT_CHOOSER_CONSISTENCY_ACCOUNT_CHOOSER_MEDIATOR_H_
+
+#import <Foundation/Foundation.h>
+
+#import "ios/chrome/browser/ui/authentication/signin/consistency_promo_signin/consistency_account_chooser/consistency_account_chooser_view_controller.h"
+
+@class ChromeIdentity;
+@protocol ConsistencyAccountChooserConsumer;
+@class ConsistencyAccountChooserMediator;
+
+// Mediator for ConsistencyAccountChooserCoordinator.
+@interface ConsistencyAccountChooserMediator
+    : NSObject <ConsistencyAccountChooserViewControllerModelDelegate>
+
+@property(nonatomic, strong) id<ConsistencyAccountChooserConsumer> consumer;
+@property(nonatomic, strong) ChromeIdentity* selectedIdentity;
+
+// See -[SigninPromoViewMediator initWithBrowserState:].
+- (instancetype)init NS_UNAVAILABLE;
+
+- (instancetype)initWithSelectedIdentity:(ChromeIdentity*)selectedIdentity
+    NS_DESIGNATED_INITIALIZER;
+
+@end
+
+#endif  // IOS_CHROME_BROWSER_UI_AUTHENTICATION_SIGNIN_CONSISTENCY_PROMO_SIGNIN_CONSISTENCY_ACCOUNT_CHOOSER_CONSISTENCY_ACCOUNT_CHOOSER_MEDIATOR_H_
diff --git a/ios/chrome/browser/ui/authentication/signin/consistency_promo_signin/consistency_account_chooser/consistency_account_chooser_mediator.mm b/ios/chrome/browser/ui/authentication/signin/consistency_promo_signin/consistency_account_chooser/consistency_account_chooser_mediator.mm
new file mode 100644
index 0000000..5bf87e5
--- /dev/null
+++ b/ios/chrome/browser/ui/authentication/signin/consistency_promo_signin/consistency_account_chooser/consistency_account_chooser_mediator.mm
@@ -0,0 +1,153 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#import "ios/chrome/browser/ui/authentication/signin/consistency_promo_signin/consistency_account_chooser/consistency_account_chooser_mediator.h"
+
+#import "ios/chrome/browser/chrome_browser_provider_observer_bridge.h"
+#import "ios/chrome/browser/signin/chrome_identity_service_observer_bridge.h"
+#import "ios/chrome/browser/ui/authentication/resized_avatar_cache.h"
+#import "ios/chrome/browser/ui/authentication/signin/consistency_promo_signin/consistency_account_chooser/identity_item_configurator.h"
+#import "ios/public/provider/chrome/browser/signin/chrome_identity.h"
+#import "ios/public/provider/chrome/browser/signin/chrome_identity_service.h"
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
+@interface ConsistencyAccountChooserMediator () <
+    ChromeBrowserProviderObserver,
+    ChromeIdentityServiceObserver,
+    ConsistencyAccountChooserViewControllerModelDelegate> {
+  std::unique_ptr<ChromeIdentityServiceObserverBridge> _identityServiceObserver;
+  std::unique_ptr<ChromeBrowserProviderObserverBridge> _browserProviderObserver;
+}
+
+@property(nonatomic, strong) ResizedAvatarCache* avatarCache;
+// Configurators based on ChromeIdentity list.
+@property(nonatomic, strong) NSArray* sortedIdentityItemConfigurators;
+@property(nonatomic, assign, readonly)
+    ios::ChromeIdentityService* chromeIdentityService;
+
+@end
+
+@implementation ConsistencyAccountChooserMediator
+
+- (instancetype)initWithSelectedIdentity:(ChromeIdentity*)selectedIdentity {
+  if (self = [super init]) {
+    _identityServiceObserver =
+        std::make_unique<ChromeIdentityServiceObserverBridge>(self);
+    _browserProviderObserver =
+        std::make_unique<ChromeBrowserProviderObserverBridge>(self);
+    _avatarCache = [[ResizedAvatarCache alloc] init];
+    _selectedIdentity = selectedIdentity;
+    [self loadIdentityItemConfigurators];
+  }
+  return self;
+}
+
+#pragma mark - Properties
+
+- (ios::ChromeIdentityService*)chromeIdentityService {
+  return ios::GetChromeBrowserProvider()->GetChromeIdentityService();
+}
+
+- (void)setSelectedIdentity:(ChromeIdentity*)identity {
+  DCHECK(identity);
+  if (_selectedIdentity == identity) {
+    return;
+  }
+  ChromeIdentity* previousSelectedIdentity = _selectedIdentity;
+  _selectedIdentity = identity;
+  [self profileUpdate:previousSelectedIdentity];
+  [self profileUpdate:_selectedIdentity];
+}
+
+#pragma mark - Private
+
+// Updates |self.sortedIdentityItemConfigurators| based on ChromeIdentity list.
+- (void)loadIdentityItemConfigurators {
+  NSMutableArray* configurators = [NSMutableArray array];
+  NSArray* identities =
+      self.chromeIdentityService->GetAllIdentitiesSortedForDisplay();
+  BOOL hasSelectedIdentity = NO;
+  for (ChromeIdentity* identity in identities) {
+    IdentityItemConfigurator* configurator =
+        [[IdentityItemConfigurator alloc] init];
+    [self updateIdentityItemConfigurator:configurator
+                      withChromeIdentity:identity];
+    [configurators addObject:configurator];
+    if (configurator.selected) {
+      hasSelectedIdentity = YES;
+    }
+    // If the configurator is selected, the identity must be equal to
+    // |self.selectedIdentity|.
+    DCHECK(!configurator.selected || [self.selectedIdentity isEqual:identity]);
+  }
+  if (!hasSelectedIdentity && identities.count > 0) {
+    // No selected identity has been found, a default need to be selected.
+    self.selectedIdentity = identities[0];
+    IdentityItemConfigurator* configurator = configurators[0];
+    configurator.selected = YES;
+  }
+  self.sortedIdentityItemConfigurators = configurators;
+}
+
+// Updates an IdentityItemConfigurator based on a ChromeIdentity.
+- (void)updateIdentityItemConfigurator:(IdentityItemConfigurator*)configurator
+                    withChromeIdentity:(ChromeIdentity*)identity {
+  configurator.gaiaID = identity.gaiaID;
+  configurator.name = identity.userGivenName;
+  configurator.email = identity.userEmail;
+  configurator.avatar = [self.avatarCache resizedAvatarForIdentity:identity];
+  configurator.selected = [identity isEqual:self.selectedIdentity];
+}
+
+#pragma mark - ChromeBrowserProviderObserver
+
+- (void)chromeIdentityServiceDidChange:(ios::ChromeIdentityService*)identity {
+  DCHECK(!_identityServiceObserver.get());
+  _identityServiceObserver =
+      std::make_unique<ChromeIdentityServiceObserverBridge>(self);
+}
+
+- (void)chromeBrowserProviderWillBeDestroyed {
+  _browserProviderObserver.reset();
+}
+
+#pragma mark - ChromeIdentityServiceObserver
+
+- (void)identityListChanged {
+  [self loadIdentityItemConfigurators];
+  [self.consumer reloadAllIdentities];
+}
+
+- (void)profileUpdate:(ChromeIdentity*)identity {
+  IdentityItemConfigurator* configurator = nil;
+  for (IdentityItemConfigurator* cursor in self
+           .sortedIdentityItemConfigurators) {
+    if ([cursor.gaiaID isEqual:identity.gaiaID]) {
+      configurator = cursor;
+    }
+  }
+  DCHECK(configurator);
+  [self updateIdentityItemConfigurator:configurator
+                    withChromeIdentity:identity];
+  [self.consumer reloadIdentityForIdentityItemConfigurator:configurator];
+}
+
+- (void)chromeIdentityServiceWillBeDestroyed {
+  _identityServiceObserver.reset();
+}
+
+#pragma mark - ConsistencyAccountChooserViewControllerModelDelegate
+
+- (NSArray*)sortedIdentityItemConfigurators {
+  if (!_sortedIdentityItemConfigurators) {
+    [self loadIdentityItemConfigurators];
+  }
+  DCHECK(_sortedIdentityItemConfigurators);
+  return _sortedIdentityItemConfigurators;
+}
+
+@end
diff --git a/ios/chrome/browser/ui/authentication/signin/consistency_promo_signin/consistency_account_chooser/consistency_account_chooser_view_controller.h b/ios/chrome/browser/ui/authentication/signin/consistency_promo_signin/consistency_account_chooser/consistency_account_chooser_view_controller.h
new file mode 100644
index 0000000..e18f762
--- /dev/null
+++ b/ios/chrome/browser/ui/authentication/signin/consistency_promo_signin/consistency_account_chooser/consistency_account_chooser_view_controller.h
@@ -0,0 +1,51 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef IOS_CHROME_BROWSER_UI_AUTHENTICATION_SIGNIN_CONSISTENCY_PROMO_SIGNIN_CONSISTENCY_ACCOUNT_CHOOSER_CONSISTENCY_ACCOUNT_CHOOSER_VIEW_CONTROLLER_H_
+#define IOS_CHROME_BROWSER_UI_AUTHENTICATION_SIGNIN_CONSISTENCY_PROMO_SIGNIN_CONSISTENCY_ACCOUNT_CHOOSER_CONSISTENCY_ACCOUNT_CHOOSER_VIEW_CONTROLLER_H_
+
+#import <UIKit/UIKit.h>
+
+#import "ios/chrome/browser/ui/authentication/signin/consistency_promo_signin/bottom_sheet/child_bottom_sheet_view_controller.h"
+#import "ios/chrome/browser/ui/authentication/signin/consistency_promo_signin/consistency_account_chooser/consistency_account_chooser_consumer.h"
+#import "ios/chrome/browser/ui/table_view/chrome_table_view_controller.h"
+
+@class ConsistencyAccountChooserViewController;
+
+// Delegate protocol for ConsistencyAccountChooserViewController.
+@protocol ConsistencyAccountChooserViewControllerActionDelegate <NSObject>
+
+// Invoked when the user selects an identity.
+- (void)consistencyAccountChooserViewController:
+            (ConsistencyAccountChooserViewController*)viewController
+                    didSelectIdentityWithGaiaID:(NSString*)gaiaID;
+// Invoked when the user taps on "Add account".
+- (void)consistencyAccountChooserViewControllerDidTapOnAddAccount:
+    (ConsistencyAccountChooserViewController*)viewController;
+
+@end
+
+// Protocol to get the model.
+@protocol ConsistencyAccountChooserViewControllerModelDelegate <NSObject>
+
+// Returns all the configurators to generate model items.
+@property(nonatomic, strong, readonly) NSArray* sortedIdentityItemConfigurators;
+
+@end
+
+// View controller for ConsistencyAccountChooserCoordinator.
+@interface ConsistencyAccountChooserViewController
+    : ChromeTableViewController <ChildBottomSheetViewController,
+                                 ConsistencyAccountChooserConsumer>
+
+@property(nonatomic, weak)
+    id<ConsistencyAccountChooserViewControllerActionDelegate>
+        actionDelegate;
+@property(nonatomic, weak)
+    id<ConsistencyAccountChooserViewControllerModelDelegate>
+        modelDelegate;
+
+@end
+
+#endif  // IOS_CHROME_BROWSER_UI_AUTHENTICATION_SIGNIN_CONSISTENCY_PROMO_SIGNIN_CONSISTENCY_ACCOUNT_CHOOSER_CONSISTENCY_ACCOUNT_CHOOSER_VIEW_CONTROLLER_H_
diff --git a/ios/chrome/browser/ui/authentication/signin/consistency_promo_signin/consistency_account_chooser/consistency_account_chooser_view_controller.mm b/ios/chrome/browser/ui/authentication/signin/consistency_promo_signin/consistency_account_chooser/consistency_account_chooser_view_controller.mm
new file mode 100644
index 0000000..9a1b90a
--- /dev/null
+++ b/ios/chrome/browser/ui/authentication/signin/consistency_promo_signin/consistency_account_chooser/consistency_account_chooser_view_controller.mm
@@ -0,0 +1,164 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#import "ios/chrome/browser/ui/authentication/signin/consistency_promo_signin/consistency_account_chooser/consistency_account_chooser_view_controller.h"
+
+#import "base/check.h"
+#import "base/mac/foundation_util.h"
+#import "ios/chrome/browser/ui/authentication/cells/table_view_identity_item.h"
+#import "ios/chrome/browser/ui/authentication/signin/consistency_promo_signin/consistency_account_chooser/identity_item_configurator.h"
+#import "ios/chrome/browser/ui/table_view/cells/table_view_image_item.h"
+#import "ios/chrome/common/ui/colors/semantic_color_names.h"
+#import "ios/chrome/grit/ios_strings.h"
+#import "ui/base/l10n/l10n_util.h"
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
+namespace {
+
+// List of sections.
+typedef NS_ENUM(NSInteger, SectionIdentifier) {
+  IdentitySectionIdentifier = kSectionIdentifierEnumZero,
+  AddAccountSectionIdentifier,
+};
+
+typedef NS_ENUM(NSInteger, ItemType) {
+  // IdentitySectionIdentifier section.
+  IdentityItemType = kItemTypeEnumZero,
+  // AddAccountSectionIdentifier section.
+  AddAccountItemType,
+};
+
+// Table view header/footer height.
+CGFloat kSectionHeaderHeight = 8.;
+CGFloat kSectionFooterHeight = 8.;
+
+}  // naemspace
+
+@interface ConsistencyAccountChooserViewController ()
+
+@end
+
+@implementation ConsistencyAccountChooserViewController
+
+#pragma mark - UITableViewController
+
+- (void)loadModel {
+  [super loadModel];
+  [self loadIdentitySection];
+  [self loadAddAccountSection];
+}
+
+- (void)tableView:(UITableView*)tableView
+    didSelectRowAtIndexPath:(NSIndexPath*)indexPath {
+  [self.tableView deselectRowAtIndexPath:indexPath animated:YES];
+  ListItem* item = [self.tableViewModel itemAtIndexPath:indexPath];
+  switch ((ItemType)item.type) {
+    case IdentityItemType: {
+      TableViewIdentityItem* identityItem =
+          base::mac::ObjCCastStrict<TableViewIdentityItem>(item);
+      DCHECK(identityItem);
+      [self.actionDelegate
+          consistencyAccountChooserViewController:self
+                      didSelectIdentityWithGaiaID:identityItem.gaiaID];
+      break;
+    }
+    case AddAccountItemType:
+      [self.actionDelegate
+          consistencyAccountChooserViewControllerDidTapOnAddAccount:self];
+      break;
+  }
+}
+
+- (CGFloat)tableView:(UITableView*)tableView
+    heightForHeaderInSection:(NSInteger)section {
+  return kSectionHeaderHeight;
+}
+
+- (CGFloat)tableView:(UITableView*)tableView
+    heightForFooterInSection:(NSInteger)section {
+  return kSectionFooterHeight;
+}
+
+#pragma mark - UIView
+
+- (void)viewDidLoad {
+  [super viewDidLoad];
+  self.title = l10n_util::GetNSString(IDS_IOS_CONSISTENCY_PROMO_CHOOSE_ACCOUNT);
+  [self loadModel];
+  [self.tableView reloadData];
+}
+
+#pragma mark - Private
+
+// Creates the identity section in the table view model.
+- (void)loadIdentitySection {
+  TableViewModel* model = self.tableViewModel;
+  [model addSectionWithIdentifier:IdentitySectionIdentifier];
+  [self loadIdentityItems];
+}
+
+// Creates all the identity items in the table view model.
+- (void)loadIdentityItems {
+  TableViewModel* model = self.tableViewModel;
+  for (IdentityItemConfigurator* configurator in self.modelDelegate
+           .sortedIdentityItemConfigurators) {
+    TableViewIdentityItem* item =
+        [[TableViewIdentityItem alloc] initWithType:IdentityItemType];
+    [configurator configureIdentityChooser:item];
+    [model addItem:item toSectionWithIdentifier:IdentitySectionIdentifier];
+  }
+}
+
+// Creates the add account section in the table view model.
+- (void)loadAddAccountSection {
+  TableViewModel* model = self.tableViewModel;
+  [model addSectionWithIdentifier:AddAccountSectionIdentifier];
+  TableViewImageItem* item =
+      [[TableViewImageItem alloc] initWithType:AddAccountItemType];
+  item.title = l10n_util::GetNSString(IDS_IOS_CONSISTENCY_PROMO_ADD_ACCOUNT);
+  item.textColor = [UIColor colorNamed:kBlueColor];
+  [model addItem:item toSectionWithIdentifier:AddAccountSectionIdentifier];
+}
+
+#pragma mark - ChildBottomSheetViewController
+
+- (CGFloat)layoutFittingHeightForWidth:(CGFloat)width {
+  NSArray* childViewControllers =
+      self.navigationController.childViewControllers;
+  DCHECK(childViewControllers.count > 0);
+  // Get the height of the first navigation view.
+  CGFloat firstViewHeight = [childViewControllers[0] view].bounds.size.height;
+  // Get the screen height.
+  CGFloat screenHeight =
+      self.navigationController.view.window.bounds.size.height;
+  return MAX(screenHeight / 2., firstViewHeight);
+}
+
+#pragma mark - ConsistencyAccountChooserConsumer
+
+- (void)reloadAllIdentities {
+  TableViewModel* model = self.tableViewModel;
+  [model deleteAllItemsFromSectionWithIdentifier:IdentitySectionIdentifier];
+  [self loadIdentityItems];
+  [self.tableView reloadData];
+}
+
+- (void)reloadIdentityForIdentityItemConfigurator:
+    (IdentityItemConfigurator*)configurator {
+  TableViewModel* model = self.tableViewModel;
+  NSArray* items =
+      [model itemsInSectionWithIdentifier:IdentitySectionIdentifier];
+  for (TableViewIdentityItem* item in items) {
+    if ([item.gaiaID isEqual:configurator.gaiaID]) {
+      [configurator configureIdentityChooser:item];
+      [self reconfigureCellsForItems:@[ item ]];
+      break;
+    }
+  }
+}
+
+@end
diff --git a/ios/chrome/browser/ui/authentication/signin/consistency_promo_signin/consistency_account_chooser/identity_item_configurator.h b/ios/chrome/browser/ui/authentication/signin/consistency_promo_signin/consistency_account_chooser/identity_item_configurator.h
new file mode 100644
index 0000000..f2a49c7
--- /dev/null
+++ b/ios/chrome/browser/ui/authentication/signin/consistency_promo_signin/consistency_account_chooser/identity_item_configurator.h
@@ -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.
+
+#ifndef IOS_CHROME_BROWSER_UI_AUTHENTICATION_SIGNIN_CONSISTENCY_PROMO_SIGNIN_CONSISTENCY_ACCOUNT_CHOOSER_IDENTITY_ITEM_CONFIGURATOR_H_
+#define IOS_CHROME_BROWSER_UI_AUTHENTICATION_SIGNIN_CONSISTENCY_PROMO_SIGNIN_CONSISTENCY_ACCOUNT_CHOOSER_IDENTITY_ITEM_CONFIGURATOR_H_
+
+#import <UIKit/UIKit.h>
+
+@class TableViewIdentityItem;
+
+// This class configures TableViewIdentityItem instances.
+@interface IdentityItemConfigurator : NSObject
+
+@property(nonatomic, strong) NSString* gaiaID;
+@property(nonatomic, strong) NSString* name;
+@property(nonatomic, strong) NSString* email;
+@property(nonatomic, strong) UIImage* avatar;
+@property(nonatomic, assign) BOOL selected;
+
+- (void)configureIdentityChooser:(TableViewIdentityItem*)item;
+
+@end
+
+#endif  // IOS_CHROME_BROWSER_UI_AUTHENTICATION_SIGNIN_CONSISTENCY_PROMO_SIGNIN_CONSISTENCY_ACCOUNT_CHOOSER_IDENTITY_ITEM_CONFIGURATOR_H_
diff --git a/ios/chrome/browser/ui/authentication/signin/consistency_promo_signin/consistency_account_chooser/identity_item_configurator.mm b/ios/chrome/browser/ui/authentication/signin/consistency_promo_signin/consistency_account_chooser/identity_item_configurator.mm
new file mode 100644
index 0000000..816b54c
--- /dev/null
+++ b/ios/chrome/browser/ui/authentication/signin/consistency_promo_signin/consistency_account_chooser/identity_item_configurator.mm
@@ -0,0 +1,29 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#import "ios/chrome/browser/ui/authentication/signin/consistency_promo_signin/consistency_account_chooser/identity_item_configurator.h"
+
+#import "ios/chrome/browser/ui/authentication/cells/table_view_identity_item.h"
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
+@implementation IdentityItemConfigurator
+
+- (void)configureIdentityChooser:(TableViewIdentityItem*)item {
+  item.gaiaID = self.gaiaID;
+  item.name = self.name;
+  item.email = self.email;
+  item.avatar = self.avatar;
+  item.selected = self.selected;
+}
+
+- (NSString*)description {
+  return [NSString stringWithFormat:@"<%@: %p, email: %@>",
+                                    NSStringFromClass(self.class), self,
+                                    self.email];
+}
+
+@end
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 68c38523..655da2f 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
@@ -16,6 +16,7 @@
 #import "ios/chrome/browser/ui/authentication/signin/consistency_promo_signin/bottom_sheet/bottom_sheet_navigation_controller.h"
 #import "ios/chrome/browser/ui/authentication/signin/consistency_promo_signin/bottom_sheet/bottom_sheet_presentation_controller.h"
 #import "ios/chrome/browser/ui/authentication/signin/consistency_promo_signin/bottom_sheet/bottom_sheet_slide_transition_animator.h"
+#import "ios/chrome/browser/ui/authentication/signin/consistency_promo_signin/consistency_account_chooser/consistency_account_chooser_coordinator.h"
 #import "ios/chrome/browser/ui/authentication/signin/consistency_promo_signin/consistency_default_account/consistency_default_account_coordinator.h"
 #import "ios/chrome/browser/ui/authentication/signin/consistency_promo_signin/consistency_signin_error/consistency_signin_error_coordinator.h"
 #import "ios/chrome/browser/ui/authentication/signin/signin_coordinator+protected.h"
@@ -27,6 +28,7 @@
 
 @interface ConsistencyPromoSigninCoordinator () <
     BottomSheetPresentationControllerPresentationDelegate,
+    ConsistencyAccountChooserCoordinatorDelegate,
     ConsistencyDefaultAccountCoordinatorDelegate,
     ConsistencySigninErrorCoordinatorDelegate,
     IdentityManagerObserverBridgeDelegate,
@@ -54,6 +56,9 @@
 // its consent level.
 @property(nonatomic, copy)
     signin_ui::CompletionCallback primaryAccountSetCompletion;
+// Coordinator to select another identity.
+@property(nonatomic, strong)
+    ConsistencyAccountChooserCoordinator* accountChooserCoordinator;
 
 @end
 
@@ -81,7 +86,7 @@
 - (void)start {
   [super start];
   self.defaultAccountCoordinator = [[ConsistencyDefaultAccountCoordinator alloc]
-      initWithBaseViewController:nil
+      initWithBaseViewController:self.navigationController
                          browser:self.browser];
   self.defaultAccountCoordinator.delegate = self;
   [self.defaultAccountCoordinator start];
@@ -116,13 +121,6 @@
 
 #pragma mark - Private
 
-// Creates the first view controller.
-- (UIViewController*)firstViewController {
-  // Needs implementation.
-  NOTIMPLEMENTED();
-  return nil;
-}
-
 // Dismisses the bottom sheet view controller.
 - (void)dismissNavigationViewController {
   __weak __typeof(self) weakSelf = self;
@@ -220,6 +218,16 @@
   self.signinErrorCoordinator = nil;
 }
 
+#pragma mark - ConsistencyAccountChooserCoordinatorDelegate
+
+- (void)consistencyAccountChooserCoordinatorChromeIdentitySelected:
+    (ConsistencyAccountChooserCoordinator*)coordinator {
+  self.defaultAccountCoordinator.selectedIdentity =
+      self.accountChooserCoordinator.selectedIdentity;
+  self.accountChooserCoordinator = nil;
+  [self.navigationController popViewControllerAnimated:YES];
+}
+
 #pragma mark - ConsistencyDefaultAccountCoordinatorDelegate
 
 - (void)consistencyDefaultAccountCoordinatorSkip:
@@ -229,7 +237,16 @@
 
 - (void)consistencyDefaultAccountCoordinatorOpenIdentityChooser:
     (ConsistencyDefaultAccountCoordinator*)coordinator {
-  NOTREACHED();
+  self.accountChooserCoordinator = [[ConsistencyAccountChooserCoordinator alloc]
+      initWithBaseViewController:self.navigationController
+                         browser:self.browser];
+  self.accountChooserCoordinator.delegate = self;
+  [self.accountChooserCoordinator
+      startWithSelectedIdentity:self.defaultAccountCoordinator
+                                    .selectedIdentity];
+  [self.navigationController
+      pushViewController:self.accountChooserCoordinator.viewController
+                animated:YES];
 }
 
 - (void)consistencyDefaultAccountCoordinatorSignin:
diff --git a/ios/chrome/browser/ui/content_suggestions/BUILD.gn b/ios/chrome/browser/ui/content_suggestions/BUILD.gn
index b41af2fa0..7cbb7e6 100644
--- a/ios/chrome/browser/ui/content_suggestions/BUILD.gn
+++ b/ios/chrome/browser/ui/content_suggestions/BUILD.gn
@@ -32,6 +32,7 @@
     "theme_change_delegate.h",
   ]
   deps = [
+    ":constants",
     ":feature_flags",
     ":metrics",
     "//base",
@@ -324,6 +325,8 @@
     "//components/strings",
     "//ios/chrome/app/strings",
     "//ios/chrome/browser:pref_names",
+    "//ios/chrome/browser:utils",
+    "//ios/chrome/browser/ui/ntp:constants",
     "//ios/chrome/browser/ui/settings:constants",
     "//ios/chrome/browser/ui/toolbar/public:constants",
     "//ios/chrome/test:eg_test_support+eg2",
@@ -345,7 +348,7 @@
   ]
   testonly = true
 
-  sources = [ "content_suggestions_app_interface.h" ]
+  sources = [ "new_tab_page_app_interface.h" ]
 
   deps = [ "//ios/third_party/earl_grey2:test_lib" ]
 }
@@ -359,8 +362,8 @@
   defines = [ "CHROME_EARL_GREY_2" ]
 
   sources = [
-    "content_suggestions_app_interface.h",
-    "content_suggestions_app_interface.mm",
+    "new_tab_page_app_interface.h",
+    "new_tab_page_app_interface.mm",
     "ntp_home_provider_test_singleton.h",
     "ntp_home_provider_test_singleton.mm",
     "ntp_home_test_utils.h",
@@ -382,6 +385,7 @@
     "//ios/chrome/browser/browser_state",
     "//ios/chrome/browser/ntp_snippets",
     "//ios/chrome/browser/search_engines",
+    "//ios/chrome/browser/ui/ntp:constants",
     "//ios/chrome/browser/ui/util",
     "//ios/chrome/test/app:test_support",
     "//ios/testing:block_swizzler",
diff --git a/ios/chrome/browser/ui/content_suggestions/content_suggestions_coordinator.h b/ios/chrome/browser/ui/content_suggestions/content_suggestions_coordinator.h
index c6fa5580..508c242 100644
--- a/ios/chrome/browser/ui/content_suggestions/content_suggestions_coordinator.h
+++ b/ios/chrome/browser/ui/content_suggestions/content_suggestions_coordinator.h
@@ -13,6 +13,7 @@
 
 @class BubblePresenter;
 @class ContentSuggestionsHeaderViewController;
+@class DiscoverFeedMetricsRecorder;
 @protocol NewTabPageCommands;
 @protocol NewTabPageControllerDelegate;
 @class NTPHomeMediator;
@@ -56,6 +57,16 @@
 // Bubble presenter for displaying IPH bubbles relating to the NTP.
 @property(nonatomic, strong) BubblePresenter* bubblePresenter;
 
+// Whether the refactored NTP and feed are enabled and visible.
+// TODO(crbug.com/1114792): Update this property to remove "refactored" when the
+// refactored NTP launches.
+@property(nonatomic, assign, getter=isRefactoredFeedVisible)
+    BOOL refactoredFeedVisible;
+
+// Metrics recorder for the Discover feed events related to ContentSuggestions.
+@property(nonatomic, strong)
+    DiscoverFeedMetricsRecorder* discoverFeedMetricsRecorder;
+
 // Dismisses all modals owned by the NTP mediator.
 - (void)dismissModals;
 
diff --git a/ios/chrome/browser/ui/content_suggestions/content_suggestions_coordinator.mm b/ios/chrome/browser/ui/content_suggestions/content_suggestions_coordinator.mm
index 7f0ad22a..c142e7c 100644
--- a/ios/chrome/browser/ui/content_suggestions/content_suggestions_coordinator.mm
+++ b/ios/chrome/browser/ui/content_suggestions/content_suggestions_coordinator.mm
@@ -22,8 +22,6 @@
 #import "components/search_engines/template_url_service.h"
 #include "ios/chrome/app/tests_hook.h"
 #include "ios/chrome/browser/browser_state/chrome_browser_state.h"
-#include "ios/chrome/browser/discover_feed/discover_feed_service.h"
-#include "ios/chrome/browser/discover_feed/discover_feed_service_factory.h"
 #import "ios/chrome/browser/drag_and_drop/url_drag_drop_handler.h"
 #include "ios/chrome/browser/favicon/ios_chrome_large_icon_cache_factory.h"
 #include "ios/chrome/browser/favicon/ios_chrome_large_icon_service_factory.h"
@@ -47,6 +45,7 @@
 #import "ios/chrome/browser/ui/commands/open_new_tab_command.h"
 #import "ios/chrome/browser/ui/content_suggestions/cells/content_suggestions_most_visited_item.h"
 #import "ios/chrome/browser/ui/content_suggestions/content_suggestions_action_handler.h"
+#import "ios/chrome/browser/ui/content_suggestions/content_suggestions_constants.h"
 #import "ios/chrome/browser/ui/content_suggestions/content_suggestions_data_sink.h"
 #import "ios/chrome/browser/ui/content_suggestions/content_suggestions_feature.h"
 #import "ios/chrome/browser/ui/content_suggestions/content_suggestions_header_commands.h"
@@ -70,6 +69,7 @@
 #import "ios/chrome/browser/ui/menu/action_factory.h"
 #import "ios/chrome/browser/ui/menu/menu_histograms.h"
 #import "ios/chrome/browser/ui/ntp/new_tab_page_commands.h"
+#import "ios/chrome/browser/ui/ntp/new_tab_page_constants.h"
 #import "ios/chrome/browser/ui/ntp/new_tab_page_feature.h"
 #import "ios/chrome/browser/ui/ntp/new_tab_page_header_constants.h"
 #import "ios/chrome/browser/ui/ntp/notification_promo_whats_new.h"
@@ -119,8 +119,6 @@
 @property(nonatomic, strong)
     ContentSuggestionsHeaderSynchronizer* headerCollectionInteractionHandler;
 @property(nonatomic, strong) ContentSuggestionsMetricsRecorder* metricsRecorder;
-@property(nonatomic, strong)
-    DiscoverFeedMetricsRecorder* discoverFeedMetricsRecorder;
 @property(nonatomic, strong) UIViewController* discoverFeedViewController;
 @property(nonatomic, strong) UIView* discoverFeedHeaderMenuButton;
 @property(nonatomic, strong) URLDragDropHandler* dragDropHandler;
@@ -223,14 +221,6 @@
       ReadingListModelFactory::GetForBrowserState(
           self.browser->GetBrowserState());
 
-  if (IsDiscoverFeedEnabled()) {
-    // Creating the DiscoverFeedService will start the DiscoverFeed.
-    DiscoverFeedService* discoverFeedService =
-        DiscoverFeedServiceFactory::GetForBrowserState(
-            self.browser->GetBrowserState());
-    self.discoverFeedMetricsRecorder =
-        discoverFeedService->GetDiscoverFeedMetricsRecorder();
-  }
   self.discoverFeedViewController = [self discoverFeed];
 
   TemplateURLService* templateURLService =
@@ -272,8 +262,7 @@
 
   // Offset to maintain Discover feed scroll position.
   CGFloat offset = 0;
-  if (IsDiscoverFeedEnabled() &&
-      (!IsRefactoredNTP() || ![self isDiscoverFeedVisible])) {
+  if (IsDiscoverFeedEnabled() && ![self isRefactoredFeedVisible]) {
     web::NavigationManager* navigationManager =
         self.webState->GetNavigationManager();
     web::NavigationItem* item = navigationManager->GetVisibleItem();
@@ -283,9 +272,9 @@
   }
 
   self.suggestionsViewController = [[ContentSuggestionsViewController alloc]
-      initWithStyle:CollectionViewControllerStyleDefault
-             offset:offset
-        feedVisible:[self isDiscoverFeedVisible]];
+              initWithStyle:CollectionViewControllerStyleDefault
+                     offset:offset
+      refactoredFeedVisible:[self isRefactoredFeedVisible]];
   [self.suggestionsViewController
       setDataSource:self.contentSuggestionsMediator];
   self.suggestionsViewController.suggestionCommandHandler = self.ntpMediator;
@@ -340,7 +329,15 @@
   // synchronizer instead.
   self.suggestionsViewController.headerProvider = self.headerController;
 
-  if (!IsRefactoredNTP() || ![self isDiscoverFeedVisible]) {
+  if ([self isRefactoredFeedVisible]) {
+    self.suggestionsViewController.collectionView.accessibilityIdentifier =
+        kContentSuggestionsCollectionIdentifier;
+  } else {
+    self.suggestionsViewController.collectionView.accessibilityIdentifier =
+        kNTPCollectionViewIdentifier;
+  }
+
+  if (![self isRefactoredFeedVisible]) {
     self.headerCollectionInteractionHandler =
         [[ContentSuggestionsHeaderSynchronizer alloc]
             initWithCollectionController:self.suggestionsViewController
@@ -526,7 +523,7 @@
                              IDS_IOS_DISCOVER_FEED_MENU_TURN_OFF_ITEM)
                   action:^{
                     [weakSelf setDiscoverFeedVisible:NO];
-                    if (IsRefactoredNTP()) {
+                    if ([weakSelf isRefactoredFeedVisible]) {
                       [weakSelf.ntpCommandHandler updateDiscoverFeedVisibility];
                     }
                   }
@@ -537,7 +534,7 @@
                              IDS_IOS_DISCOVER_FEED_MENU_TURN_ON_ITEM)
                   action:^{
                     [weakSelf setDiscoverFeedVisible:YES];
-                    if (IsRefactoredNTP()) {
+                    if ([weakSelf isRefactoredFeedVisible]) {
                       [weakSelf.ntpCommandHandler updateDiscoverFeedVisibility];
                     }
                   }
diff --git a/ios/chrome/browser/ui/content_suggestions/content_suggestions_egtest.mm b/ios/chrome/browser/ui/content_suggestions/content_suggestions_egtest.mm
index 3062250..7d13011 100644
--- a/ios/chrome/browser/ui/content_suggestions/content_suggestions_egtest.mm
+++ b/ios/chrome/browser/ui/content_suggestions/content_suggestions_egtest.mm
@@ -12,10 +12,11 @@
 #include "base/strings/utf_string_conversions.h"
 #import "base/test/ios/wait_util.h"
 #include "components/strings/grit/components_strings.h"
-#import "ios/chrome/browser/ui/content_suggestions/content_suggestions_app_interface.h"
 #import "ios/chrome/browser/ui/content_suggestions/content_suggestions_constants.h"
 #import "ios/chrome/browser/ui/content_suggestions/content_suggestions_feature.h"
+#import "ios/chrome/browser/ui/content_suggestions/new_tab_page_app_interface.h"
 #import "ios/chrome/browser/ui/content_suggestions/ntp_home_constant.h"
+#import "ios/chrome/browser/ui/ntp/new_tab_page_constants.h"
 #include "ios/chrome/grit/ios_strings.h"
 #import "ios/chrome/test/earl_grey/chrome_earl_grey.h"
 #import "ios/chrome/test/earl_grey/chrome_earl_grey_ui.h"
@@ -34,7 +35,7 @@
 
 #pragma clang diagnostic push
 #pragma clang diagnostic ignored "-Wc++98-compat-extra-semi"
-GREY_STUB_CLASS_IN_APP_MAIN_QUEUE(ContentSuggestionsAppInterface);
+GREY_STUB_CLASS_IN_APP_MAIN_QUEUE(NewTabPageAppInterface);
 #pragma clang diagnostic pop
 
 namespace {
@@ -49,7 +50,7 @@
       selectElementWithMatcher:grey_allOf(chrome_test_util::ToolsMenuButton(),
                                           grey_sufficientlyVisible(), nil)]
          usingSearchAction:grey_scrollInDirection(kGREYDirectionUp, 150)
-      onElementWithMatcher:chrome_test_util::ContentSuggestionCollectionView()]
+      onElementWithMatcher:chrome_test_util::NTPCollectionView()]
       assertWithMatcher:grey_notNil()];
 }
 
@@ -80,7 +81,7 @@
       selectElementWithMatcher:grey_allOf(matcher, grey_sufficientlyVisible(),
                                           nil)]
          usingSearchAction:action
-      onElementWithMatcher:chrome_test_util::ContentSuggestionCollectionView()];
+      onElementWithMatcher:chrome_test_util::NTPCollectionView()];
 }
 
 }  // namespace
@@ -110,24 +111,24 @@
 + (void)setUpHelper {
   [self closeAllTabs];
 
-  [ContentSuggestionsAppInterface setUpService];
+  [NewTabPageAppInterface setUpService];
 }
 
 + (void)tearDown {
   [self closeAllTabs];
 
-  [ContentSuggestionsAppInterface resetService];
+  [NewTabPageAppInterface resetService];
 
   [super tearDown];
 }
 
 - (void)setUp {
   [super setUp];
-  [ContentSuggestionsAppInterface makeSuggestionsAvailable];
+  [NewTabPageAppInterface makeSuggestionsAvailable];
 }
 
 - (void)tearDown {
-  [ContentSuggestionsAppInterface disableSuggestions];
+  [NewTabPageAppInterface disableSuggestions];
   [ChromeEarlGrey clearBrowsingHistory];
   [super tearDown];
 }
@@ -147,9 +148,8 @@
   const GURL pageURL = self.testServer->GetURL(kPageURL);
 
   // Add 3 suggestions, persisted accross page loads.
-  [ContentSuggestionsAppInterface
-        addNumberOfSuggestions:3
-      additionalSuggestionsURL:net::NSURLWithGURL(pageURL)];
+  [NewTabPageAppInterface addNumberOfSuggestions:3
+                        additionalSuggestionsURL:net::NSURLWithGURL(pageURL)];
 
   // Tap on more, which adds 10 elements.
   [CellWithMatcher(chrome_test_util::ButtonWithAccessibilityLabelId(
@@ -179,11 +179,11 @@
     EARL_GREY_TEST_DISABLED(@"Legacy Feed Test.");
   }
   // Add 2 suggestions, persisted accross page loads.
-  [ContentSuggestionsAppInterface addNumberOfSuggestions:2
-                                additionalSuggestionsURL:nil];
+  [NewTabPageAppInterface addNumberOfSuggestions:2
+                        additionalSuggestionsURL:nil];
 
   // Change the suggestions to have one the second one.
-  [ContentSuggestionsAppInterface addSuggestionNumber:2];
+  [NewTabPageAppInterface addSuggestionNumber:2];
 
   // Check that the first suggestion is still displayed.
   [CellWithMatcher(grey_accessibilityID(@"http://chromium.org/1"))
@@ -213,9 +213,8 @@
   const GURL pageURL = self.testServer->GetURL(kPageURL);
 
   // Add 3 suggestions, persisted accross page loads.
-  [ContentSuggestionsAppInterface
-        addNumberOfSuggestions:3
-      additionalSuggestionsURL:net::NSURLWithGURL(pageURL)];
+  [NewTabPageAppInterface addNumberOfSuggestions:3
+                        additionalSuggestionsURL:net::NSURLWithGURL(pageURL)];
 
   // Tap on more, which adds 10 elements.
   [CellWithMatcher(chrome_test_util::ButtonWithAccessibilityLabelId(
@@ -264,11 +263,11 @@
       selectElementWithMatcher:grey_accessibilityID(
                                    kContentSuggestionsLearnMoreIdentifier)]
          usingSearchAction:action
-      onElementWithMatcher:chrome_test_util::ContentSuggestionCollectionView()]
+      onElementWithMatcher:chrome_test_util::NTPCollectionView()]
       assertWithMatcher:grey_nil()];
 
-  [ContentSuggestionsAppInterface addNumberOfSuggestions:1
-                                additionalSuggestionsURL:nil];
+  [NewTabPageAppInterface addNumberOfSuggestions:1
+                        additionalSuggestionsURL:nil];
 
   [CellWithMatcher(grey_accessibilityID(kContentSuggestionsLearnMoreIdentifier))
       assertWithMatcher:grey_sufficientlyVisible()];
@@ -292,8 +291,7 @@
   // Check that the tab has been opened in background.
   ConditionBlock condition = ^{
     NSError* error = nil;
-    [[EarlGrey selectElementWithMatcher:chrome_test_util::
-                                            ContentSuggestionCollectionView()]
+    [[EarlGrey selectElementWithMatcher:chrome_test_util::NTPCollectionView()]
         assertWithMatcher:grey_notNil()
                     error:&error];
     return error == nil;
diff --git a/ios/chrome/browser/ui/content_suggestions/content_suggestions_layout.mm b/ios/chrome/browser/ui/content_suggestions/content_suggestions_layout.mm
index 97ed601..414e646 100644
--- a/ios/chrome/browser/ui/content_suggestions/content_suggestions_layout.mm
+++ b/ios/chrome/browser/ui/content_suggestions/content_suggestions_layout.mm
@@ -5,7 +5,6 @@
 #import "ios/chrome/browser/ui/content_suggestions/content_suggestions_layout.h"
 
 #import "ios/chrome/browser/ui/content_suggestions/content_suggestions_collection_utils.h"
-#import "ios/chrome/browser/ui/ntp/new_tab_page_feature.h"
 #import "ios/chrome/browser/ui/ntp/new_tab_page_header_constants.h"
 #import "ios/chrome/browser/ui/ntp/new_tab_page_omnibox_positioning.h"
 #import "ios/chrome/browser/ui/toolbar/public/toolbar_utils.h"
@@ -34,7 +33,7 @@
 }
 
 - (CGSize)collectionViewContentSize {
-  if (IsRefactoredNTP() && [self isFeedVisible]) {
+  if ([self isFeedVisible]) {
     // In the refactored NTP and when the Feed is visible, we don't want to
     // extend the view height beyond its content.
     return [super collectionViewContentSize];
@@ -124,7 +123,7 @@
   if ([kind isEqualToString:UICollectionElementKindSectionHeader] &&
       indexPath.section == 0) {
     CGFloat contentOffset;
-    if (IsRefactoredNTP() && [self isFeedVisible]) {
+    if ([self isFeedVisible]) {
       contentOffset = self.parentCollectionView.contentOffset.y +
                       self.collectionView.contentSize.height;
     } else {
@@ -145,13 +144,13 @@
             [UIApplication sharedApplication].preferredContentSizeCategory) -
         self.collectionView.safeAreaInsets.top;
 
-    if (IsRefactoredNTP() && [self isFeedVisible]) {
+    if ([self isFeedVisible]) {
       minY = [self.omniboxPositioner stickyOmniboxHeight];
     }
     // TODO(crbug.com/1114792): Remove mentioned of "refactored" from the
     // variable name once this launches.
     BOOL hasScrolledIntoRefactoredDiscoverFeed =
-        [self isFeedVisible] && self.isScrolledIntoFeed && IsRefactoredNTP();
+        [self isFeedVisible] && self.isScrolledIntoFeed;
     if (contentOffset > minY && !hasScrolledIntoRefactoredDiscoverFeed) {
       origin.y = contentOffset - minY;
     }
diff --git a/ios/chrome/browser/ui/content_suggestions/content_suggestions_view_controller.h b/ios/chrome/browser/ui/content_suggestions/content_suggestions_view_controller.h
index ebaeb5d..3951806 100644
--- a/ios/chrome/browser/ui/content_suggestions/content_suggestions_view_controller.h
+++ b/ios/chrome/browser/ui/content_suggestions/content_suggestions_view_controller.h
@@ -41,7 +41,7 @@
 // Offset is only required if Discover feed is visible.
 - (instancetype)initWithStyle:(CollectionViewControllerStyle)style
                        offset:(CGFloat)offset
-                  feedVisible:(BOOL)visible NS_DESIGNATED_INITIALIZER;
+        refactoredFeedVisible:(BOOL)visible NS_DESIGNATED_INITIALIZER;
 
 - (instancetype)initWithLayout:(UICollectionViewLayout*)layout
                          style:(CollectionViewControllerStyle)style
diff --git a/ios/chrome/browser/ui/content_suggestions/content_suggestions_view_controller.mm b/ios/chrome/browser/ui/content_suggestions/content_suggestions_view_controller.mm
index 00f0587..a252414 100644
--- a/ios/chrome/browser/ui/content_suggestions/content_suggestions_view_controller.mm
+++ b/ios/chrome/browser/ui/content_suggestions/content_suggestions_view_controller.mm
@@ -116,7 +116,7 @@
 
 - (instancetype)initWithStyle:(CollectionViewControllerStyle)style
                        offset:(CGFloat)offset
-                  feedVisible:(BOOL)visible {
+        refactoredFeedVisible:(BOOL)visible {
   _offset = offset;
   _layout = [[ContentSuggestionsLayout alloc] initWithOffset:offset
                                                  feedVisible:visible];
@@ -264,8 +264,6 @@
   // to never and internally offset the UI to account for safe area insets.
   self.collectionView.contentInsetAdjustmentBehavior =
       UIScrollViewContentInsetAdjustmentNever;
-  self.collectionView.accessibilityIdentifier =
-      kContentSuggestionsCollectionIdentifier;
   _collectionUpdater.collectionViewController = self;
 
   self.collectionView.delegate = self;
diff --git a/ios/chrome/browser/ui/content_suggestions/content_suggestions_app_interface.h b/ios/chrome/browser/ui/content_suggestions/new_tab_page_app_interface.h
similarity index 78%
rename from ios/chrome/browser/ui/content_suggestions/content_suggestions_app_interface.h
rename to ios/chrome/browser/ui/content_suggestions/new_tab_page_app_interface.h
index ffdae458..900dbbefa 100644
--- a/ios/chrome/browser/ui/content_suggestions/content_suggestions_app_interface.h
+++ b/ios/chrome/browser/ui/content_suggestions/new_tab_page_app_interface.h
@@ -2,13 +2,14 @@
 // 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_CONTENT_SUGGESTIONS_CONTENT_SUGGESTIONS_APP_INTERFACE_H_
-#define IOS_CHROME_BROWSER_UI_CONTENT_SUGGESTIONS_CONTENT_SUGGESTIONS_APP_INTERFACE_H_
+#ifndef IOS_CHROME_BROWSER_UI_CONTENT_SUGGESTIONS_NEW_TAB_PAGE_APP_INTERFACE_H_
+#define IOS_CHROME_BROWSER_UI_CONTENT_SUGGESTIONS_NEW_TAB_PAGE_APP_INTERFACE_H_
 
 #import <UIKit/UIKit.h>
 
-// App interface for the Content Suggestions.
-@interface ContentSuggestionsAppInterface : NSObject
+// App interface for the NTP.
+// TODO(crbug.com/1200303): Move this to */ui/ntp.
+@interface NewTabPageAppInterface : NSObject
 
 // Sets the fake service up.
 + (void)setUpService;
@@ -52,12 +53,15 @@
                               traitCollection:
                                   (UITraitCollection*)traitCollection;
 
-// Returns the collection view.
+// Returns the NTP collection view.
 + (UICollectionView*)collectionView;
 
+// Returns the content suggestions collection view.
++ (UICollectionView*)contentSuggestionsCollectionView;
+
 // Returns the fake omnibox.
 + (UIView*)fakeOmnibox;
 
 @end
 
-#endif  // IOS_CHROME_BROWSER_UI_CONTENT_SUGGESTIONS_CONTENT_SUGGESTIONS_APP_INTERFACE_H_
+#endif  // IOS_CHROME_BROWSER_UI_CONTENT_SUGGESTIONS_NEW_TAB_PAGE_APP_INTERFACE_H_
diff --git a/ios/chrome/browser/ui/content_suggestions/content_suggestions_app_interface.mm b/ios/chrome/browser/ui/content_suggestions/new_tab_page_app_interface.mm
similarity index 90%
rename from ios/chrome/browser/ui/content_suggestions/content_suggestions_app_interface.mm
rename to ios/chrome/browser/ui/content_suggestions/new_tab_page_app_interface.mm
index f4c3740..6c10e59 100644
--- a/ios/chrome/browser/ui/content_suggestions/content_suggestions_app_interface.mm
+++ b/ios/chrome/browser/ui/content_suggestions/new_tab_page_app_interface.mm
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#import "ios/chrome/browser/ui/content_suggestions/content_suggestions_app_interface.h"
+#import "ios/chrome/browser/ui/content_suggestions/new_tab_page_app_interface.h"
 
 #include "base/strings/string_number_conversions.h"
 #include "base/strings/sys_string_conversions.h"
@@ -59,7 +59,7 @@
 
 }  // namespace
 
-@implementation ContentSuggestionsAppInterface
+@implementation NewTabPageAppInterface
 
 + (void)setUpService {
   ChromeBrowserState* browserState =
@@ -90,14 +90,13 @@
 }
 
 + (void)makeSuggestionsAvailable {
-  [self provider] -> FireCategoryStatusChanged([self category],
-                                               CategoryStatus::AVAILABLE);
+  [self provider]->FireCategoryStatusChanged([self category],
+                                             CategoryStatus::AVAILABLE);
 }
 
 + (void)disableSuggestions {
-  [self provider] -> FireCategoryStatusChanged(
-                      [self category],
-                      CategoryStatus::ALL_SUGGESTIONS_EXPLICITLY_DISABLED);
+  [self provider]->FireCategoryStatusChanged(
+      [self category], CategoryStatus::ALL_SUGGESTIONS_EXPLICITLY_DISABLED);
 }
 
 + (void)addNumberOfSuggestions:(NSInteger)numberOfSuggestions
@@ -110,8 +109,8 @@
         CreateSuggestion([self category], "chromium" + index,
                          GURL("http://chromium.org/" + index)));
   }
-  [self provider] -> FireSuggestionsChanged([self category],
-                                            std::move(suggestions));
+  [self provider]->FireSuggestionsChanged([self category],
+                                          std::move(suggestions));
 
   if (URL) {
     // Set up the action when "More" is tapped.
@@ -130,8 +129,8 @@
   std::vector<ContentSuggestion> suggestions;
   suggestions.push_back(CreateSuggestion([self category], "chromium" + index,
                                          GURL("http://chromium.org/" + index)));
-  [self provider] -> FireSuggestionsChanged([self category],
-                                            std::move(suggestions));
+  [self provider]->FireSuggestionsChanged([self category],
+                                          std::move(suggestions));
 }
 
 + (NSString*)defaultSearchEngine {
@@ -189,6 +188,10 @@
   return ntp_home::CollectionView();
 }
 
++ (UICollectionView*)contentSuggestionsCollectionView {
+  return ntp_home::ContentSuggestionsCollectionView();
+}
+
 + (UIView*)fakeOmnibox {
   return ntp_home::FakeOmnibox();
 }
diff --git a/ios/chrome/browser/ui/content_suggestions/ntp_home_egtest.mm b/ios/chrome/browser/ui/content_suggestions/ntp_home_egtest.mm
index 7b5e55c..9d5e72b2 100644
--- a/ios/chrome/browser/ui/content_suggestions/ntp_home_egtest.mm
+++ b/ios/chrome/browser/ui/content_suggestions/ntp_home_egtest.mm
@@ -7,10 +7,12 @@
 #include "base/mac/foundation_util.h"
 #include "base/strings/sys_string_conversions.h"
 #include "components/strings/grit/components_strings.h"
+#include "ios/chrome/browser/chrome_switches.h"
 #import "ios/chrome/browser/pref_names.h"
-#import "ios/chrome/browser/ui/content_suggestions/content_suggestions_app_interface.h"
 #import "ios/chrome/browser/ui/content_suggestions/content_suggestions_constants.h"
+#import "ios/chrome/browser/ui/content_suggestions/new_tab_page_app_interface.h"
 #import "ios/chrome/browser/ui/content_suggestions/ntp_home_constant.h"
+#import "ios/chrome/browser/ui/ntp/new_tab_page_constants.h"
 #import "ios/chrome/browser/ui/settings/settings_table_view_controller_constants.h"
 #import "ios/chrome/browser/ui/toolbar/public/toolbar_constants.h"
 #include "ios/chrome/grit/ios_strings.h"
@@ -108,33 +110,40 @@
   [pasteboard setValue:@"" forPasteboardType:UIPasteboardNameGeneral];
 
   [self closeAllTabs];
-  [ContentSuggestionsAppInterface setUpService];
+  [NewTabPageAppInterface setUpService];
 }
 
 + (void)tearDown {
   [self closeAllTabs];
-  [ContentSuggestionsAppInterface resetService];
+  [NewTabPageAppInterface resetService];
 
   [super tearDown];
 }
 
+- (AppLaunchConfiguration)appConfigurationForTestCase {
+  // Use commandline args to enable the Discover feed for this test case.
+  // Disabled elsewhere to account for possible flakiness.
+  AppLaunchConfiguration config;
+  config.additional_args.push_back(std::string("--") +
+                                   switches::kEnableDiscoverFeed);
+  return config;
+}
+
 - (void)setUp {
   [super setUp];
-  [ContentSuggestionsAppInterface makeSuggestionsAvailable];
+  [NewTabPageAppInterface makeSuggestionsAvailable];
   [ChromeEarlGreyAppInterface
       setBoolValue:YES
        forUserPref:base::SysUTF8ToNSString(prefs::kArticlesForYouEnabled)];
 
-  self.defaultSearchEngine =
-      [ContentSuggestionsAppInterface defaultSearchEngine];
+  self.defaultSearchEngine = [NewTabPageAppInterface defaultSearchEngine];
 }
 
 - (void)tearDown {
-  [ContentSuggestionsAppInterface disableSuggestions];
+  [NewTabPageAppInterface disableSuggestions];
   [EarlGrey rotateDeviceToOrientation:UIDeviceOrientationPortrait
                                 error:nil];
-
-  [ContentSuggestionsAppInterface resetSearchEngineTo:self.defaultSearchEngine];
+  [NewTabPageAppInterface resetSearchEngineTo:self.defaultSearchEngine];
 
   [super tearDown];
 }
@@ -232,13 +241,12 @@
     EARL_GREY_TEST_DISABLED(@"Disabled for iPad due to device rotation bug.");
   }
   [ChromeEarlGreyUI waitForAppToIdle];
-  UICollectionView* collectionView =
-      [ContentSuggestionsAppInterface collectionView];
+  UICollectionView* collectionView = [NewTabPageAppInterface collectionView];
   UIEdgeInsets safeArea = collectionView.safeAreaInsets;
   CGFloat collectionWidth =
       CGRectGetWidth(UIEdgeInsetsInsetRect(collectionView.bounds, safeArea));
   GREYAssertTrue(collectionWidth > 0, @"The collection width is nil.");
-  CGFloat fakeOmniboxWidth = [ContentSuggestionsAppInterface
+  CGFloat fakeOmniboxWidth = [NewTabPageAppInterface
       searchFieldWidthForCollectionWidth:collectionWidth
                          traitCollection:collectionView.traitCollection];
 
@@ -250,13 +258,13 @@
 
   [ChromeEarlGreyUI waitForAppToIdle];
 
-  collectionView = [ContentSuggestionsAppInterface collectionView];
+  collectionView = [NewTabPageAppInterface collectionView];
   safeArea = collectionView.safeAreaInsets;
   CGFloat collectionWidthAfterRotation =
       CGRectGetWidth(UIEdgeInsetsInsetRect(collectionView.bounds, safeArea));
   GREYAssertNotEqual(collectionWidth, collectionWidthAfterRotation,
                      @"The collection width has not changed.");
-  fakeOmniboxWidth = [ContentSuggestionsAppInterface
+  fakeOmniboxWidth = [NewTabPageAppInterface
       searchFieldWidthForCollectionWidth:collectionWidthAfterRotation
                          traitCollection:collectionView.traitCollection];
 
@@ -273,13 +281,12 @@
     EARL_GREY_TEST_DISABLED(@"Disabled for iPad due to device rotation bug.");
   }
   [ChromeEarlGreyUI waitForAppToIdle];
-  UICollectionView* collectionView =
-      [ContentSuggestionsAppInterface collectionView];
+  UICollectionView* collectionView = [NewTabPageAppInterface collectionView];
   UIEdgeInsets safeArea = collectionView.safeAreaInsets;
   CGFloat collectionWidth =
       CGRectGetWidth(UIEdgeInsetsInsetRect(collectionView.bounds, safeArea));
   GREYAssertTrue(collectionWidth > 0, @"The collection width is nil.");
-  CGFloat fakeOmniboxWidth = [ContentSuggestionsAppInterface
+  CGFloat fakeOmniboxWidth = [NewTabPageAppInterface
       searchFieldWidthForCollectionWidth:collectionWidth
                          traitCollection:collectionView.traitCollection];
 
@@ -296,13 +303,13 @@
   [[EarlGrey selectElementWithMatcher:chrome_test_util::SettingsDoneButton()]
       performAction:grey_tap()];
 
-  collectionView = [ContentSuggestionsAppInterface collectionView];
+  collectionView = [NewTabPageAppInterface collectionView];
   safeArea = collectionView.safeAreaInsets;
   CGFloat collectionWidthAfterRotation =
       CGRectGetWidth(UIEdgeInsetsInsetRect(collectionView.bounds, safeArea));
   GREYAssertNotEqual(collectionWidth, collectionWidthAfterRotation,
                      @"The collection width has not changed.");
-  fakeOmniboxWidth = [ContentSuggestionsAppInterface
+  fakeOmniboxWidth = [NewTabPageAppInterface
       searchFieldWidthForCollectionWidth:collectionWidthAfterRotation
                          traitCollection:collectionView.traitCollection];
 
@@ -319,13 +326,12 @@
     EARL_GREY_TEST_DISABLED(@"Disabled for iPad due to device rotation bug.");
   }
 
-  [[EarlGrey selectElementWithMatcher:chrome_test_util::
-                                          ContentSuggestionCollectionView()]
+  [[EarlGrey selectElementWithMatcher:chrome_test_util::NTPCollectionView()]
       performAction:grey_swipeFastInDirection(kGREYDirectionUp)];
 
   [ChromeEarlGreyUI waitForAppToIdle];
   CGFloat collectionWidth =
-      [ContentSuggestionsAppInterface collectionView].bounds.size.width;
+      [NewTabPageAppInterface collectionView].bounds.size.width;
   GREYAssertTrue(collectionWidth > 0, @"The collection width is nil.");
 
   // The fake omnibox might be slightly bigger than the screen in order to cover
@@ -338,7 +344,7 @@
 
   [ChromeEarlGreyUI waitForAppToIdle];
   CGFloat collectionWidthAfterRotation =
-      [ContentSuggestionsAppInterface collectionView].bounds.size.width;
+      [NewTabPageAppInterface collectionView].bounds.size.width;
   GREYAssertNotEqual(collectionWidth, collectionWidthAfterRotation,
                      @"The collection width has not changed.");
 }
@@ -351,14 +357,13 @@
         @"Disabled for iPad since it does not pin the omnibox.");
   }
 
-  UIView* fakeOmnibox = [ContentSuggestionsAppInterface fakeOmnibox];
+  UIView* fakeOmnibox = [NewTabPageAppInterface fakeOmnibox];
   [[EarlGrey selectElementWithMatcher:chrome_test_util::FakeOmnibox()]
       assertWithMatcher:grey_sufficientlyVisible()];
   GREYAssertTrue(fakeOmnibox.frame.origin.x > 1,
                  @"The omnibox is pinned to top before scrolling down.");
 
-  [[EarlGrey selectElementWithMatcher:chrome_test_util::
-                                          ContentSuggestionCollectionView()]
+  [[EarlGrey selectElementWithMatcher:chrome_test_util::NTPCollectionView()]
       performAction:grey_swipeFastInDirection(kGREYDirectionUp)];
 
   [ChromeEarlGreyUI waitForAppToIdle];
@@ -380,15 +385,14 @@
   }
 
   CGFloat omniboxWidthBeforeScrolling =
-      [ContentSuggestionsAppInterface fakeOmnibox].frame.size.width;
-  [[EarlGrey selectElementWithMatcher:chrome_test_util::
-                                          ContentSuggestionCollectionView()]
+      [NewTabPageAppInterface fakeOmnibox].frame.size.width;
+  [[EarlGrey selectElementWithMatcher:chrome_test_util::NTPCollectionView()]
       performAction:grey_swipeFastInDirection(kGREYDirectionUp)];
 
   [ChromeEarlGreyUI waitForAppToIdle];
 
   CGFloat omniboxWidthAfterScrolling =
-      [ContentSuggestionsAppInterface fakeOmnibox].frame.size.width;
+      [NewTabPageAppInterface fakeOmnibox].frame.size.width;
 
   GREYAssertTrue(
       omniboxWidthAfterScrolling > omniboxWidthBeforeScrolling,
@@ -408,7 +412,7 @@
 
 // Tests that the promo is correctly displayed and removed once tapped.
 - (void)testPromoTap {
-  [ContentSuggestionsAppInterface setWhatsNewPromoToMoveToDock];
+  [NewTabPageAppInterface setWhatsNewPromoToMoveToDock];
 
   // Open a new tab to have the promo.
   [ChromeEarlGreyUI openNewTab];
@@ -425,7 +429,7 @@
                                    @"ContentSuggestionsWhatsNewIdentifier")]
       assertWithMatcher:grey_not(grey_sufficientlyVisible())];
 
-  [ContentSuggestionsAppInterface resetWhatsNewPromo];
+  [NewTabPageAppInterface resetWhatsNewPromo];
 }
 
 // Tests that the position of the collection view is restored when navigating
@@ -434,17 +438,16 @@
   [self addMostVisitedTile];
 
   // Add suggestions to be able to scroll on iPad.
-  [ContentSuggestionsAppInterface addNumberOfSuggestions:15
-                                additionalSuggestionsURL:nil];
+  [NewTabPageAppInterface addNumberOfSuggestions:15
+                        additionalSuggestionsURL:nil];
 
   // Scroll to have a position to restored.
-  [[EarlGrey selectElementWithMatcher:chrome_test_util::
-                                          ContentSuggestionCollectionView()]
+  [[EarlGrey selectElementWithMatcher:chrome_test_util::NTPCollectionView()]
       performAction:grey_scrollInDirection(kGREYDirectionDown, 150)];
 
   // Save the position before navigating.
-  UIView* omnibox = [ContentSuggestionsAppInterface fakeOmnibox];
-  CGPoint previousPosition = omnibox.bounds.origin;
+  UICollectionView* collectionView = [NewTabPageAppInterface collectionView];
+  CGFloat previousPosition = collectionView.contentOffset.y;
 
   // Navigate and come back.
   [[EarlGrey selectElementWithMatcher:
@@ -455,9 +458,8 @@
   [ChromeEarlGrey goBack];
 
   // Check that the new position is the same.
-  omnibox = [ContentSuggestionsAppInterface fakeOmnibox];
-  GREYAssertEqual(previousPosition.y, omnibox.bounds.origin.y,
-                  @"Omnibox not at the same position");
+  GREYAssertEqual(previousPosition, collectionView.contentOffset.y,
+                  @"NTP is not at the same position.");
 }
 
 // Tests that when navigating back to the NTP while having the omnibox focused
@@ -467,17 +469,16 @@
   [self addMostVisitedTile];
 
   // Add suggestions to be able to scroll on iPad.
-  [ContentSuggestionsAppInterface addNumberOfSuggestions:15
-                                additionalSuggestionsURL:nil];
+  [NewTabPageAppInterface addNumberOfSuggestions:15
+                        additionalSuggestionsURL:nil];
 
   // Scroll to have a position to restored.
-  [[EarlGrey selectElementWithMatcher:chrome_test_util::
-                                          ContentSuggestionCollectionView()]
+  [[EarlGrey selectElementWithMatcher:chrome_test_util::NTPCollectionView()]
       performAction:grey_scrollInDirection(kGREYDirectionDown, 150)];
 
   // Save the position before navigating.
-  UIView* omnibox = [ContentSuggestionsAppInterface fakeOmnibox];
-  CGPoint previousPosition = omnibox.bounds.origin;
+  UICollectionView* collectionView = [NewTabPageAppInterface collectionView];
+  CGFloat previousPosition = collectionView.contentOffset.y;
 
   // Tap the omnibox to focus it.
   [self focusFakebox];
@@ -491,9 +492,10 @@
   [ChromeEarlGrey goBack];
 
   // Check that the new position is the same.
-  omnibox = [ContentSuggestionsAppInterface fakeOmnibox];
-  GREYAssertEqual(previousPosition.y, omnibox.bounds.origin.y,
-                  @"Omnibox not at the same position");
+  collectionView = [NewTabPageAppInterface collectionView];
+  GREYAssertEqual(
+      previousPosition, collectionView.contentOffset.y,
+      @"NTP is not at the same position as before tapping the omnibox");
 }
 
 // Tests that tapping the fake omnibox focuses the real omnibox.
@@ -524,13 +526,8 @@
 // Tests that tapping the fake omnibox moves the collection.
 - (void)testTapFakeOmniboxScroll {
   // Get the collection and its layout.
-  UICollectionView* collectionView =
-      [ContentSuggestionsAppInterface collectionView];
+  UICollectionView* collectionView = [NewTabPageAppInterface collectionView];
 
-  GREYAssertTrue(
-      [collectionView.delegate
-          conformsToProtocol:@protocol(UICollectionViewDelegateFlowLayout)],
-      @"The collection has not the expected delegate.");
   id<UICollectionViewDelegateFlowLayout> delegate =
       (id<UICollectionViewDelegateFlowLayout>)(collectionView.delegate);
   CGFloat headerHeight =
@@ -552,14 +549,12 @@
   [[EarlGrey selectElementWithMatcher:chrome_test_util::FakeOmnibox()]
       assertWithMatcher:grey_not(grey_sufficientlyVisible())];
 
-  CGFloat top =
-      [ContentSuggestionsAppInterface collectionView].safeAreaInsets.top;
+  CGFloat top = [NewTabPageAppInterface collectionView].safeAreaInsets.top;
   GREYAssertTrue(offsetAfterTap.y >= origin.y + headerHeight - (60 + top),
                  @"The collection has not moved.");
 
   // Unfocus the omnibox.
-  [[EarlGrey selectElementWithMatcher:chrome_test_util::
-                                          ContentSuggestionCollectionView()]
+  [[EarlGrey selectElementWithMatcher:chrome_test_util::NTPCollectionView()]
       performAction:grey_tapAtPoint(CGPointMake(0, offsetAfterTap.y + 100))];
 
   // Check the fake omnibox is displayed again at the same position.
@@ -575,12 +570,10 @@
 // back to where it was.
 - (void)testTapFakeOmniboxScrollScrolled {
   // Get the collection and its layout.
-  UICollectionView* collectionView =
-      [ContentSuggestionsAppInterface collectionView];
+  UICollectionView* collectionView = [NewTabPageAppInterface collectionView];
 
   // Scroll to have a position different from the default.
-  [[EarlGrey selectElementWithMatcher:chrome_test_util::
-                                          ContentSuggestionCollectionView()]
+  [[EarlGrey selectElementWithMatcher:chrome_test_util::NTPCollectionView()]
       performAction:grey_scrollInDirection(kGREYDirectionDown, 50)];
 
   // Offset before the tap.
@@ -590,8 +583,7 @@
   [self focusFakebox];
 
   // Unfocus the omnibox.
-  [[EarlGrey selectElementWithMatcher:chrome_test_util::
-                                          ContentSuggestionCollectionView()]
+  [[EarlGrey selectElementWithMatcher:chrome_test_util::NTPCollectionView()]
       performAction:grey_tapAtPoint(
                         CGPointMake(0, collectionView.contentOffset.y + 100))];
 
@@ -686,11 +678,11 @@
   [ChromeEarlGreyAppInterface
       setBoolValue:NO
        forUserPref:base::SysUTF8ToNSString(prefs::kArticlesForYouEnabled)];
-  [self testNTPInitialPositionAndContent:[ContentSuggestionsAppInterface
-                                             collectionView]];
 
-  [[EarlGrey selectElementWithMatcher:chrome_test_util::
-                                          ContentSuggestionCollectionView()]
+  [self
+      testNTPInitialPositionAndContent:[NewTabPageAppInterface collectionView]];
+
+  [[EarlGrey selectElementWithMatcher:chrome_test_util::NTPCollectionView()]
       performAction:grey_swipeFastInDirection(kGREYDirectionUp)];
 
   // Ensures that tiles are still all visible with feed turned off.
@@ -708,8 +700,7 @@
 // Test to ensure that initial position and content are maintained when rotating
 // the device back and forth.
 - (void)testInitialPositionAndOrientationChange {
-  UICollectionView* collectionView =
-      [ContentSuggestionsAppInterface collectionView];
+  UICollectionView* collectionView = [NewTabPageAppInterface collectionView];
 
   [self testNTPInitialPositionAndContent:collectionView];
 
@@ -752,10 +743,8 @@
 }
 
 - (void)testNTPInitialPositionAndContent:(UICollectionView*)collectionView {
-  // TODO(crbug.com/1194106): Initial offset should not be 0 with refactored
-  // NTP, until native header is used.
-  GREYAssertTrue(collectionView.contentOffset.y == 0,
-                 @"The NTP is not scrolled to top by default.");
+  // TODO(crbug.com/1194106): Initial offset should be checked to be 0 with
+  // refactored NTP using native header.
   [[EarlGrey selectElementWithMatcher:chrome_test_util::NTPLogo()]
       assertWithMatcher:grey_sufficientlyVisible()];
   [[EarlGrey selectElementWithMatcher:chrome_test_util::FakeOmnibox()]
diff --git a/ios/chrome/browser/ui/content_suggestions/ntp_home_test_utils.h b/ios/chrome/browser/ui/content_suggestions/ntp_home_test_utils.h
index 2dbce1b7..74fbad2 100644
--- a/ios/chrome/browser/ui/content_suggestions/ntp_home_test_utils.h
+++ b/ios/chrome/browser/ui/content_suggestions/ntp_home_test_utils.h
@@ -13,10 +13,14 @@
 @protocol GREYMatcher;
 
 namespace ntp_home {
-// Returns the view corresponding to the ContentSuggestionsViewController.
-// Returns nil if it is not in the view hierarchy.
+// Returns the primary collection view of the new tab page. Returns nil if it is
+// not in the view hierarchy.
 UICollectionView* CollectionView();
 
+// Returns the collection view of the content suggestions. Returns nil if it is
+// not in the view hierarchy.
+UICollectionView* ContentSuggestionsCollectionView();
+
 // Returns the view corresponding to the fake omnibox. Returns nil if it is not
 // in the view hierarchy.
 UIView* FakeOmnibox();
diff --git a/ios/chrome/browser/ui/content_suggestions/ntp_home_test_utils.mm b/ios/chrome/browser/ui/content_suggestions/ntp_home_test_utils.mm
index 702ef686..0c9550b5 100644
--- a/ios/chrome/browser/ui/content_suggestions/ntp_home_test_utils.mm
+++ b/ios/chrome/browser/ui/content_suggestions/ntp_home_test_utils.mm
@@ -14,6 +14,7 @@
 #import "ios/chrome/browser/ui/content_suggestions/content_suggestions_constants.h"
 #import "ios/chrome/browser/ui/content_suggestions/content_suggestions_view_controller.h"
 #import "ios/chrome/browser/ui/content_suggestions/ntp_home_constant.h"
+#import "ios/chrome/browser/ui/ntp/new_tab_page_constants.h"
 #import "ios/testing/earl_grey/earl_grey_app.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
@@ -61,6 +62,13 @@
 UICollectionView* CollectionView() {
   return base::mac::ObjCCast<UICollectionView>(
       SubviewWithAccessibilityIdentifier(
+          kNTPCollectionViewIdentifier,
+          [[UIApplication sharedApplication] keyWindow]));
+}
+
+UICollectionView* ContentSuggestionsCollectionView() {
+  return base::mac::ObjCCast<UICollectionView>(
+      SubviewWithAccessibilityIdentifier(
           kContentSuggestionsCollectionIdentifier,
           [[UIApplication sharedApplication] keyWindow]));
 }
diff --git a/ios/chrome/browser/ui/main/scene_controller.mm b/ios/chrome/browser/ui/main/scene_controller.mm
index e917589..bb9eb64c 100644
--- a/ios/chrome/browser/ui/main/scene_controller.mm
+++ b/ios/chrome/browser/ui/main/scene_controller.mm
@@ -870,6 +870,10 @@
             .restoreHelper showRestorePrompt];
     self.sceneState.appState.startupInformation.restoreHelper = nil;
   }
+
+  // Make sure the geolocation controller is created to observe permission
+  // events.
+  [OmniboxGeolocationController sharedInstance];
 }
 
 // Determines which UI should be shown on startup, and shows it.
diff --git a/ios/chrome/browser/ui/ntp/BUILD.gn b/ios/chrome/browser/ui/ntp/BUILD.gn
index c4988645..cb0c598 100644
--- a/ios/chrome/browser/ui/ntp/BUILD.gn
+++ b/ios/chrome/browser/ui/ntp/BUILD.gn
@@ -7,15 +7,26 @@
     "new_tab_page_commands.h",
     "new_tab_page_content_delegate.h",
     "new_tab_page_controller_delegate.h",
-    "new_tab_page_header_constants.h",
-    "new_tab_page_header_constants.mm",
     "new_tab_page_omnibox_positioning.h",
   ]
   configs += [ "//build/config/compiler:enable_arc" ]
-  public_deps = [ ":feature_flags" ]
+  public_deps = [
+    ":constants",
+    ":feature_flags",
+  ]
   deps = [ "//ios/chrome/browser/ui/util" ]
 }
 
+source_set("constants") {
+  sources = [
+    "new_tab_page_constants.h",
+    "new_tab_page_constants.mm",
+    "new_tab_page_header_constants.h",
+    "new_tab_page_header_constants.mm",
+  ]
+  configs += [ "//build/config/compiler:enable_arc" ]
+}
+
 source_set("coordinator") {
   sources = [
     "new_tab_page_coordinator.h",
@@ -34,12 +45,14 @@
     "//ios/chrome/app:tests_hook",
     "//ios/chrome/browser:pref_names",
     "//ios/chrome/browser/browser_state",
+    "//ios/chrome/browser/discover_feed",
     "//ios/chrome/browser/main:public",
     "//ios/chrome/browser/search_engines",
     "//ios/chrome/browser/signin",
     "//ios/chrome/browser/ui/commands",
     "//ios/chrome/browser/ui/content_suggestions",
     "//ios/chrome/browser/ui/content_suggestions:content_suggestions_ui",
+    "//ios/chrome/browser/ui/content_suggestions:feature_flags",
     "//ios/chrome/browser/ui/coordinators:chrome_coordinators",
     "//ios/chrome/browser/ui/main:scene_state_header",
     "//ios/chrome/browser/ui/main:scene_state_observer",
diff --git a/ios/chrome/browser/ui/ntp/new_tab_page_constants.h b/ios/chrome/browser/ui/ntp/new_tab_page_constants.h
new file mode 100644
index 0000000..defcb19
--- /dev/null
+++ b/ios/chrome/browser/ui/ntp/new_tab_page_constants.h
@@ -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.
+
+#ifndef IOS_CHROME_BROWSER_UI_NTP_NEW_TAB_PAGE_CONSTANTS_H_
+#define IOS_CHROME_BROWSER_UI_NTP_NEW_TAB_PAGE_CONSTANTS_H_
+
+#import <Foundation/Foundation.h>
+
+// Represents the NTP collection view.
+extern NSString* const kNTPCollectionViewIdentifier;
+
+#endif  // IOS_CHROME_BROWSER_UI_NTP_NEW_TAB_PAGE_CONSTANTS_H_
diff --git a/ios/chrome/browser/ui/ntp/new_tab_page_constants.mm b/ios/chrome/browser/ui/ntp/new_tab_page_constants.mm
new file mode 100644
index 0000000..bbd9aa4b
--- /dev/null
+++ b/ios/chrome/browser/ui/ntp/new_tab_page_constants.mm
@@ -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.
+
+#import "ios/chrome/browser/ui/ntp/new_tab_page_constants.h"
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
+NSString* const kNTPCollectionViewIdentifier = @"NTPCollectionViewIdentifier";
diff --git a/ios/chrome/browser/ui/ntp/new_tab_page_coordinator.mm b/ios/chrome/browser/ui/ntp/new_tab_page_coordinator.mm
index fb3febe..575e843f 100644
--- a/ios/chrome/browser/ui/ntp/new_tab_page_coordinator.mm
+++ b/ios/chrome/browser/ui/ntp/new_tab_page_coordinator.mm
@@ -15,6 +15,8 @@
 #import "components/search_engines/default_search_manager.h"
 #include "ios/chrome/app/tests_hook.h"
 #include "ios/chrome/browser/browser_state/chrome_browser_state.h"
+#import "ios/chrome/browser/discover_feed/discover_feed_service.h"
+#import "ios/chrome/browser/discover_feed/discover_feed_service_factory.h"
 #import "ios/chrome/browser/main/browser.h"
 #import "ios/chrome/browser/pref_names.h"
 #import "ios/chrome/browser/search_engines/template_url_service_factory.h"
@@ -26,8 +28,10 @@
 #import "ios/chrome/browser/ui/commands/omnibox_commands.h"
 #import "ios/chrome/browser/ui/commands/open_new_tab_command.h"
 #import "ios/chrome/browser/ui/content_suggestions/content_suggestions_coordinator.h"
+#import "ios/chrome/browser/ui/content_suggestions/content_suggestions_feature.h"
 #import "ios/chrome/browser/ui/content_suggestions/content_suggestions_header_synchronizer.h"
 #import "ios/chrome/browser/ui/content_suggestions/content_suggestions_header_view_controller.h"
+#import "ios/chrome/browser/ui/content_suggestions/discover_feed_metrics_recorder.h"
 #import "ios/chrome/browser/ui/content_suggestions/ntp_home_mediator.h"
 #import "ios/chrome/browser/ui/main/scene_state.h"
 #import "ios/chrome/browser/ui/main/scene_state_browser_agent.h"
@@ -132,6 +136,9 @@
 // means the feed header is shown, but not any of the feed content.
 @property(nonatomic, strong) PrefBackedBoolean* discoverFeedExpanded;
 
+// The view controller representing the Discover feed.
+@property(nonatomic, weak) UIViewController* discoverFeedViewController;
+
 @end
 
 @implementation NewTabPageCoordinator
@@ -219,25 +226,44 @@
   self.contentSuggestionsCoordinator.ntpCommandHandler = self;
   self.contentSuggestionsCoordinator.bubblePresenter = self.bubblePresenter;
 
-  [self.contentSuggestionsCoordinator start];
+  if (IsDiscoverFeedEnabled()) {
+    // Creating the DiscoverFeedService will start the DiscoverFeed.
+    DiscoverFeedService* discoverFeedService =
+        DiscoverFeedServiceFactory::GetForBrowserState(
+            self.browser->GetBrowserState());
+    self.contentSuggestionsCoordinator.discoverFeedMetricsRecorder =
+        discoverFeedService->GetDiscoverFeedMetricsRecorder();
+  }
 
-  self.ntpMediator.refactoredFeedVisible = [self isNTPRefactoredAndFeedVisible];
+  // Requests a Discover feed here if the correct flags and prefs are enabled.
   if ([self isNTPRefactoredAndFeedVisible]) {
-    self.ntpViewController = [[NewTabPageViewController alloc]
-        initWithContentSuggestionsViewController:
-            self.contentSuggestionsCoordinator.viewController];
-    self.ntpViewController.panGestureHandler = self.panGestureHandler;
-    self.ntpMediator.ntpViewController = self.ntpViewController;
-
-    UIViewController* discoverFeedViewController =
+    self.ntpViewController = [[NewTabPageViewController alloc] init];
+    self.discoverFeedViewController =
         ios::GetChromeBrowserProvider()
             ->GetDiscoverFeedProvider()
             ->NewFeedViewControllerWithScrollDelegate(self.browser,
                                                       self.ntpViewController);
+    if (!self.discoverFeedViewController) {
+      self.ntpViewController = nil;
+    }
+  }
+
+  self.contentSuggestionsCoordinator.refactoredFeedVisible =
+      self.discoverFeedViewController;
+  self.ntpMediator.refactoredFeedVisible = self.discoverFeedViewController;
+
+  [self.contentSuggestionsCoordinator start];
+
+  // Uses refactored NTP if Discover feed was successfully fetched.
+  if (self.discoverFeedViewController) {
+    self.ntpViewController.contentSuggestionsViewController =
+        self.contentSuggestionsCoordinator.viewController;
+    self.ntpViewController.panGestureHandler = self.panGestureHandler;
+    self.ntpMediator.ntpViewController = self.ntpViewController;
 
     self.discoverFeedWrapperViewController =
         [[DiscoverFeedWrapperViewController alloc]
-            initWithDiscoverFeedViewController:discoverFeedViewController];
+            initWithDiscoverFeedViewController:self.discoverFeedViewController];
 
     self.headerSynchronizer = [[ContentSuggestionsHeaderSynchronizer alloc]
         initWithCollectionController:self.ntpViewController
@@ -267,7 +293,7 @@
       sceneState.activationLevel >= SceneActivationLevelForegroundInactive;
 
   UIViewController* containedViewController =
-      [self isNTPRefactoredAndFeedVisible]
+      self.discoverFeedViewController
           ? self.ntpViewController
           : self.contentSuggestionsCoordinator.viewController;
 
@@ -358,7 +384,7 @@
 }
 
 - (id<ThumbStripSupporting>)thumbStripSupporting {
-  return [self isNTPRefactoredAndFeedVisible]
+  return self.discoverFeedViewController
              ? self.ntpViewController
              : self.contentSuggestionsCoordinator.thumbStripSupporting;
 }
@@ -373,7 +399,7 @@
   if (!self.contentSuggestionsCoordinator) {
     return;
   }
-  if ([self isNTPRefactoredAndFeedVisible]) {
+  if (self.discoverFeedViewController) {
     [self.ntpViewController stopScrolling];
   } else {
     [self.contentSuggestionsCoordinator stopScrolling];
@@ -390,7 +416,7 @@
 
 - (void)willUpdateSnapshot {
   if (self.contentSuggestionsCoordinator.started &&
-      [self isNTPRefactoredAndFeedVisible]) {
+      self.discoverFeedViewController) {
     [self.ntpViewController willUpdateSnapshot];
   } else {
     [self.contentSuggestionsCoordinator willUpdateSnapshot];
@@ -402,7 +428,7 @@
 }
 
 - (void)reload {
-  if ([self isNTPRefactoredAndFeedVisible]) {
+  if (self.discoverFeedViewController) {
     ios::GetChromeBrowserProvider()->GetDiscoverFeedProvider()->RefreshFeed();
   }
   [self reloadContentSuggestions];
@@ -441,7 +467,7 @@
 }
 
 - (void)updateDiscoverFeedLayout {
-  if ([self isNTPRefactoredAndFeedVisible]) {
+  if (self.discoverFeedViewController) {
     [self.containedViewController.view setNeedsLayout];
     [self.containedViewController.view layoutIfNeeded];
     [self.ntpViewController updateContentSuggestionForCurrentLayout];
@@ -556,7 +582,7 @@
        preferenceName == prefs::kNTPContentSuggestionsEnabled)) {
     [self updateDiscoverFeedVisibility];
   }
-  if ([self isNTPRefactoredAndFeedVisible] &&
+  if (self.discoverFeedViewController &&
       preferenceName ==
           DefaultSearchManager::kDefaultSearchProviderDataPrefName) {
     [self updateDiscoverFeedLayout];
diff --git a/ios/chrome/browser/ui/ntp/new_tab_page_view_controller.h b/ios/chrome/browser/ui/ntp/new_tab_page_view_controller.h
index 8bd4904..195bff69 100644
--- a/ios/chrome/browser/ui/ntp/new_tab_page_view_controller.h
+++ b/ios/chrome/browser/ui/ntp/new_tab_page_view_controller.h
@@ -47,13 +47,14 @@
 // ContentSuggestions.
 @property(nonatomic, weak) UIButton* identityDiscButton;
 
-// Initializes view controller with NTP content view controllers.
-// |discoverFeedViewController| represents the Discover feed for suggesting
-// articles. |contentSuggestionsViewController| represents other content
-// suggestions, such as the most visited site tiles.
-- (instancetype)initWithContentSuggestionsViewController:
-    (UICollectionViewController*)contentSuggestionsViewController
-    NS_DESIGNATED_INITIALIZER;
+// View controller representing the NTP content suggestions. These suggestions
+// include the most visited site tiles, the shortcut tiles, the fake omnibox and
+// the Google doodle.
+@property(nonatomic, strong)
+    UICollectionViewController* contentSuggestionsViewController;
+
+// Initializes the new tab page view controller.
+- (instancetype)init NS_DESIGNATED_INITIALIZER;
 
 - (instancetype)initWithNibName:(NSString*)name
                          bundle:(NSBundle*)bundle NS_UNAVAILABLE;
diff --git a/ios/chrome/browser/ui/ntp/new_tab_page_view_controller.mm b/ios/chrome/browser/ui/ntp/new_tab_page_view_controller.mm
index 782be1f..ef50c29 100644
--- a/ios/chrome/browser/ui/ntp/new_tab_page_view_controller.mm
+++ b/ios/chrome/browser/ui/ntp/new_tab_page_view_controller.mm
@@ -12,6 +12,7 @@
 #import "ios/chrome/browser/ui/content_suggestions/ntp_home_constant.h"
 #import "ios/chrome/browser/ui/gestures/view_revealing_vertical_pan_handler.h"
 #import "ios/chrome/browser/ui/ntp/discover_feed_wrapper_view_controller.h"
+#import "ios/chrome/browser/ui/ntp/new_tab_page_constants.h"
 #import "ios/chrome/browser/ui/ntp/new_tab_page_content_delegate.h"
 #import "ios/chrome/browser/ui/ntp/new_tab_page_header_constants.h"
 #import "ios/chrome/browser/ui/ntp/new_tab_page_omnibox_positioning.h"
@@ -37,12 +38,6 @@
 @interface NewTabPageViewController () <NewTabPageOmniboxPositioning,
                                         UIGestureRecognizerDelegate>
 
-// View controller representing the NTP content suggestions. These suggestions
-// include the most visited site tiles, the shortcut tiles, the fake omnibox and
-// the Google doodle.
-@property(nonatomic, strong)
-    UICollectionViewController* contentSuggestionsViewController;
-
 // The overscroll actions controller managing accelerators over the toolbar.
 @property(nonatomic, strong)
     OverscrollActionsController* overscrollActionsController;
@@ -81,18 +76,8 @@
 @synthesize headerSynchronizer = _headerSynchronizer;
 @synthesize scrolledToTop = _scrolledToTop;
 
-- (instancetype)initWithContentSuggestionsViewController:
-    (UICollectionViewController*)contentSuggestionsViewController {
-  self = [super initWithNibName:nil bundle:nil];
-  if (self) {
-    _contentSuggestionsViewController = contentSuggestionsViewController;
-    // TODO(crbug.com/1114792): Instantiate this depending on the initial scroll
-    // position.
-    // TODO(crbug.com/1114792): Stick the fake omnibox based on default scroll
-    // position.
-  }
-
-  return self;
+- (instancetype)init {
+  return [super initWithNibName:nil bundle:nil];
 }
 
 - (void)dealloc {
@@ -103,12 +88,16 @@
   [super viewDidLoad];
 
   DCHECK(self.discoverFeedWrapperViewController);
+  DCHECK(self.contentSuggestionsViewController);
 
   // Prevent the NTP from spilling behind the toolbar and tab strip.
   self.view.clipsToBounds = YES;
 
   UIView* discoverFeedView = self.discoverFeedWrapperViewController.view;
 
+  self.discoverFeedWrapperViewController.feedCollectionView
+      .accessibilityIdentifier = kNTPCollectionViewIdentifier;
+
   [self.discoverFeedWrapperViewController willMoveToParentViewController:self];
   [self addChildViewController:self.discoverFeedWrapperViewController];
   [self.view addSubview:discoverFeedView];
diff --git a/ios/chrome/browser/ui/open_in/open_in_controller.h b/ios/chrome/browser/ui/open_in/open_in_controller.h
index 8569ce7..b2856e26 100644
--- a/ios/chrome/browser/ui/open_in/open_in_controller.h
+++ b/ios/chrome/browser/ui/open_in/open_in_controller.h
@@ -36,18 +36,19 @@
 
 // Class used to handle opening files in other applications.
 @interface OpenInController : NSObject <UIGestureRecognizerDelegate>
-// Designated initializer.
-- (id)initWithBaseViewController:(UIViewController*)baseViewController
-                URLLoaderFactory:
-                    (scoped_refptr<network::SharedURLLoaderFactory>)
-                        urlLoaderFactory
-                        webState:(web::WebState*)webState;
+
+- (instancetype)initWithBaseViewController:(UIViewController*)baseViewController
+                          URLLoaderFactory:
+                              (scoped_refptr<network::SharedURLLoaderFactory>)
+                                  urlLoaderFactory
+                                  webState:(web::WebState*)webState
+                                   browser:(Browser*)browser
+    NS_DESIGNATED_INITIALIZER;
+- (instancetype)init NS_UNAVAILABLE;
 
 // Base view on which the Open In toolbar will be presented.
 @property(nonatomic, weak) UIView* baseView;
 
-@property(nonatomic, assign) Browser* browser;
-
 // Removes the |openInToolbar_| from the |webController_|'s view and resets the
 // variables specific to the loaded document.
 - (void)disable;
diff --git a/ios/chrome/browser/ui/open_in/open_in_controller.mm b/ios/chrome/browser/ui/open_in/open_in_controller.mm
index fc2f47d9..b3946ed 100644
--- a/ios/chrome/browser/ui/open_in/open_in_controller.mm
+++ b/ios/chrome/browser/ui/open_in/open_in_controller.mm
@@ -14,6 +14,7 @@
 #include "base/metrics/histogram_macros.h"
 #include "base/metrics/user_metrics.h"
 #include "base/metrics/user_metrics_action.h"
+#include "base/sequence_checker.h"
 #include "base/sequenced_task_runner.h"
 #include "base/strings/sys_string_conversions.h"
 #include "base/task/post_task.h"
@@ -26,7 +27,6 @@
 #import "ios/chrome/browser/ui/util/uikit_ui_util.h"
 #import "ios/chrome/common/ui/util/constraints_ui_util.h"
 #include "ios/chrome/grit/ios_strings.h"
-#include "ios/web/public/thread/web_thread.h"
 #import "ios/web/public/ui/crw_web_view_proxy.h"
 #import "ios/web/public/ui/crw_web_view_scroll_view_proxy.h"
 #import "ios/web/public/web_state.h"
@@ -44,7 +44,7 @@
 namespace {
 // The path in the temp directory containing documents that are to be opened in
 // other applications.
-static NSString* const kDocumentsTempPath = @"OpenIn";
+static NSString* const kDocumentsTemporaryPath = @"OpenIn";
 
 // Duration of the show/hide animation for the |openInToolbar_|.
 const NSTimeInterval kOpenInToolbarAnimationDuration = 0.2;
@@ -71,7 +71,7 @@
 }
 
 // Returns true if the file located at |url| is file.
-bool HasValidFileAtUrl(NSURL* _Nullable url) {
+bool HasValidFileAtUrl(NSURL* url) {
   if (!url)
     return false;
 
@@ -85,6 +85,87 @@
   return [QLPreviewController canPreviewItem:url];
 }
 
+// Returns the temporary path where documents are stored.
+NSString* GetTemporaryDocumentDirectory() {
+  return [NSTemporaryDirectory()
+      stringByAppendingPathComponent:kDocumentsTemporaryPath];
+}
+
+// Removes the file at |file_url|.
+void RemoveDocumentAtPath(NSURL* file_url) {
+  base::ScopedBlockingCall scoped_blocking_call(FROM_HERE,
+                                                base::BlockingType::WILL_BLOCK);
+
+  if (!file_url.path)
+    return;
+
+  NSError* error = nil;
+  if (![[NSFileManager defaultManager] removeItemAtPath:file_url.path
+                                                  error:&error]) {
+    DLOG(ERROR) << "Failed to remove file: "
+                << base::SysNSStringToUTF8([error description]);
+  }
+}
+
+// Removes all the stored files at |path|.
+void RemoveAllStoredDocumentsAtPath(NSString* path) {
+  base::ScopedBlockingCall scoped_blocking_call(FROM_HERE,
+                                                base::BlockingType::WILL_BLOCK);
+
+  NSFileManager* file_manager = [NSFileManager defaultManager];
+
+  NSError* error = nil;
+  NSArray<NSString*>* document_files =
+      [file_manager contentsOfDirectoryAtPath:path error:&error];
+  if (!document_files) {
+    DLOG(ERROR) << "Failed to get content of directory at path: "
+                << base::SysNSStringToUTF8([error description]);
+    return;
+  }
+
+  for (NSString* filename in document_files) {
+    NSString* file_path = [path stringByAppendingPathComponent:filename];
+    if (![file_manager removeItemAtPath:file_path error:&error]) {
+      DLOG(ERROR) << "Failed to remove file: "
+                  << base::SysNSStringToUTF8([error description]);
+    }
+  }
+}
+
+// Ensures the destination directory is created and any contained obsolete files
+// are deleted. Returns YES if the directory is created successfully.
+BOOL CreateDestinationDirectoryAndRemoveObsoleteFiles() {
+  base::ScopedBlockingCall scoped_blocking_call(FROM_HERE,
+                                                base::BlockingType::WILL_BLOCK);
+
+  NSString* temporary_directory_path = GetTemporaryDocumentDirectory();
+  NSFileManager* file_manager = [NSFileManager defaultManager];
+
+  NSError* error = nil;
+  BOOL is_directory = NO;
+  if (![file_manager fileExistsAtPath:temporary_directory_path
+                          isDirectory:&is_directory]) {
+    BOOL created = [file_manager createDirectoryAtPath:temporary_directory_path
+                           withIntermediateDirectories:YES
+                                            attributes:nil
+                                                 error:&error];
+    DCHECK(created);
+    if (!created) {
+      DLOG(ERROR) << "Error creating destination dir: "
+                  << base::SysNSStringToUTF8([error description]);
+      return NO;
+    }
+  } else {
+    if (!is_directory) {
+      DLOG(ERROR) << "Destination Directory already exists and is a file.";
+      return NO;
+    }
+    // Remove all documents that might be still on temporary storage.
+    RemoveAllStoredDocumentsAtPath(temporary_directory_path);
+  }
+  return YES;
+}
+
 }  // anonymous namespace
 
 @interface OpenInController () <CRWWebViewScrollViewProxyObserver> {
@@ -106,10 +187,7 @@
 
 // SimpleURLLoader completion callback, when |urlLoader_| completes a request.
 - (void)urlLoadDidComplete:(const base::FilePath&)file_path;
-// Ensures the destination directory is created and any contained obsolete files
-// are deleted. Returns YES if the directory is created successfully.
-+ (BOOL)createDestinationDirectoryAndRemoveObsoleteFiles;
-// Starts downloading the file at path |kDocumentsTempPath| with the name
+// Starts downloading the file at path |kDocumentsTemporaryPath| with the name
 // |suggestedFilename_|.
 - (void)startDownload;
 // Shows the overlayed toolbar |openInToolbar_|. If |withTimer| is YES, it would
@@ -133,10 +211,6 @@
 - (void)showErrorWithMessage:(NSString*)message;
 // Presents the OpenIn menu for the file at |fileURL|.
 - (void)presentOpenInMenuForFileAtURL:(NSURL*)fileURL;
-// Removes the file at path |path|.
-- (void)removeDocumentAtPath:(NSString*)path;
-// Removes all the stored files at path |path|.
-+ (void)removeAllStoredDocumentsAtPath:(NSString*)path;
 // Shows an overlayed spinner on the top view to indicate that a file download
 // is in progress.
 - (void)showDownloadOverlayView;
@@ -145,48 +219,9 @@
 - (OpenInToolbar*)openInToolbar;
 @end
 
-// Bridge to deliver method calls from C++ to the |OpenInController| class.
-class OpenInControllerBridge
-    : public base::RefCountedThreadSafe<OpenInControllerBridge> {
- public:
-  explicit OpenInControllerBridge(OpenInController* owner) : owner_(owner) {}
-
-  BOOL CreateDestinationDirectoryAndRemoveObsoleteFiles(void) {
-    return [OpenInController createDestinationDirectoryAndRemoveObsoleteFiles];
-  }
-
-  void OnDestinationDirectoryCreated(BOOL success) {
-    DCHECK_CURRENTLY_ON(web::WebThread::UI);
-    if (!success)
-      [owner_ hideOpenInToolbar];
-    else
-      [owner_ startDownload];
-  }
-
-  void OnOwnerDisabled() {
-    // When the owner is disabled:
-    // - if there is a task in flight posted via |PostTaskAndReplyWithResult|
-    // then dereferencing |bridge_| will not release it as |bridge_| is also
-    // referenced by the task posting; setting |owner_| to nil makes sure that
-    // no methods are called on it, and it works since |owner_| is only used on
-    // the main thread.
-    // - if there is a task in flight posted by the URLFetcher then
-    // |OpenInController| destroys the fetcher and cancels the callback. This is
-    // why |OnURLFetchComplete| will neved be called after |owner_| is disabled.
-    owner_ = nil;
-  }
-
- protected:
-  friend base::RefCountedThreadSafe<OpenInControllerBridge>;
-  virtual ~OpenInControllerBridge() {}
-
- private:
-  __weak OpenInController* owner_;
-};
-
 @implementation OpenInController {
-  // Bridge from C++ to Obj-C class.
-  scoped_refptr<OpenInControllerBridge> _bridge;
+  // To check that callbacks are executed on the correct sequence.
+  SEQUENCE_CHECKER(_sequenceChecker);
 
   // URL of the document.
   GURL _documentURL;
@@ -213,6 +248,9 @@
   // |openInToolbar_| should be displayed.
   web::WebState* _webState;
 
+  // Browser used to display errors.
+  Browser* _browser;
+
   // URLLoaderFactory instance needed for URLLoader.
   scoped_refptr<network::SharedURLLoaderFactory> _urlLoaderFactory;
 
@@ -227,17 +265,20 @@
 
   // YES if the toolbar is displayed.
   BOOL _isOpenInToolbarDisplayed;
+
+  // YES if the workflow has been canceled.
+  BOOL _disabled;
 }
 
 @synthesize baseView = _baseView;
-@synthesize browser = _browser;
 @synthesize previousScrollViewOffset = _previousScrollViewOffset;
 
-- (id)initWithBaseViewController:(UIViewController*)baseViewController
-                URLLoaderFactory:
-                    (scoped_refptr<network::SharedURLLoaderFactory>)
-                        urlLoaderFactory
-                        webState:(web::WebState*)webState {
+- (instancetype)initWithBaseViewController:(UIViewController*)baseViewController
+                          URLLoaderFactory:
+                              (scoped_refptr<network::SharedURLLoaderFactory>)
+                                  urlLoaderFactory
+                                  webState:(web::WebState*)webState
+                                   browser:(Browser*)browser {
   self = [super init];
   if (self) {
     _baseViewController = baseViewController;
@@ -250,12 +291,14 @@
     _sequencedTaskRunner = base::ThreadPool::CreateSequencedTaskRunner(
         {base::MayBlock(), base::TaskPriority::BEST_EFFORT});
     _previousScrollViewOffset = 0;
+    _browser = browser;
   }
   return self;
 }
 
 - (void)enableWithDocumentURL:(const GURL&)documentURL
             suggestedFilename:(NSString*)suggestedFilename {
+  _disabled = NO;
   _documentURL = GURL(documentURL);
   _suggestedFilename = suggestedFilename;
   [self.baseView addGestureRecognizer:_tapRecognizer];
@@ -268,11 +311,9 @@
 }
 
 - (void)disable {
+  _disabled = YES;
   [self openInToolbar].alpha = 0.0f;
   [_openInTimer invalidate];
-  if (_bridge.get())
-    _bridge->OnOwnerDisabled();
-  _bridge = nil;
   [self.baseView removeGestureRecognizer:_tapRecognizer];
   if (_webState)
     [[_webState->GetWebViewProxy() scrollViewProxy] removeObserver:self];
@@ -285,9 +326,10 @@
 
 - (void)detachFromWebState {
   [self disable];
-  // Animation blocks may be keeping this object alive; don't extend the
-  // lifetime of WebState.
+  // Animation blocks may be keeping this object alive; don't keep a
+  // potentially dangling pointer to WebState and Browser.
   _webState = nullptr;
+  _browser = nullptr;
 }
 
 - (void)dealloc {
@@ -344,8 +386,8 @@
 }
 
 - (void)exportFileWithOpenInMenuAnchoredAt:(UIView*)view {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(_sequenceChecker);
   DCHECK([view isKindOfClass:[UIView class]]);
-  DCHECK_CURRENTLY_ON(web::WebThread::UI);
 
   base::RecordAction(base::UserMetricsAction("IOS.OpenIn.Tapped"));
 
@@ -355,25 +397,32 @@
   _anchorLocation = [[self openInToolbar] convertRect:view.frame
                                                toView:self.baseView];
   [_openInTimer invalidate];
-  if (!_bridge.get())
-    _bridge = new OpenInControllerBridge(self);
 
-  // This needs to be done in two steps, on two separate threads. The
-  // first task needs to be done on the worker pool and returns a BOOL which is
-  // then used in the second function, |OnDestinationDirectoryCreated|, which
-  // runs on the UI thread.
-  base::OnceCallback<BOOL(void)> task = base::BindOnce(
-      &OpenInControllerBridge::CreateDestinationDirectoryAndRemoveObsoleteFiles,
-      _bridge);
-  base::OnceCallback<void(BOOL)> reply = base::BindOnce(
-      &OpenInControllerBridge::OnDestinationDirectoryCreated, _bridge);
-  base::PostTaskAndReplyWithResult(_sequencedTaskRunner.get(), FROM_HERE,
-                                   std::move(task), std::move(reply));
+  // Creating the directory can block the main thread, so perform it on a
+  // background sequence, then on current sequence complete the workflow.
+  __weak OpenInController* weakSelf = self;
+  _sequencedTaskRunner->PostTaskAndReplyWithResult(
+      FROM_HERE,
+      base::BindOnce(&CreateDestinationDirectoryAndRemoveObsoleteFiles),
+      base::BindOnce(^(BOOL directoryCreated) {
+        [weakSelf onDestinationDirectoryCreated:directoryCreated];
+      }));
+}
+
+- (void)onDestinationDirectoryCreated:(BOOL)directoryCreated {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(_sequenceChecker);
+  if (_disabled)
+    return;
+
+  if (!directoryCreated) {
+    [self hideOpenInToolbar];
+  } else {
+    [self startDownload];
+  }
 }
 
 - (void)startDownload {
-  NSString* tempDirPath = [NSTemporaryDirectory()
-      stringByAppendingPathComponent:kDocumentsTempPath];
+  NSString* tempDirPath = GetTemporaryDocumentDirectory();
   NSString* filePath =
       [tempDirPath stringByAppendingPathComponent:_suggestedFilename];
 
@@ -386,10 +435,6 @@
   [self showDownloadOverlayView];
   _downloadCanceled = NO;
 
-  // Ensure |bridge_| is set in case this function is called from a unittest.
-  if (!_bridge.get())
-    _bridge = new OpenInControllerBridge(self);
-
   // Download the document and save it at |filePath|.
   auto resourceRequest = std::make_unique<network::ResourceRequest>();
   resourceRequest->url = _documentURL;
@@ -461,10 +506,9 @@
   activityViewController.completionWithItemsHandler =
       ^(NSString* activityType, BOOL completed, NSArray* returnedItems,
         NSError* activityError) {
-        weakSelf.sequencedTaskRunner->PostTask(
-            FROM_HERE, base::BindOnce(^{
-              [weakSelf removeDocumentAtPath:[fileURL path]];
-            }));
+        weakSelf.sequencedTaskRunner->PostTask(FROM_HERE, base::BindOnce(^{
+                                                 RemoveDocumentAtPath(fileURL);
+                                               }));
 
         if (IsIPadIdiom()) {
           _openInTimer = [NSTimer
@@ -558,72 +602,6 @@
 #pragma mark -
 #pragma mark File management
 
-- (void)removeDocumentAtPath:(nullable NSString*)path {
-  if (!path)
-    return;
-  base::ScopedBlockingCall scoped_blocking_call(FROM_HERE,
-                                                base::BlockingType::WILL_BLOCK);
-  NSFileManager* fileManager = [NSFileManager defaultManager];
-  NSError* error = nil;
-  if (![fileManager removeItemAtPath:path error:&error]) {
-    DLOG(ERROR) << "Failed to remove file: "
-                << base::SysNSStringToUTF8([error description]);
-  }
-}
-
-+ (void)removeAllStoredDocumentsAtPath:(NSString*)tempDirPath {
-  base::ScopedBlockingCall scoped_blocking_call(FROM_HERE,
-                                                base::BlockingType::WILL_BLOCK);
-  NSFileManager* fileManager = [NSFileManager defaultManager];
-  NSError* error = nil;
-  NSArray* documentFiles = [fileManager contentsOfDirectoryAtPath:tempDirPath
-                                                            error:&error];
-  if (!documentFiles) {
-    DLOG(ERROR) << "Failed to get content of directory at path: "
-                << base::SysNSStringToUTF8([error description]);
-    return;
-  }
-
-  for (NSString* filename in documentFiles) {
-    NSString* filePath = [tempDirPath stringByAppendingPathComponent:filename];
-    if (![fileManager removeItemAtPath:filePath error:&error]) {
-      DLOG(ERROR) << "Failed to remove file: "
-                  << base::SysNSStringToUTF8([error description]);
-    }
-  }
-}
-
-+ (BOOL)createDestinationDirectoryAndRemoveObsoleteFiles {
-  base::ScopedBlockingCall scoped_blocking_call(FROM_HERE,
-                                                base::BlockingType::WILL_BLOCK);
-  NSString* tempDirPath = [NSTemporaryDirectory()
-      stringByAppendingPathComponent:kDocumentsTempPath];
-  NSFileManager* fileManager = [NSFileManager defaultManager];
-  BOOL isDirectory;
-  NSError* error = nil;
-  if (![fileManager fileExistsAtPath:tempDirPath isDirectory:&isDirectory]) {
-    BOOL created = [fileManager createDirectoryAtPath:tempDirPath
-                          withIntermediateDirectories:YES
-                                           attributes:nil
-                                                error:&error];
-    DCHECK(created);
-    if (!created) {
-      DLOG(ERROR) << "Error creating destination dir: "
-                  << base::SysNSStringToUTF8([error description]);
-      return NO;
-    }
-  } else {
-    DCHECK(isDirectory);
-    if (!isDirectory) {
-      DLOG(ERROR) << "Destination Directory already exists and is a file.";
-      return NO;
-    }
-    // Remove all documents that might be still on temporary storage.
-    [self removeAllStoredDocumentsAtPath:(NSString*)tempDirPath];
-  }
-  return YES;
-}
-
 - (void)urlLoadDidComplete:(const base::FilePath&)filePath {
   NSURL* fileURL = nil;
   if (!filePath.empty())
@@ -634,7 +612,7 @@
     return;
   }
   _sequencedTaskRunner->PostTask(FROM_HERE, base::BindOnce(^{
-                                   [self removeDocumentAtPath:fileURL.path];
+                                   RemoveDocumentAtPath(fileURL);
                                  }));
   OpenInDownloadResult download_result = OpenInDownloadResult::kCanceled;
   if (!_downloadCanceled) {
diff --git a/ios/chrome/browser/ui/open_in/open_in_controller_unittest.mm b/ios/chrome/browser/ui/open_in/open_in_controller_unittest.mm
index 334c54a..933b1ab8 100644
--- a/ios/chrome/browser/ui/open_in/open_in_controller_unittest.mm
+++ b/ios/chrome/browser/ui/open_in/open_in_controller_unittest.mm
@@ -56,7 +56,8 @@
     open_in_controller_ = [[OpenInController alloc]
         initWithBaseViewController:base_view_controller
                   URLLoaderFactory:test_shared_url_loader_factory_
-                          webState:&web_state_];
+                          webState:&web_state_
+                           browser:nullptr];
     [open_in_controller_ enableWithDocumentURL:documentURL
                              suggestedFilename:@"doc.pdf"];
   }
diff --git a/ios/chrome/browser/ui/open_in/open_in_coordinator.mm b/ios/chrome/browser/ui/open_in/open_in_coordinator.mm
index 05f0a40..032b1e7 100644
--- a/ios/chrome/browser/ui/open_in/open_in_coordinator.mm
+++ b/ios/chrome/browser/ui/open_in/open_in_coordinator.mm
@@ -26,6 +26,7 @@
 }
 
 - (void)stop {
+  [self.openInMediator disableAll];
   self.openInMediator = nil;
 }
 
diff --git a/ios/chrome/browser/ui/open_in/open_in_mediator.mm b/ios/chrome/browser/ui/open_in/open_in_mediator.mm
index c44b25cf..34937cb 100644
--- a/ios/chrome/browser/ui/open_in/open_in_mediator.mm
+++ b/ios/chrome/browser/ui/open_in/open_in_mediator.mm
@@ -61,7 +61,7 @@
 
 - (void)disableAll {
   for (const auto& element : _openInControllersForWebStates)
-    [element.second disable];
+    [element.second detachFromWebState];
   _openInControllersForWebStates.clear();
 }
 
@@ -96,8 +96,8 @@
         initWithBaseViewController:_baseViewController
                   URLLoaderFactory:webState->GetBrowserState()
                                        ->GetSharedURLLoaderFactory()
-                          webState:webState];
-    openInController.browser = _browser;
+                          webState:webState
+                           browser:_browser];
     _openInControllersForWebStates[webState] = openInController;
   }
   OpenInController* controller = _openInControllersForWebStates[webState];
diff --git a/ios/chrome/browser/ui/overscroll_actions/BUILD.gn b/ios/chrome/browser/ui/overscroll_actions/BUILD.gn
index 64a408f5..53dd83f 100644
--- a/ios/chrome/browser/ui/overscroll_actions/BUILD.gn
+++ b/ios/chrome/browser/ui/overscroll_actions/BUILD.gn
@@ -3,10 +3,7 @@
 # found in the LICENSE file.
 
 source_set("overscroll_actions") {
-  configs += [
-    "//build/config/compiler:enable_arc",
-    "//build/config/ios:disable_implicit_retain_self_warning",
-  ]
+  configs += [ "//build/config/compiler:enable_arc" ]
   sources = [
     "overscroll_actions_controller.h",
     "overscroll_actions_controller.mm",
diff --git a/ios/chrome/browser/ui/overscroll_actions/overscroll_actions_controller.mm b/ios/chrome/browser/ui/overscroll_actions/overscroll_actions_controller.mm
index 0211e7e..c3cb5567 100644
--- a/ios/chrome/browser/ui/overscroll_actions/overscroll_actions_controller.mm
+++ b/ios/chrome/browser/ui/overscroll_actions/overscroll_actions_controller.mm
@@ -763,74 +763,71 @@
 
 - (void)onOverscrollStateChangeWithPreviousState:
     (OverscrollState)previousOverscrollState {
-  [UIView
-      animateWithDuration:0.2
-               animations:^{
-                 switch (self.overscrollState) {
-                   case OverscrollState::NO_PULL_STARTED: {
-                     [self.overscrollActionView removeFromSuperview];
+  __weak OverscrollActionsController* weakSelf = self;
+  [UIView animateWithDuration:0.2
+                   animations:^{
+                     [weakSelf
+                         animateOverscrollStateChange:previousOverscrollState];
+                   }
+                   completion:nil];
+}
+
+// Helper to animate onOverscrollStateChangeWithPreviousState
+- (void)animateOverscrollStateChange:(OverscrollState)previousOverscrollState {
+  switch (self.overscrollState) {
+    case OverscrollState::NO_PULL_STARTED: {
+      [self.overscrollActionView removeFromSuperview];
 #if !defined(__IPHONE_13_0) || __IPHONE_OS_VERSION_MIN_REQUIRED < __IPHONE_13_0
-                     CGRect statusBarFrame =
-                         [UIApplication sharedApplication].statusBarFrame;
+      CGRect statusBarFrame = [UIApplication sharedApplication].statusBarFrame;
 #else
-                     CGRect statusBarFrame =
-                         [self scrollView]
-                             .window.windowScene.statusBarManager
-                             .statusBarFrame;
+      CGRect statusBarFrame =
+          [self scrollView].window.windowScene.statusBarManager.statusBarFrame;
 #endif
-                     SetViewFrameHeight(
-                         self.overscrollActionView,
+      SetViewFrameHeight(self.overscrollActionView,
                          self.initialContentInset + statusBarFrame.size.height,
                          0);
-                     self.panPointScreenOrigin = CGPointZero;
-                     [[NSNotificationCenter defaultCenter]
-                         postNotificationName:kOverscrollActionsDidEnd
-                                       object:self];
-                     [self resetScrollViewTopContentInset];
-                     self.disablingFullscreen = NO;
-                     if (_shouldInvalidate) {
-                       [self invalidate];
-                     }
-                   } break;
-                   case OverscrollState::STARTED_PULLING: {
-                     if (!self.overscrollActionView.superview &&
-                         self.scrollViewDragged) {
-                       UIView* headerView = [self.delegate
-                           headerViewForOverscrollActionsController:self];
-                       DCHECK(headerView);
-                       if (previousOverscrollState ==
-                           OverscrollState::NO_PULL_STARTED) {
-                         UIView* view = [self.delegate
-                             toolbarSnapshotViewForOverscrollActionsController:
-                                 self];
-                         [self.overscrollActionView addSnapshotView:view];
-                         [[NSNotificationCenter defaultCenter]
-                             postNotificationName:kOverscrollActionsWillStart
-                                           object:self];
-                         self.disablingFullscreen = YES;
-                       }
-                       [CATransaction begin];
-                       [CATransaction setDisableActions:YES];
-                       self.overscrollActionView.backgroundView.alpha = 1;
-                       [self.overscrollActionView updateWithVerticalOffset:0];
-                       [self.overscrollActionView updateWithHorizontalOffset:0];
-                       self.overscrollActionView.frame = headerView.bounds;
-                       [headerView addSubview:self.overscrollActionView];
-                       [CATransaction commit];
-                     }
-                   } break;
-                   case OverscrollState::ACTION_READY: {
-                     _didTransitionToActionReady = YES;
-                     if (CGPointEqualToPoint(self.panPointScreenOrigin,
-                                             CGPointZero)) {
-                       CGPoint panPointScreen =
-                           [self.panGestureRecognizer locationInView:nil];
-                       self.panPointScreenOrigin = panPointScreen;
-                     }
-                   } break;
-                 }
-               }
-               completion:nil];
+      self.panPointScreenOrigin = CGPointZero;
+      [[NSNotificationCenter defaultCenter]
+          postNotificationName:kOverscrollActionsDidEnd
+                        object:self];
+      [self resetScrollViewTopContentInset];
+      self.disablingFullscreen = NO;
+      if (_shouldInvalidate) {
+        [self invalidate];
+      }
+    } break;
+    case OverscrollState::STARTED_PULLING: {
+      if (!self.overscrollActionView.superview && self.scrollViewDragged) {
+        UIView* headerView =
+            [self.delegate headerViewForOverscrollActionsController:self];
+        DCHECK(headerView);
+        if (previousOverscrollState == OverscrollState::NO_PULL_STARTED) {
+          UIView* view = [self.delegate
+              toolbarSnapshotViewForOverscrollActionsController:self];
+          [self.overscrollActionView addSnapshotView:view];
+          [[NSNotificationCenter defaultCenter]
+              postNotificationName:kOverscrollActionsWillStart
+                            object:self];
+          self.disablingFullscreen = YES;
+        }
+        [CATransaction begin];
+        [CATransaction setDisableActions:YES];
+        self.overscrollActionView.backgroundView.alpha = 1;
+        [self.overscrollActionView updateWithVerticalOffset:0];
+        [self.overscrollActionView updateWithHorizontalOffset:0];
+        self.overscrollActionView.frame = headerView.bounds;
+        [headerView addSubview:self.overscrollActionView];
+        [CATransaction commit];
+      }
+    } break;
+    case OverscrollState::ACTION_READY: {
+      _didTransitionToActionReady = YES;
+      if (CGPointEqualToPoint(self.panPointScreenOrigin, CGPointZero)) {
+        CGPoint panPointScreen = [self.panGestureRecognizer locationInView:nil];
+        self.panPointScreenOrigin = panPointScreen;
+      }
+    } break;
+  }
 }
 
 - (void)setWebViewInteractionEnabled:(BOOL)enabled {
diff --git a/ios/chrome/browser/ui/overscroll_actions/overscroll_actions_view.mm b/ios/chrome/browser/ui/overscroll_actions/overscroll_actions_view.mm
index 2c83a45..df945b1 100644
--- a/ios/chrome/browser/ui/overscroll_actions/overscroll_actions_view.mm
+++ b/ios/chrome/browser/ui/overscroll_actions/overscroll_actions_view.mm
@@ -395,18 +395,11 @@
 }
 
 - (void)displayActionAnimation {
+  __weak OverscrollActionsView* weakSelf = self;
   _animatingActionTrigger = YES;
   [CATransaction begin];
   [CATransaction setCompletionBlock:^{
-    _animatingActionTrigger = NO;
-    [CATransaction begin];
-    [CATransaction setDisableActions:YES];
-    // See comment below for why we manually set opacity to 0 and remove
-    // the animation.
-    self.selectionCircleLayer.opacity = 0;
-    [self.selectionCircleLayer removeAnimationForKey:@"opacity"];
-    [self onStateChange];
-    [CATransaction commit];
+    [weakSelf completionForDisplayActionAnimation];
   }];
 
   CABasicAnimation* scaleAnimation =
@@ -436,6 +429,18 @@
   [CATransaction commit];
 }
 
+- (void)completionForDisplayActionAnimation {
+  _animatingActionTrigger = NO;
+  [CATransaction begin];
+  [CATransaction setDisableActions:YES];
+  // See comment below for why we manually set opacity to 0 and remove
+  // the animation.
+  self.selectionCircleLayer.opacity = 0;
+  [self.selectionCircleLayer removeAnimationForKey:@"opacity"];
+  [self onStateChange];
+  [CATransaction commit];
+}
+
 - (void)layoutSubviews {
   [super layoutSubviews];
 
@@ -730,20 +735,10 @@
       _animatingActionTrigger)
     return;
 
+  __weak OverscrollActionsView* weakSelf = self;
   [UIView animateWithDuration:kSelectionSnappingAnimationDuration
                    animations:^{
-                     if (self.selectedAction == OverscrollAction::NONE) {
-                       if (!_deformationBehaviorEnabled) {
-                         // Scale selection down.
-                         self.selectionCircleLayer.transform =
-                             CATransform3DMakeScale(kSelectionDownScale,
-                                                    kSelectionDownScale, 1);
-                       }
-                     } else {
-                       // Scale selection up.
-                       self.selectionCircleLayer.transform =
-                           CATransform3DMakeScale(1, 1, 1);
-                     }
+                     [weakSelf animateSelectedActionChanged];
                    }
                    completion:nil];
 
@@ -751,6 +746,20 @@
                selectedActionDidChange:self.selectedAction];
 }
 
+// Animation handler for onSelectedActionChangedFromAction
+- (void)animateSelectedActionChanged {
+  if (self.selectedAction == OverscrollAction::NONE) {
+    if (!_deformationBehaviorEnabled) {
+      // Scale selection down.
+      self.selectionCircleLayer.transform =
+          CATransform3DMakeScale(kSelectionDownScale, kSelectionDownScale, 1);
+    }
+  } else {
+    // Scale selection up.
+    self.selectionCircleLayer.transform = CATransform3DMakeScale(1, 1, 1);
+  }
+}
+
 - (NSArray*)layersToCenterVertically {
   if (!_layersToCenterVertically) {
     _layersToCenterVertically = @[
@@ -987,11 +996,15 @@
 - (void)clearDirectTouchInteraction {
   if (!_viewTouched)
     return;
+  __weak OverscrollActionsView* weakSelf = self;
   dispatch_after(
       dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.1 * NSEC_PER_SEC)),
       dispatch_get_main_queue(), ^{
-        _deformationBehaviorEnabled = YES;
-        _viewTouched = NO;
+        OverscrollActionsView* strongSelf = weakSelf;
+        if (strongSelf) {
+          strongSelf->_deformationBehaviorEnabled = YES;
+          strongSelf->_viewTouched = NO;
+        }
       });
 }
 
diff --git a/ios/chrome/browser/ui/tab_switcher/tab_grid/tab_grid_thumb_strip_egtest.mm b/ios/chrome/browser/ui/tab_switcher/tab_grid/tab_grid_thumb_strip_egtest.mm
index 6697dfb..12feff8 100644
--- a/ios/chrome/browser/ui/tab_switcher/tab_grid/tab_grid_thumb_strip_egtest.mm
+++ b/ios/chrome/browser/ui/tab_switcher/tab_grid/tab_grid_thumb_strip_egtest.mm
@@ -23,7 +23,6 @@
 #error "This file requires ARC support."
 #endif
 
-using chrome_test_util::ContentSuggestionCollectionView;
 using chrome_test_util::PrimaryToolbar;
 using chrome_test_util::RegularTabGrid;
 using chrome_test_util::TabGridBackground;
diff --git a/ios/chrome/browser/ui/tab_switcher/tab_grid/tab_grid_view_controller.mm b/ios/chrome/browser/ui/tab_switcher/tab_grid/tab_grid_view_controller.mm
index f165530..60cedb3 100644
--- a/ios/chrome/browser/ui/tab_switcher/tab_grid/tab_grid_view_controller.mm
+++ b/ios/chrome/browser/ui/tab_switcher/tab_grid/tab_grid_view_controller.mm
@@ -1511,6 +1511,7 @@
     case TabGridPageRemoteTabs:
       base::RecordAction(
           base::UserMetricsAction("MobileTabGridSelectRemotePanel"));
+      LogLikelyInterestedDefaultBrowserUserActivity(DefaultPromoTypeAllTabs);
       break;
   }
   switch (self.pageChangeInteraction) {
diff --git a/ios/chrome/browser/ui/thumb_strip/thumb_strip_egtest.mm b/ios/chrome/browser/ui/thumb_strip/thumb_strip_egtest.mm
index 192a3b0..684d48f 100644
--- a/ios/chrome/browser/ui/thumb_strip/thumb_strip_egtest.mm
+++ b/ios/chrome/browser/ui/thumb_strip/thumb_strip_egtest.mm
@@ -22,7 +22,7 @@
 #error "This file requires ARC support."
 #endif
 
-using chrome_test_util::ContentSuggestionCollectionView;
+using chrome_test_util::NTPCollectionView;
 using chrome_test_util::PrimaryToolbar;
 using chrome_test_util::WebStateScrollViewMatcher;
 
@@ -228,7 +228,7 @@
   }
 
   // Scroll the NTP to reveal the thumb strip.
-  [[EarlGrey selectElementWithMatcher:ContentSuggestionCollectionView()]
+  [[EarlGrey selectElementWithMatcher:NTPCollectionView()]
       performAction:grey_swipeSlowInDirection(kGREYDirectionDown)];
 
   // Make sure that the entire tab thumbnail is fully visible and not covered.
@@ -240,7 +240,7 @@
       assertWithMatcher:grey_minimumVisiblePercent(1)];
 
   // Scroll the NTP the other way to close the thumb strip.
-  [[EarlGrey selectElementWithMatcher:ContentSuggestionCollectionView()]
+  [[EarlGrey selectElementWithMatcher:NTPCollectionView()]
       performAction:grey_swipeSlowInDirection(kGREYDirectionUp)];
 
   // Make sure that the tab thumbnail is not visible.
diff --git a/ios/chrome/browser/web/navigation_egtest.mm b/ios/chrome/browser/web/navigation_egtest.mm
index d9bbb064..578441ee 100644
--- a/ios/chrome/browser/web/navigation_egtest.mm
+++ b/ios/chrome/browser/web/navigation_egtest.mm
@@ -21,7 +21,7 @@
 #endif
 
 using base::test::ios::kWaitForUIElementTimeout;
-using chrome_test_util::ContentSuggestionCollectionView;
+using chrome_test_util::NTPCollectionView;
 using chrome_test_util::BackButton;
 using chrome_test_util::ForwardButton;
 using chrome_test_util::OmniboxText;
@@ -325,7 +325,7 @@
   // Tap the back button and verify NTP is loaded.
   [[EarlGrey selectElementWithMatcher:BackButton()] performAction:grey_tap()];
   [ChromeEarlGrey waitForPageToFinishLoading];
-  [[EarlGrey selectElementWithMatcher:ContentSuggestionCollectionView()]
+  [[EarlGrey selectElementWithMatcher:NTPCollectionView()]
       assertWithMatcher:grey_notNil()];
 
   // Tap the forward button and verify test page is loaded.
diff --git a/ios/chrome/browser/web/restore_egtest.mm b/ios/chrome/browser/web/restore_egtest.mm
index 63d2231..2ab7e8b 100644
--- a/ios/chrome/browser/web/restore_egtest.mm
+++ b/ios/chrome/browser/web/restore_egtest.mm
@@ -21,7 +21,7 @@
 #endif
 
 using chrome_test_util::OmniboxText;
-using chrome_test_util::ContentSuggestionCollectionView;
+using chrome_test_util::NTPCollectionView;
 using chrome_test_util::BackButton;
 using chrome_test_util::ForwardButton;
 
@@ -345,10 +345,10 @@
   [ChromeEarlGrey waitForPageToFinishLoading];
 
   // Confirm the NTP is still at the start.
-  [[EarlGrey selectElementWithMatcher:ContentSuggestionCollectionView()]
+  [[EarlGrey selectElementWithMatcher:NTPCollectionView()]
       assertWithMatcher:grey_notNil()];
   [self triggerRestore];
-  [[EarlGrey selectElementWithMatcher:ContentSuggestionCollectionView()]
+  [[EarlGrey selectElementWithMatcher:NTPCollectionView()]
       assertWithMatcher:grey_notNil()];
 }
 
diff --git a/ios/chrome/common/ui/confirmation_alert/confirmation_alert_view_controller.mm b/ios/chrome/common/ui/confirmation_alert/confirmation_alert_view_controller.mm
index 7108cc1..5c105f7 100644
--- a/ios/chrome/common/ui/confirmation_alert/confirmation_alert_view_controller.mm
+++ b/ios/chrome/common/ui/confirmation_alert/confirmation_alert_view_controller.mm
@@ -556,6 +556,7 @@
                        forState:UIControlStateNormal];
   primaryActionButton.accessibilityIdentifier =
       kConfirmationAlertPrimaryActionAccessibilityIdentifier;
+  primaryActionButton.titleLabel.adjustsFontSizeToFitWidth = YES;
 
   return primaryActionButton;
 }
@@ -582,6 +583,7 @@
   secondaryActionButton.translatesAutoresizingMaskIntoConstraints = NO;
   secondaryActionButton.accessibilityIdentifier =
       kConfirmationAlertSecondaryActionAccessibilityIdentifier;
+  secondaryActionButton.titleLabel.adjustsFontSizeToFitWidth = YES;
 
   if (@available(iOS 13.4, *)) {
     if (self.pointerInteractionEnabled) {
diff --git a/ios/chrome/search_widget_extension/strings/ios_search_widget_extension_strings.grd b/ios/chrome/search_widget_extension/strings/ios_search_widget_extension_strings.grd
index 26137db..c091bfc3 100644
--- a/ios/chrome/search_widget_extension/strings/ios_search_widget_extension_strings.grd
+++ b/ios/chrome/search_widget_extension/strings/ios_search_widget_extension_strings.grd
@@ -164,7 +164,7 @@
       <message name="IDS_IOS_NEW_SEARCH" desc="Label in the widget extension for the action to launch the app and open a new search. [Length: 30em]">
         New Search
       </message>
-      <message name="IDS_IOS_INCOGNITO_SEARCH" desc="Label in the widget extension for the action to launch the app and open a new incognito search. [Length: 30em]">
+      <message name="IDS_IOS_INCOGNITO_SEARCH" desc="Label in the widget extension for the action to launch the app and open a new Incognito search. [Length: 30em]">
         Incognito Search
       </message>
       <message name="IDS_IOS_VOICE_SEARCH" desc="Label in the widget extension for the action to launch the app and open a new voice search. [Length: 30em]">
diff --git a/ios/chrome/test/earl_grey/BUILD.gn b/ios/chrome/test/earl_grey/BUILD.gn
index 436b543..c7818b9 100644
--- a/ios/chrome/test/earl_grey/BUILD.gn
+++ b/ios/chrome/test/earl_grey/BUILD.gn
@@ -29,6 +29,7 @@
     "//base",
     "//components/policy/core/common:test_support",
     "//ios/chrome/app:tests_hook",
+    "//ios/chrome/browser",
     "//ios/chrome/browser/policy:test_support",
   ]
 }
@@ -112,6 +113,7 @@
     "//ios/chrome/browser/ui/location_bar:location_bar",
     "//ios/chrome/browser/ui/main:scene_state_header",
     "//ios/chrome/browser/ui/material_components",
+    "//ios/chrome/browser/ui/ntp",
     "//ios/chrome/browser/ui/omnibox:app_support+eg2",
     "//ios/chrome/browser/ui/omnibox:omnibox_internal",
     "//ios/chrome/browser/ui/omnibox/keyboard_assist",
diff --git a/ios/chrome/test/earl_grey/chrome_matchers.h b/ios/chrome/test/earl_grey/chrome_matchers.h
index be2e4d3..70a4a42 100644
--- a/ios/chrome/test/earl_grey/chrome_matchers.h
+++ b/ios/chrome/test/earl_grey/chrome_matchers.h
@@ -321,7 +321,10 @@
 id<GREYMatcher> ClearAutofillButton();
 
 // Returns matcher for the collection view of content suggestion.
-id<GREYMatcher> ContentSuggestionCollectionView();
+id<GREYMatcher> ContentSuggestionsCollectionView();
+
+// Returns matcher for the collection view of the NTP.
+id<GREYMatcher> NTPCollectionView();
 
 // Returns matcher for the warning message while filling in payment requests.
 id<GREYMatcher> WarningMessageView();
diff --git a/ios/chrome/test/earl_grey/chrome_matchers.mm b/ios/chrome/test/earl_grey/chrome_matchers.mm
index ccf560f..c14e0ec 100644
--- a/ios/chrome/test/earl_grey/chrome_matchers.mm
+++ b/ios/chrome/test/earl_grey/chrome_matchers.mm
@@ -404,10 +404,14 @@
   return [ChromeMatchersAppInterface clearAutofillButton];
 }
 
-id<GREYMatcher> ContentSuggestionCollectionView() {
+id<GREYMatcher> ContentSuggestionsCollectionView() {
   return [ChromeMatchersAppInterface contentSuggestionCollectionView];
 }
 
+id<GREYMatcher> NTPCollectionView() {
+  return [ChromeMatchersAppInterface ntpCollectionView];
+}
+
 id<GREYMatcher> WarningMessageView() {
   return [ChromeMatchersAppInterface warningMessageView];
 }
diff --git a/ios/chrome/test/earl_grey/chrome_matchers_app_interface.h b/ios/chrome/test/earl_grey/chrome_matchers_app_interface.h
index f6f30ad..3c0229d 100644
--- a/ios/chrome/test/earl_grey/chrome_matchers_app_interface.h
+++ b/ios/chrome/test/earl_grey/chrome_matchers_app_interface.h
@@ -318,6 +318,9 @@
 // Returns matcher for the collection view of content suggestion.
 + (id<GREYMatcher>)contentSuggestionCollectionView;
 
+// Returns matcher for the collection view of the NTP.
++ (id<GREYMatcher>)ntpCollectionView;
+
 // Returns matcher for the warning message while filling in payment requests.
 + (id<GREYMatcher>)warningMessageView;
 
diff --git a/ios/chrome/test/earl_grey/chrome_matchers_app_interface.mm b/ios/chrome/test/earl_grey/chrome_matchers_app_interface.mm
index 42fb4bd4d..e654edfc 100644
--- a/ios/chrome/test/earl_grey/chrome_matchers_app_interface.mm
+++ b/ios/chrome/test/earl_grey/chrome_matchers_app_interface.mm
@@ -27,6 +27,7 @@
 #import "ios/chrome/browser/ui/history/history_ui_constants.h"
 #import "ios/chrome/browser/ui/location_bar/location_bar_constants.h"
 #import "ios/chrome/browser/ui/location_bar/location_bar_steady_view.h"
+#import "ios/chrome/browser/ui/ntp/new_tab_page_constants.h"
 #import "ios/chrome/browser/ui/omnibox/keyboard_assist/omnibox_assistive_keyboard_views_utils.h"
 #import "ios/chrome/browser/ui/omnibox/omnibox_text_field_ios.h"
 #import "ios/chrome/browser/ui/popup_menu/popup_menu_constants.h"
@@ -682,6 +683,10 @@
   return grey_accessibilityID(kContentSuggestionsCollectionIdentifier);
 }
 
++ (id<GREYMatcher>)ntpCollectionView {
+  return grey_accessibilityID(kNTPCollectionViewIdentifier);
+}
+
 // TODO(crbug.com/1021752): Remove this stub.
 + (id<GREYMatcher>)warningMessageView {
   return nil;
diff --git a/ios/chrome/test/earl_grey/eg_tests_hook.mm b/ios/chrome/test/earl_grey/eg_tests_hook.mm
index 871cec2d..bb861b33 100644
--- a/ios/chrome/test/earl_grey/eg_tests_hook.mm
+++ b/ios/chrome/test/earl_grey/eg_tests_hook.mm
@@ -6,6 +6,7 @@
 
 #include "base/command_line.h"
 #include "base/logging.h"
+#include "ios/chrome/browser/chrome_switches.h"
 #include "ios/chrome/browser/policy/test_platform_policy_provider.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
@@ -23,7 +24,8 @@
 }
 
 bool DisableDiscoverFeed() {
-  return true;
+  return !base::CommandLine::ForCurrentProcess()->HasSwitch(
+      switches::kEnableDiscoverFeed);
 }
 
 bool DisableFirstRun() {
diff --git a/ios/chrome/widget_kit_extension/strings/ios_widget_kit_extension_strings.grd b/ios/chrome/widget_kit_extension/strings/ios_widget_kit_extension_strings.grd
index 46dd0ae..9c9f5bd9 100644
--- a/ios/chrome/widget_kit_extension/strings/ios_widget_kit_extension_strings.grd
+++ b/ios/chrome/widget_kit_extension/strings/ios_widget_kit_extension_strings.grd
@@ -185,7 +185,7 @@
       <message name="IDS_IOS_WIDGET_KIT_EXTENSION_QUICK_ACTIONS_SEARCH_A11Y_LABEL" desc="Accessibility label for the search action in the quick actions widget" meaning="Accessibility label for the search action in the quick actions widget [CHAR_LIMIT=40][iOS only]">
         Search or type URL
       </message>
-      <message name="IDS_IOS_WIDGET_KIT_EXTENSION_QUICK_ACTIONS_INCOGNITO_A11Y_LABEL" desc="Accessibility label for the incognito search in the quick actions widget" meaning="Accessibility label for the incognito search action in the quick actions widget [CHAR_LIMIT=40][iOS only]">
+      <message name="IDS_IOS_WIDGET_KIT_EXTENSION_QUICK_ACTIONS_INCOGNITO_A11Y_LABEL" desc="Accessibility label for the Incognito search in the quick actions widget" meaning="Accessibility label for the Incognito search action in the quick actions widget [CHAR_LIMIT=40][iOS only]">
         Incognito Search
       </message>
       <message name="IDS_IOS_WIDGET_KIT_EXTENSION_QUICK_ACTIONS_QR_SCAN_A11Y_LABEL" desc="Accessibility label for the QR scan action in the quick actions widget" meaning="Accessibility label for the QR search action in the quick actions widget [CHAR_LIMIT=40][iOS only]">
diff --git a/ios/showcase/content_suggestions/BUILD.gn b/ios/showcase/content_suggestions/BUILD.gn
index f037ddb..695877a 100644
--- a/ios/showcase/content_suggestions/BUILD.gn
+++ b/ios/showcase/content_suggestions/BUILD.gn
@@ -18,6 +18,7 @@
     "//components/strings",
     "//ios/chrome/app/strings",
     "//ios/chrome/browser/ui/collection_view/cells",
+    "//ios/chrome/browser/ui/content_suggestions:constants",
     "//ios/chrome/browser/ui/content_suggestions:content_suggestions_ui",
     "//ios/chrome/browser/ui/content_suggestions/cells:cells_ui",
     "//ios/chrome/browser/ui/content_suggestions/identifier",
@@ -44,6 +45,7 @@
     "//ios/chrome/app/strings",
     "//ios/chrome/browser/ui/content_suggestions:constants",
     "//ios/chrome/browser/ui/content_suggestions:feature_flags",
+    "//ios/chrome/browser/ui/ntp:constants",
     "//ios/showcase/test:eg2_test",
     "//ios/testing/earl_grey:eg_test_support+eg2",
     "//ios/third_party/earl_grey2:test_lib",
diff --git a/ios/showcase/content_suggestions/sc_content_suggestions_coordinator.mm b/ios/showcase/content_suggestions/sc_content_suggestions_coordinator.mm
index 8ed6c0b6..683c4cd 100644
--- a/ios/showcase/content_suggestions/sc_content_suggestions_coordinator.mm
+++ b/ios/showcase/content_suggestions/sc_content_suggestions_coordinator.mm
@@ -5,6 +5,7 @@
 #import "ios/showcase/content_suggestions/sc_content_suggestions_coordinator.h"
 
 #import "ios/chrome/browser/ui/content_suggestions/content_suggestions_commands.h"
+#import "ios/chrome/browser/ui/content_suggestions/content_suggestions_constants.h"
 #import "ios/chrome/browser/ui/content_suggestions/content_suggestions_view_controller.h"
 #import "ios/showcase/common/protocol_alerter.h"
 #import "ios/showcase/content_suggestions/sc_content_suggestions_data_source.h"
@@ -39,9 +40,11 @@
   _dataSource = [[SCContentSuggestionsDataSource alloc] init];
 
   _suggestionViewController = [[ContentSuggestionsViewController alloc]
-      initWithStyle:CollectionViewControllerStyleDefault
-             offset:0
-        feedVisible:NO];
+              initWithStyle:CollectionViewControllerStyleDefault
+                     offset:0
+      refactoredFeedVisible:NO];
+  _suggestionViewController.collectionView.accessibilityIdentifier =
+      kContentSuggestionsCollectionIdentifier;
   [_suggestionViewController setDataSource:_dataSource];
 
   _suggestionViewController.suggestionCommandHandler =
diff --git a/ios/showcase/content_suggestions/sc_content_suggestions_egtest.mm b/ios/showcase/content_suggestions/sc_content_suggestions_egtest.mm
index 825870a2..ffff846 100644
--- a/ios/showcase/content_suggestions/sc_content_suggestions_egtest.mm
+++ b/ios/showcase/content_suggestions/sc_content_suggestions_egtest.mm
@@ -5,6 +5,7 @@
 #include "base/ios/ios_util.h"
 #include "components/strings/grit/components_strings.h"
 #import "ios/chrome/browser/ui/content_suggestions/content_suggestions_constants.h"
+#import "ios/chrome/browser/ui/ntp/new_tab_page_constants.h"
 #include "ios/chrome/grit/ios_strings.h"
 #import "ios/showcase/test/showcase_eg_utils.h"
 #import "ios/showcase/test/showcase_test_case.h"
diff --git a/ios/testing/perf/BUILD.gn b/ios/testing/perf/BUILD.gn
deleted file mode 100644
index e081e26..0000000
--- a/ios/testing/perf/BUILD.gn
+++ /dev/null
@@ -1,12 +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.
-
-source_set("startup") {
-  sources = [
-    "startupLoggers.h",
-    "startupLoggers.mm",
-  ]
-  deps = [ "//base" ]
-  configs += [ "//build/config/compiler:enable_arc" ]
-}
diff --git a/ios/testing/perf/startupLoggers.h b/ios/testing/perf/startupLoggers.h
deleted file mode 100644
index d16a2b2..0000000
--- a/ios/testing/perf/startupLoggers.h
+++ /dev/null
@@ -1,23 +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.
-
-#import <Foundation/Foundation.h>
-
-namespace startup_loggers {
-
-// Registers the current time variable. This function should be called when the
-// app is launched.
-void RegisterAppStartTime();
-// Registers the current time variable. This function should be called when the
-// app gets didFinishLaunchingWithOptions notification.
-void RegisterAppDidFinishLaunchingTime();
-// Registers the current time variable. Chrome does some lauch option steps
-// after the app gets didFinishLaunchingWithOptions notification. This function
-// should be called when the app gets applicationDidBecomeActive notification.
-void RegisterAppDidBecomeActiveTime();
-// Returns whether data is successfully stored in the output json file.  This
-// function stores the time variables into a json file.
-bool LogData(NSString* testName);
-
-}  // namespace startup_loggers
diff --git a/ios/testing/perf/startupLoggers.mm b/ios/testing/perf/startupLoggers.mm
deleted file mode 100644
index b4c3eee7..0000000
--- a/ios/testing/perf/startupLoggers.mm
+++ /dev/null
@@ -1,66 +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.
-
-#import "ios/testing/perf/startupLoggers.h"
-#include "base/time/time.h"
-
-#if !defined(__has_feature) || !__has_feature(objc_arc)
-#error "This file requires ARC support."
-#endif
-
-namespace startup_loggers {
-// Stores the time of app startup states.
-base::Time* g_start_time;
-base::Time* g_finish_launching_time;
-base::Time* g_become_active_time;
-
-void RegisterAppStartTime() {
-  DCHECK(!g_start_time);
-  g_start_time = new base::Time(base::Time::Now());
-}
-
-void RegisterAppDidFinishLaunchingTime() {
-  g_finish_launching_time = new base::Time(base::Time::Now());
-}
-
-void RegisterAppDidBecomeActiveTime() {
-  g_become_active_time = new base::Time(base::Time::Now());
-}
-
-bool LogData(NSString* testName) {
-  // Store the data into a format compatible with infra scripts.
-  double finishLaunchingDuration =
-      g_finish_launching_time->ToDoubleT() - g_start_time->ToDoubleT();
-  double becomeActiveDuration =
-      g_become_active_time->ToDoubleT() - g_start_time->ToDoubleT();
-  NSDictionary* values = @{
-    @"AppDidFinishLaunchingTime" : @(finishLaunchingDuration),
-    @"AppDidBecomeActiveTime" : @(becomeActiveDuration)
-  };
-  NSDictionary* timingData =
-      @{ testName : @{@"unit" : @"seconds", @"value" : values} };
-  NSDictionary* summary = @{@"Perf Data" : timingData};
-
-  // Converts data into json format.
-  NSError* error;
-  NSData* jsonData =
-      [NSJSONSerialization dataWithJSONObject:summary
-                                      options:NSJSONWritingPrettyPrinted
-                                        error:&error];
-  if (error) {
-    return false;
-  }
-
-  // Stores data into a json file under the app's document directory.
-  NSString* fileName = @"perf_result.json";
-  NSArray<NSString*>* outputDirectories = NSSearchPathForDirectoriesInDomains(
-      NSDocumentDirectory, NSUserDomainMask, YES);
-  if ([outputDirectories count] == 0) {
-    return false;
-  }
-  NSString* outputPath =
-      [outputDirectories[0] stringByAppendingPathComponent:fileName];
-  return [jsonData writeToFile:outputPath atomically:YES];
-}
-}
diff --git a/ios/web/navigation/crw_wk_navigation_handler.mm b/ios/web/navigation/crw_wk_navigation_handler.mm
index 67b3d2b..ea5a0ad 100644
--- a/ios/web/navigation/crw_wk_navigation_handler.mm
+++ b/ios/web/navigation/crw_wk_navigation_handler.mm
@@ -357,6 +357,13 @@
       self.webStateImpl->GetNavigationManager()->LoadURLWithParams(loadParams);
       return;
     }
+    // On iOS 12, we allow the navigation since cancelling it here causes
+    // crbug.com/965067. The underlying issue is a WebKit bug that converts
+    // valid URLs into invalid ones. This issue is fixed in iOS 13.
+    if (@available(iOS 13, *)) {
+      decisionHandler(WKNavigationActionPolicyCancel);
+      return;
+    }
   }
 
   // First check if the navigation action should be blocked by the controller
diff --git a/ios/web/web_state/web_state_observer_inttest.mm b/ios/web/web_state/web_state_observer_inttest.mm
index 32365b25..f56e150 100644
--- a/ios/web/web_state/web_state_observer_inttest.mm
+++ b/ios/web/web_state/web_state_observer_inttest.mm
@@ -1025,6 +1025,53 @@
   EXPECT_EQ(item->GetTitle(), base::UTF8ToUTF16(kFailedTitle));
 }
 
+// Tests that navigation to an invalid URL is disallowed.
+TEST_F(WebStateObserverTest, InvalidURL) {
+  // Navigations to invalid URLs are allowed on iOS 12 (see crbug.com/965067).
+  if (!base::ios::IsRunningOnIOS13OrLater()) {
+    return;
+  }
+
+  const GURL url = test_server_->GetURL("/echoall");
+
+  // Perform new page navigation.
+  NavigationContext* context = nullptr;
+  int32_t nav_id = 0;
+  EXPECT_CALL(observer_, DidStartLoading(web_state()));
+  WebStatePolicyDecider::RequestInfo expected_request_info(
+      ui::PageTransition::PAGE_TRANSITION_TYPED,
+      /*target_main_frame=*/true, /*target_frame_is_cross_origin=*/false,
+      /*has_user_gesture=*/false);
+  EXPECT_CALL(*decider_,
+              ShouldAllowRequest(_, RequestInfoMatch(expected_request_info)))
+      .WillOnce(Return(WebStatePolicyDecider::PolicyDecision::Allow()));
+  EXPECT_CALL(observer_, DidStartNavigation(web_state(), _))
+      .WillOnce(VerifyPageStartedContext(
+          web_state(), url, ui::PageTransition::PAGE_TRANSITION_TYPED, &context,
+          &nav_id));
+  EXPECT_CALL(*decider_, ShouldAllowResponse(_, /*for_main_frame=*/true, _))
+      .WillOnce(
+          RunOnceCallback<2>(WebStatePolicyDecider::PolicyDecision::Allow()));
+  EXPECT_CALL(observer_, DidFinishNavigation(web_state(), _))
+      .WillOnce(VerifyNewPageFinishedContext(
+          web_state(), url, kExpectedMimeType, /*content_is_html=*/true,
+          &context, &nav_id));
+  EXPECT_CALL(observer_, TitleWasSet(web_state()))
+      .WillOnce(VerifyTitle(url.GetContent()));
+  EXPECT_CALL(observer_, TitleWasSet(web_state()))
+      .WillOnce(VerifyTitle("EmbeddedTestServer - EchoAll"));
+  EXPECT_CALL(observer_, DidStopLoading(web_state()));
+  EXPECT_CALL(observer_,
+              PageLoaded(web_state(), PageLoadCompletionStatus::SUCCESS));
+  ASSERT_TRUE(LoadUrl(url));
+
+  // Navigate to an invalid URL using JavaScript.
+  // There should be no calls to WebStatePolicyDecider, since the navigation
+  // should get cancelled before that is reached.
+  EXPECT_CALL(*decider_, ShouldAllowRequest(_, _)).Times(0);
+  ExecuteJavaScript(@"window.location.pathname = '/%00%50'");
+}
+
 // Tests navigation to a URL with /..; suffix. On iOS 12 and earlier this
 // navigation fails becasue WebKit rewrites valid URL to invalid during the
 // navigation. On iOS 13+ this navigation sucessfully completes.
diff --git a/media/base/status_codes.h b/media/base/status_codes.h
index 70078b0..45eee646 100644
--- a/media/base/status_codes.h
+++ b/media/base/status_codes.h
@@ -94,7 +94,6 @@
   kGetQuantBufferFailed = 0x00000328,
   kReleaseQuantBufferFailed = 0x00000329,
   kBitstreamBufferSliceTooBig = 0x00000330,
-  kCreateSharedImageFailed = 0x00000331,
 
   // MojoDecoder Errors: 0x04
   kMojoDecoderNoWrappedDecoder = 0x00000401,
diff --git a/media/cdm/BUILD.gn b/media/cdm/BUILD.gn
index 44cd592..a8c81b7 100644
--- a/media/cdm/BUILD.gn
+++ b/media/cdm/BUILD.gn
@@ -51,6 +51,8 @@
     "aes_decryptor.h",
     "cbcs_decryptor.cc",
     "cbcs_decryptor.h",
+    "cdm_capability.cc",
+    "cdm_capability.h",
     "cdm_context_ref_impl.cc",
     "cdm_context_ref_impl.h",
     "cenc_decryptor.cc",
diff --git a/media/cdm/cdm_capability.cc b/media/cdm/cdm_capability.cc
new file mode 100644
index 0000000..da043fae
--- /dev/null
+++ b/media/cdm/cdm_capability.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 "media/cdm/cdm_capability.h"
+
+#include <utility>
+
+namespace media {
+
+CdmCapability::CdmCapability() = default;
+
+CdmCapability::CdmCapability(
+    std::vector<VideoCodec> video_codecs,
+    base::flat_set<EncryptionScheme> encryption_schemes,
+    base::flat_set<CdmSessionType> session_types)
+    : video_codecs(std::move(video_codecs)),
+      encryption_schemes(std::move(encryption_schemes)),
+      session_types(std::move(session_types)) {}
+
+CdmCapability::CdmCapability(const CdmCapability& other) = default;
+
+CdmCapability::~CdmCapability() = default;
+
+}  // namespace media
diff --git a/media/cdm/cdm_capability.h b/media/cdm/cdm_capability.h
new file mode 100644
index 0000000..4b83182
--- /dev/null
+++ b/media/cdm/cdm_capability.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 MEDIA_CDM_CDM_CAPABILITY_H_
+#define MEDIA_CDM_CDM_CAPABILITY_H_
+
+#include <vector>
+
+#include "base/containers/flat_set.h"
+#include "media/base/content_decryption_module.h"
+#include "media/base/encryption_scheme.h"
+#include "media/base/media_export.h"
+#include "media/base/video_codecs.h"
+
+namespace media {
+
+// Capabilities supported by a Content Decryption Module.
+struct MEDIA_EXPORT CdmCapability {
+  CdmCapability();
+  CdmCapability(std::vector<VideoCodec> video_codecs,
+                base::flat_set<EncryptionScheme> encryption_schemes,
+                base::flat_set<CdmSessionType> session_types);
+  CdmCapability(const CdmCapability& other);
+  ~CdmCapability();
+
+  // List of video codecs supported by the CDM (e.g. vp8). This is the set of
+  // codecs supported by the media pipeline using the CDM, where CDM does the
+  // decryption, and the media pipeline does decoding. As this is generic, not
+  // all profiles or levels of the specified codecs may actually be supported.
+  // TODO(crbug.com/796725) Find a way to include profiles and levels.
+  std::vector<VideoCodec> video_codecs;
+
+  // List of encryption schemes supported by the CDM (e.g. cenc).
+  base::flat_set<EncryptionScheme> encryption_schemes;
+
+  // List of session types supported by the CDM.
+  base::flat_set<CdmSessionType> session_types;
+};
+
+}  // namespace media
+
+#endif  // MEDIA_CDM_CDM_CAPABILITY_H_
diff --git a/media/gpu/windows/d3d11_texture_selector.cc b/media/gpu/windows/d3d11_texture_selector.cc
index 4a84e58..7a92a8f 100644
--- a/media/gpu/windows/d3d11_texture_selector.cc
+++ b/media/gpu/windows/d3d11_texture_selector.cc
@@ -66,6 +66,7 @@
     }
     case DXGI_FORMAT_P010: {
       MEDIA_LOG(INFO, media_log) << "D3D11VideoDecoder producing P010";
+      output_pixel_format = PIXEL_FORMAT_ARGB;
 
       // TODO(liberato): handle case where we bind P010 directly (see dxva).
 
@@ -93,7 +94,6 @@
           // TODO(liberato): use the format checker, else bind P010.
           MEDIA_LOG(INFO, media_log) << "D3D11VideoDecoder: 8 bit sRGB";
           output_dxgi_format = DXGI_FORMAT_B8G8R8A8_UNORM;
-          output_pixel_format = PIXEL_FORMAT_ARGB;
           output_color_space = gfx::ColorSpace::CreateSRGB();
         } else {
           // Bind P010 directly, since we can't copy.
@@ -116,10 +116,9 @@
           output_color_space = gfx::ColorSpace::CreateSCRGBLinear();
         } else if (format_checker->CheckOutputFormatSupport(
                        DXGI_FORMAT_R10G10B10A2_UNORM)) {
-          MEDIA_LOG(INFO, media_log) << "D3D11VideoDecoder: RGB10A2 HDR10/PQ";
+          MEDIA_LOG(INFO, media_log) << "D3D11VideoDecoder: BGRA10 scRGBLinear";
           output_dxgi_format = DXGI_FORMAT_R10G10B10A2_UNORM;
-          output_pixel_format = PIXEL_FORMAT_XB30;
-          output_color_space = gfx::ColorSpace::CreateHDR10();
+          output_color_space = gfx::ColorSpace::CreateSCRGBLinear();
         } else {
           // No support at all.  Just bind P010, and hope for the best.
           MEDIA_LOG(INFO, media_log)
@@ -150,10 +149,9 @@
   // If we're trying to produce an output texture that's different from what
   // the decoder is providing, then we need to copy it.  If sharing decoder
   // textures is not allowed, then copy either way.
-  bool needs_texture_copy =
-      !SupportsZeroCopy(gpu_preferences, workarounds) ||
-      (decoder_output_format != output_dxgi_format) ||
-      base::FeatureList::IsEnabled(kD3D11VideoDecoderAlwaysCopy);
+  bool needs_texture_copy = !SupportsZeroCopy(gpu_preferences, workarounds) ||
+                            (decoder_output_format != output_dxgi_format) ||
+               base::FeatureList::IsEnabled(kD3D11VideoDecoderAlwaysCopy);
 
   MEDIA_LOG(INFO, media_log)
       << "D3D11VideoDecoder output color space: "
@@ -180,7 +178,8 @@
     ComD3D11Device device,
     gfx::Size size) {
   // TODO(liberato): If the output format is rgb, then create a pbuffer wrapper.
-  return std::make_unique<DefaultTexture2DWrapper>(size, OutputDXGIFormat());
+  return std::make_unique<DefaultTexture2DWrapper>(size, OutputDXGIFormat(),
+                                                   PixelFormat());
 }
 
 bool TextureSelector::WillCopyForTesting() const {
@@ -229,7 +228,9 @@
     return nullptr;
 
   return std::make_unique<CopyingTexture2DWrapper>(
-      size, std::make_unique<DefaultTexture2DWrapper>(size, OutputDXGIFormat()),
+      size,
+      std::make_unique<DefaultTexture2DWrapper>(size, OutputDXGIFormat(),
+                                                PixelFormat()),
       video_processor_proxy_, out_texture, output_color_space_);
 }
 
diff --git a/media/gpu/windows/d3d11_texture_selector_unittest.cc b/media/gpu/windows/d3d11_texture_selector_unittest.cc
index 9ebb9c34..12bd030 100644
--- a/media/gpu/windows/d3d11_texture_selector_unittest.cc
+++ b/media/gpu/windows/d3d11_texture_selector_unittest.cc
@@ -129,7 +129,7 @@
       CreateWithDefaultGPUInfo(DXGI_FORMAT_P010, ZeroCopyEnabled::kTrue,
                                TextureSelector::HDRMode::kSDROrHDR);
 
-  EXPECT_EQ(tex_sel->PixelFormat(), PIXEL_FORMAT_XB30);
+  EXPECT_EQ(tex_sel->PixelFormat(), PIXEL_FORMAT_ARGB);
   EXPECT_EQ(tex_sel->OutputDXGIFormat(), DXGI_FORMAT_R10G10B10A2_UNORM);
   EXPECT_TRUE(tex_sel->WillCopyForTesting());
 }
diff --git a/media/gpu/windows/d3d11_texture_wrapper.cc b/media/gpu/windows/d3d11_texture_wrapper.cc
index 0d1da96..ed2ede8d 100644
--- a/media/gpu/windows/d3d11_texture_wrapper.cc
+++ b/media/gpu/windows/d3d11_texture_wrapper.cc
@@ -22,43 +22,90 @@
 
 namespace {
 
-bool SupportsFormat(DXGI_FORMAT dxgi_format) {
+// Populates Viz |texture_formats| that map to the corresponding DXGI format and
+// VideoPixelFormat. Returns true if they can be successfully mapped.
+bool DXGIFormatToVizFormat(
+    DXGI_FORMAT dxgi_format,
+    VideoPixelFormat pixel_format,
+    size_t textures_per_picture,
+    std::array<viz::ResourceFormat, VideoFrame::kMaxPlanes>& texture_formats) {
   switch (dxgi_format) {
     case DXGI_FORMAT_NV12:
-    case DXGI_FORMAT_P010:
-    case DXGI_FORMAT_B8G8R8A8_UNORM:
-    case DXGI_FORMAT_R10G10B10A2_UNORM:
-    case DXGI_FORMAT_R16G16B16A16_FLOAT:
+      DCHECK_EQ(textures_per_picture, 2u);
+      texture_formats[0] = viz::RED_8;  // Y
+      texture_formats[1] = viz::RG_88;  // UV
       return true;
-    default:
+    case DXGI_FORMAT_P010:
+      // TODO(crbug.com/1011555): P010 formats are not fully supported.
+      // Treat them to be the same as NV12 for the time being.
+      DCHECK_EQ(textures_per_picture, 2u);
+      texture_formats[0] = viz::RED_8;
+      texture_formats[1] = viz::RG_88;
+      return true;
+    case DXGI_FORMAT_B8G8R8A8_UNORM:
+      DCHECK_EQ(textures_per_picture, 1u);
+      if (pixel_format != PIXEL_FORMAT_ARGB) {
+        return false;
+      }
+      texture_formats[0] = viz::BGRA_8888;
+      return true;
+    case DXGI_FORMAT_R16G16B16A16_FLOAT:
+      DCHECK_EQ(textures_per_picture, 1u);
+      if (pixel_format != PIXEL_FORMAT_RGBAF16)
+        return false;
+      texture_formats[0] = viz::RGBA_F16;
+      return true;
+    default:  // Unsupported
       return false;
   }
 }
 
-size_t NumPlanes(DXGI_FORMAT dxgi_format) {
-  switch (dxgi_format) {
-    case DXGI_FORMAT_NV12:
-    case DXGI_FORMAT_P010:
-      return 2;
-    case DXGI_FORMAT_B8G8R8A8_UNORM:
-    case DXGI_FORMAT_R10G10B10A2_UNORM:
-    case DXGI_FORMAT_R16G16B16A16_FLOAT:
-      return 1;
-    default:
-      NOTREACHED();
-      return 0;
-  }
-}
-
 }  // anonymous namespace
 
+// Handy structure so that we can activate / bind one or two textures.
+struct ScopedTextureEverything {
+  ScopedTextureEverything(GLenum unit, GLuint service_id)
+      : active_(unit), binder_(GL_TEXTURE_EXTERNAL_OES, service_id) {}
+  ~ScopedTextureEverything() = default;
+
+  // Order is important; we need |active_| to be constructed first
+  // and destructed last.
+  gl::ScopedActiveTexture active_;
+  gl::ScopedTextureBinder binder_;
+
+  DISALLOW_COPY_AND_ASSIGN(ScopedTextureEverything);
+};
+
+// Another handy helper class to guarantee that ScopedTextureEverythings
+// are deleted in reverse order.  This is required so that the scoped
+// active texture unit doesn't change.  Surprisingly, none of the stl
+// containers, or the chromium ones, seem to guarantee anything about
+// the order of destruction.
+struct OrderedDestructionList {
+  OrderedDestructionList() = default;
+  ~OrderedDestructionList() {
+    // Erase last-to-first.
+    while (!list_.empty())
+      list_.pop_back();
+  }
+
+  template <typename... Args>
+  void emplace_back(Args&&... args) {
+    list_.emplace_back(std::forward<Args>(args)...);
+  }
+
+  std::list<ScopedTextureEverything> list_;
+  DISALLOW_COPY_AND_ASSIGN(OrderedDestructionList);
+};
+
 Texture2DWrapper::Texture2DWrapper() = default;
 
 Texture2DWrapper::~Texture2DWrapper() = default;
 
 DefaultTexture2DWrapper::DefaultTexture2DWrapper(const gfx::Size& size,
-                                                 DXGI_FORMAT dxgi_format)
-    : size_(size), dxgi_format_(dxgi_format) {}
+                                                 DXGI_FORMAT dxgi_format,
+                                                 VideoPixelFormat pixel_format)
+    : size_(size), dxgi_format_(dxgi_format), pixel_format_(pixel_format) {}
 
 DefaultTexture2DWrapper::~DefaultTexture2DWrapper() = default;
 
@@ -88,17 +135,28 @@
     GetCommandBufferHelperCB get_helper_cb,
     ComD3D11Texture2D texture,
     size_t array_slice) {
-  if (!SupportsFormat(dxgi_format_))
+  gpu_resources_ = base::SequenceBound<GpuResources>(
+      std::move(gpu_task_runner),
+      BindToCurrentLoop(base::BindOnce(&DefaultTexture2DWrapper::OnError,
+                                       weak_factory_.GetWeakPtr())));
+
+  const size_t textures_per_picture = VideoFrame::NumPlanes(pixel_format_);
+
+  std::array<viz::ResourceFormat, VideoFrame::kMaxPlanes> texture_formats;
+  if (!DXGIFormatToVizFormat(dxgi_format_, pixel_format_, textures_per_picture,
+                             texture_formats)) {
     return Status(StatusCode::kUnsupportedTextureFormatForBind);
+  }
 
   // Generate mailboxes and holders.
   // TODO(liberato): Verify that this is really okay off the GPU main thread.
   // The current implementation is.
   std::vector<gpu::Mailbox> mailboxes;
-  for (size_t plane = 0; plane < NumPlanes(dxgi_format_); plane++) {
+  for (size_t texture_idx = 0; texture_idx < textures_per_picture;
+       texture_idx++) {
     mailboxes.push_back(gpu::Mailbox::GenerateForSharedImage());
-    mailbox_holders_[plane] = gpu::MailboxHolder(
-        mailboxes[plane], gpu::SyncToken(), GL_TEXTURE_EXTERNAL_OES);
+    mailbox_holders_[texture_idx] = gpu::MailboxHolder(
+        mailboxes[texture_idx], gpu::SyncToken(), GL_TEXTURE_EXTERNAL_OES);
   }
 
   // Start construction of the GpuResources.
@@ -106,12 +164,10 @@
   // device for decoding.  Sharing seems not to work very well.  Otherwise, we
   // would create the texture with KEYED_MUTEX and NTHANDLE, then send along
   // a handle that we get from |texture| as an IDXGIResource1.
-  auto on_error_cb = BindToCurrentLoop(base::BindOnce(
-      &DefaultTexture2DWrapper::OnError, weak_factory_.GetWeakPtr()));
-  gpu_resources_ = base::SequenceBound<GpuResources>(
-      std::move(gpu_task_runner), std::move(on_error_cb),
-      std::move(get_helper_cb), std::move(mailboxes), size_, dxgi_format_,
-      texture, array_slice);
+  gpu_resources_.AsyncCall(&GpuResources::Init)
+      .WithArgs(std::move(get_helper_cb), std::move(mailboxes),
+                GL_TEXTURE_EXTERNAL_OES, size_, textures_per_picture,
+                texture_formats, pixel_format_, texture, array_slice);
   return OkStatus();
 }
 
@@ -126,47 +182,217 @@
 void DefaultTexture2DWrapper::SetDisplayHDRMetadata(
     const DXGI_HDR_METADATA_HDR10& dxgi_display_metadata) {}
 
-DefaultTexture2DWrapper::GpuResources::GpuResources(
-    OnErrorCB on_error_cb,
+DefaultTexture2DWrapper::GpuResources::GpuResources(OnErrorCB on_error_cb)
+    : on_error_cb_(std::move(on_error_cb)) {}
+
+DefaultTexture2DWrapper::GpuResources::~GpuResources() {
+  if (helper_ && helper_->MakeContextCurrent()) {
+    for (uint32_t service_id : service_ids_)
+      helper_->DestroyTexture(service_id);
+  }
+}
+
+void DefaultTexture2DWrapper::GpuResources::Init(
     GetCommandBufferHelperCB get_helper_cb,
-    const std::vector<gpu::Mailbox>& mailboxes,
-    const gfx::Size& size,
-    DXGI_FORMAT dxgi_format,
+    const std::vector<gpu::Mailbox> mailboxes,
+    GLenum target,
+    gfx::Size size,
+    size_t textures_per_picture,
+    std::array<viz::ResourceFormat, VideoFrame::kMaxPlanes> texture_formats,
+    VideoPixelFormat pixel_format,
     ComD3D11Texture2D texture,
     size_t array_slice) {
   helper_ = get_helper_cb.Run();
 
   if (!helper_ || !helper_->MakeContextCurrent()) {
-    std::move(on_error_cb)
-        .Run(std::move(StatusCode::kMakeContextCurrentFailed));
+    NotifyError(StatusCode::kMakeContextCurrentFailed);
     return;
   }
 
-  // Usage flags to allow the display compositor to draw from it, video to
-  // decode, and allow webgl/canvas access.
-  constexpr uint32_t usage =
-      gpu::SHARED_IMAGE_USAGE_VIDEO_DECODE | gpu::SHARED_IMAGE_USAGE_GLES2 |
-      gpu::SHARED_IMAGE_USAGE_RASTER | gpu::SHARED_IMAGE_USAGE_DISPLAY |
-      gpu::SHARED_IMAGE_USAGE_SCANOUT;
-
-  auto shared_image_backings =
-      gpu::SharedImageBackingD3D::CreateFromVideoTexture(
-          mailboxes, dxgi_format, size, usage, texture, array_slice);
-  if (shared_image_backings.empty()) {
-    std::move(on_error_cb).Run(std::move(StatusCode::kCreateSharedImageFailed));
+  // Create the stream for zero-copy use by gl.
+  EGLDisplay egl_display = gl::GLSurfaceEGL::GetHardwareDisplay();
+  const EGLint stream_attributes[] = {
+      // clang-format off
+      EGL_CONSUMER_LATENCY_USEC_KHR,         0,
+      EGL_CONSUMER_ACQUIRE_TIMEOUT_USEC_KHR, 0,
+      EGL_NONE,
+      // clang-format on
+  };
+  EGLStreamKHR stream = eglCreateStreamKHR(egl_display, stream_attributes);
+  if (!stream) {
+    NotifyError(StatusCode::kCreateEglStreamFailed);
     return;
   }
-  DCHECK_EQ(shared_image_backings.size(), NumPlanes(dxgi_format));
 
-  for (auto& backing : shared_image_backings)
-    shared_images_.push_back(helper_->Register(std::move(backing)));
+  // |stream| will be destroyed when the GLImage is.
+  // TODO(liberato): for tests, it will be destroyed pretty much at the end of
+  // this function unless |helper_| retains it.  Also, this won't work if we
+  // have a FakeCommandBufferHelper since the service IDs aren't meaningful.
+  gl_image_ = base::MakeRefCounted<gl::GLImageDXGI>(size, stream);
+
+  // Create the textures and attach them to the mailboxes.
+  // TODO(liberato): Should we use GL_FLOAT for an fp16 texture?  It doesn't
+  // really seem to matter so far as I can tell.
+  for (size_t texture_idx = 0; texture_idx < textures_per_picture;
+       texture_idx++) {
+    const viz::ResourceFormat format = texture_formats[texture_idx];
+    const GLenum internal_format = viz::GLInternalFormat(format);
+    const GLenum data_type = viz::GLDataType(format);
+    const GLenum data_format = viz::GLDataFormat(format);
+
+    // Adjust the size by the subsampling factor.
+    const size_t width =
+        VideoFrame::Columns(texture_idx, pixel_format, size.width());
+    const size_t height =
+        VideoFrame::Rows(texture_idx, pixel_format, size.height());
+    const gfx::Size plane_size(width, height);
+
+    // TODO(crbug.com/1011555): CreateTexture allocates a GL texture, figure out
+    // if this can be removed.
+    const uint32_t service_id =
+        helper_->CreateTexture(target, internal_format, plane_size.width(),
+                               plane_size.height(), data_format, data_type);
+
+    const auto& mailbox = mailboxes[texture_idx];
+
+    // Shared image does not need to store the colorspace since it is already
+    // stored on the VideoFrame which is provided upon presenting the overlay.
+    // To prevent the developer from mistakenly using it, provide the invalid
+    // value from default-construction.
+    const gfx::ColorSpace kInvalidColorSpace;
+
+    // Usage flags to allow the display compositor to draw from it, video to
+    // decode, and allow webgl/canvas access.
+    const uint32_t shared_image_usage =
+        gpu::SHARED_IMAGE_USAGE_VIDEO_DECODE | gpu::SHARED_IMAGE_USAGE_GLES2 |
+        gpu::SHARED_IMAGE_USAGE_RASTER | gpu::SHARED_IMAGE_USAGE_DISPLAY |
+        gpu::SHARED_IMAGE_USAGE_SCANOUT;
+
+    // Create a shared image
+    // TODO(crbug.com/1011555): Need key shared mutex if shared image is ever
+    // used by another device.
+    scoped_refptr<gpu::gles2::TexturePassthrough> gl_texture =
+        gpu::gles2::TexturePassthrough::CheckedCast(
+            helper_->GetTexture(service_id));
+
+    auto shared_image = std::make_unique<gpu::SharedImageBackingD3D>(
+        mailbox, format, plane_size, kInvalidColorSpace,
+        kTopLeft_GrSurfaceOrigin, kPremul_SkAlphaType, shared_image_usage,
+        /*swap_chain=*/nullptr, std::move(gl_texture), gl_image_,
+        /*buffer_index=*/0, texture, base::win::ScopedHandle(),
+        /*dxgi_key_mutex=*/nullptr);
+
+    // Caller is assumed to provide cleared d3d textures.
+    shared_image->SetCleared();
+
+    // Shared images will be destroyed when this wrapper goes away.
+    // Only GpuResource can be used to safely destroy the shared images on the
+    // gpu main thread.
+    shared_images_.push_back(helper_->Register(std::move(shared_image)));
+
+    service_ids_.push_back(service_id);
+  }
+
+  // Bind all the textures so that the stream can find them.
+  OrderedDestructionList texture_everythings;
+  for (size_t i = 0; i < textures_per_picture; i++)
+    texture_everythings.emplace_back(GL_TEXTURE0 + i, service_ids_[i]);
+
+  std::vector<EGLAttrib> consumer_attributes;
+  if (textures_per_picture == 2) {
+    // Assume NV12.
+    consumer_attributes = {
+        // clang-format off
+        EGL_COLOR_BUFFER_TYPE,               EGL_YUV_BUFFER_EXT,
+        EGL_YUV_NUMBER_OF_PLANES_EXT,        2,
+        EGL_YUV_PLANE0_TEXTURE_UNIT_NV,      0,
+        EGL_YUV_PLANE1_TEXTURE_UNIT_NV,      1,
+        EGL_NONE,
+        // clang-format on
+    };
+  } else {
+    // Assume some rgb format.
+    consumer_attributes = {
+        // clang-format off
+        EGL_COLOR_BUFFER_TYPE,               EGL_RGB_BUFFER,
+        EGL_NONE,
+        // clang-format on
+    };
+  }
+  EGLBoolean result = eglStreamConsumerGLTextureExternalAttribsNV(
+      egl_display, stream, consumer_attributes.data());
+  if (!result) {
+    NotifyError(StatusCode::kCreateEglStreamConsumerFailed);
+    return;
+  }
+
+  EGLAttrib producer_attributes[] = {
+      EGL_NONE,
+  };
+
+  result = eglCreateStreamProducerD3DTextureANGLE(egl_display, stream,
+                                                  producer_attributes);
+  if (!result) {
+    NotifyError(StatusCode::kCreateEglStreamProducerFailed);
+    return;
+  }
+
+  // Note that this is valid as long as |gl_image_| is valid; it is
+  // what deletes the stream.
+  stream_ = stream;
+
+  // Bind the image to each texture.
+  for (size_t texture_idx = 0; texture_idx < service_ids_.size();
+       texture_idx++) {
+    helper_->BindImage(service_ids_[texture_idx], gl_image_.get(),
+                       false /* client_managed */);
+  }
+
+  // Specify the texture so ProcessTexture knows how to process it using a GL
+  // image.
+  gl_image_->SetTexture(texture, array_slice);
+
+  PushNewTexture();
 }
 
-DefaultTexture2DWrapper::GpuResources::~GpuResources() {
-  // Destroy shared images with a current context.
-  if (!helper_ || !helper_->MakeContextCurrent())
+void DefaultTexture2DWrapper::GpuResources::PushNewTexture() {
+  // If init didn't complete, then signal (another) error that will probably be
+  // ignored in favor of whatever we signalled earlier.
+  if (!gl_image_ || !stream_) {
+    NotifyError(StatusCode::kDecoderInitializeNeverCompleted);
     return;
-  shared_images_.clear();
+  }
+
+  if (!helper_ || !helper_->MakeContextCurrent()) {
+    NotifyError(StatusCode::kMakeContextCurrentFailed);
+    return;
+  }
+
+  // Notify angle that it has a new texture.
+  EGLAttrib frame_attributes[] = {
+      EGL_D3D_TEXTURE_SUBRESOURCE_ID_ANGLE,
+      gl_image_->level(),
+      EGL_NONE,
+  };
+
+  EGLDisplay egl_display = gl::GLSurfaceEGL::GetHardwareDisplay();
+  if (!eglStreamPostD3DTextureANGLE(
+          egl_display, stream_, static_cast<void*>(gl_image_->texture().Get()),
+          frame_attributes)) {
+    NotifyError(StatusCode::kPostTextureFailed);
+    return;
+  }
+
+  if (!eglStreamConsumerAcquireKHR(egl_display, stream_)) {
+    NotifyError(StatusCode::kPostAcquireStreamFailed);
+    return;
+  }
+}
+
+void DefaultTexture2DWrapper::GpuResources::NotifyError(Status status) {
+  if (on_error_cb_)
+    std::move(on_error_cb_).Run(std::move(status));
+  // else this isn't the first error, so skip it.
 }
 
 }  // namespace media
diff --git a/media/gpu/windows/d3d11_texture_wrapper.h b/media/gpu/windows/d3d11_texture_wrapper.h
index 9604e88..5c097ec9 100644
--- a/media/gpu/windows/d3d11_texture_wrapper.h
+++ b/media/gpu/windows/d3d11_texture_wrapper.h
@@ -74,7 +74,9 @@
 
   // While the specific texture instance can change on every call to
   // ProcessTexture, the dxgi format must be the same for all of them.
-  DefaultTexture2DWrapper(const gfx::Size& size, DXGI_FORMAT dxgi_format);
+  DefaultTexture2DWrapper(const gfx::Size& size,
+                          DXGI_FORMAT dxgi_format,
+                          VideoPixelFormat pixel_format);
   ~DefaultTexture2DWrapper() override;
 
   Status Init(scoped_refptr<base::SingleThreadTaskRunner> gpu_task_runner,
@@ -97,17 +99,36 @@
   // can use the mailbox.
   class GpuResources {
    public:
-    GpuResources(OnErrorCB on_error_cb,
-                 GetCommandBufferHelperCB get_helper_cb,
-                 const std::vector<gpu::Mailbox>& mailboxes,
-                 const gfx::Size& size,
-                 DXGI_FORMAT dxgi_format,
-                 ComD3D11Texture2D texture,
-                 size_t array_slice);
+    GpuResources(OnErrorCB on_error_cb);
     ~GpuResources();
 
+    void Init(
+        GetCommandBufferHelperCB get_helper_cb,
+        const std::vector<gpu::Mailbox> mailboxes,
+        GLenum target,
+        gfx::Size size,
+        size_t textures_per_picture,
+        std::array<viz::ResourceFormat, VideoFrame::kMaxPlanes> texture_formats,
+        VideoPixelFormat pixel_format,
+        ComD3D11Texture2D texture,
+        size_t array_slice);
+
+    std::vector<uint32_t> service_ids_;
+
    private:
+    // Push a new |texture|, |array_slice| to |gl_image_|.
+    // Both |texture| and |array_slice| were set by Init.
+    void PushNewTexture();
+
+    // Notify our wrapper about |status|, if we haven't before.
+    void NotifyError(Status status);
+
+    // May be empty if we've already sent an error.
+    OnErrorCB on_error_cb_;
+
     scoped_refptr<CommandBufferHelper> helper_;
+    scoped_refptr<gl::GLImageDXGI> gl_image_;
+    EGLStreamKHR stream_;
 
     std::vector<std::unique_ptr<gpu::SharedImageRepresentationFactoryRef>>
         shared_images_;
@@ -125,6 +146,7 @@
   base::SequenceBound<GpuResources> gpu_resources_;
   MailboxHolderArray mailbox_holders_;
   DXGI_FORMAT dxgi_format_;
+  VideoPixelFormat pixel_format_;
 
   base::WeakPtrFactory<DefaultTexture2DWrapper> weak_factory_{this};
 };
diff --git a/media/gpu/windows/d3d11_texture_wrapper_unittest.cc b/media/gpu/windows/d3d11_texture_wrapper_unittest.cc
index cfcd5fa..3142782 100644
--- a/media/gpu/windows/d3d11_texture_wrapper_unittest.cc
+++ b/media/gpu/windows/d3d11_texture_wrapper_unittest.cc
@@ -88,8 +88,10 @@
 TEST_F(D3D11TextureWrapperUnittest, NV12InitSucceeds) {
   STOP_IF_WIN7();
   const DXGI_FORMAT dxgi_format = DXGI_FORMAT_NV12;
+  const VideoPixelFormat pixel_format = PIXEL_FORMAT_NV12;
 
-  auto wrapper = std::make_unique<DefaultTexture2DWrapper>(size_, dxgi_format);
+  auto wrapper = std::make_unique<DefaultTexture2DWrapper>(size_, dxgi_format,
+                                                           pixel_format);
   const Status init_result = wrapper->Init(
       task_runner_, get_helper_cb_, /*texture_d3d=*/nullptr, /*array_slice=*/0);
   EXPECT_TRUE(init_result.is_ok());
@@ -100,8 +102,10 @@
 TEST_F(D3D11TextureWrapperUnittest, BGRA8InitSucceeds) {
   STOP_IF_WIN7();
   const DXGI_FORMAT dxgi_format = DXGI_FORMAT_B8G8R8A8_UNORM;
+  const VideoPixelFormat pixel_format = PIXEL_FORMAT_ARGB;
 
-  auto wrapper = std::make_unique<DefaultTexture2DWrapper>(size_, dxgi_format);
+  auto wrapper = std::make_unique<DefaultTexture2DWrapper>(size_, dxgi_format,
+                                                           pixel_format);
   const Status init_result = wrapper->Init(
       task_runner_, get_helper_cb_, /*texture_d3d=*/nullptr, /*array_slice=*/0);
   EXPECT_TRUE(init_result.is_ok());
@@ -110,8 +114,10 @@
 TEST_F(D3D11TextureWrapperUnittest, FP16InitSucceeds) {
   STOP_IF_WIN7();
   const DXGI_FORMAT dxgi_format = DXGI_FORMAT_R16G16B16A16_FLOAT;
+  const VideoPixelFormat pixel_format = PIXEL_FORMAT_RGBAF16;
 
-  auto wrapper = std::make_unique<DefaultTexture2DWrapper>(size_, dxgi_format);
+  auto wrapper = std::make_unique<DefaultTexture2DWrapper>(size_, dxgi_format,
+                                                           pixel_format);
   const Status init_result = wrapper->Init(
       task_runner_, get_helper_cb_, /*texture_d3d=*/nullptr, /*array_slice=*/0);
   EXPECT_TRUE(init_result.is_ok());
@@ -120,8 +126,10 @@
 TEST_F(D3D11TextureWrapperUnittest, P010InitSucceeds) {
   STOP_IF_WIN7();
   const DXGI_FORMAT dxgi_format = DXGI_FORMAT_P010;
+  const VideoPixelFormat pixel_format = PIXEL_FORMAT_NV12;
 
-  auto wrapper = std::make_unique<DefaultTexture2DWrapper>(size_, dxgi_format);
+  auto wrapper = std::make_unique<DefaultTexture2DWrapper>(size_, dxgi_format,
+                                                           pixel_format);
   const Status init_result = wrapper->Init(
       task_runner_, get_helper_cb_, /*texture_d3d=*/nullptr, /*array_slice=*/0);
   EXPECT_TRUE(init_result.is_ok());
@@ -130,11 +138,13 @@
 TEST_F(D3D11TextureWrapperUnittest, UnknownInitFails) {
   STOP_IF_WIN7();
   const DXGI_FORMAT dxgi_format = DXGI_FORMAT_UNKNOWN;
+  const VideoPixelFormat pixel_format = PIXEL_FORMAT_UNKNOWN;
 
-  auto wrapper = std::make_unique<DefaultTexture2DWrapper>(size_, dxgi_format);
+  auto wrapper = std::make_unique<DefaultTexture2DWrapper>(size_, dxgi_format,
+                                                           pixel_format);
   const Status init_result = wrapper->Init(
       task_runner_, get_helper_cb_, /*texture_d3d=*/nullptr, /*array_slice=*/0);
   EXPECT_FALSE(init_result.is_ok());
 }
 
-}  // namespace media
+}  // namespace media
\ No newline at end of file
diff --git a/media/gpu/windows/dxva_video_decode_accelerator_win.cc b/media/gpu/windows/dxva_video_decode_accelerator_win.cc
index b4e309b..55c4d93 100644
--- a/media/gpu/windows/dxva_video_decode_accelerator_win.cc
+++ b/media/gpu/windows/dxva_video_decode_accelerator_win.cc
@@ -2732,12 +2732,16 @@
       // this |picture_buffer| will be updated when the video frame is created.
       const auto& mailbox = gpu::Mailbox::GenerateForSharedImage();
 
-      auto shared_image = gpu::SharedImageBackingD3D::CreateFromGLTexture(
+      auto shared_image = std::make_unique<gpu::SharedImageBackingD3D>(
           mailbox, viz_formats[texture_idx],
           picture_buffer->texture_size(texture_idx),
           picture_buffer->color_space(), kTopLeft_GrSurfaceOrigin,
-          kPremul_SkAlphaType, shared_image_usage, gl_image_dxgi->texture(),
-          std::move(gl_texture));
+          kPremul_SkAlphaType, shared_image_usage,
+          /*swap_chain=*/nullptr, std::move(gl_texture),
+          picture_buffer->gl_image(),
+          /*buffer_index=*/0, gl_image_dxgi->texture(),
+          base::win::ScopedHandle(),
+          /*dxgi_keyed_mutex=*/nullptr);
 
       // Caller is assumed to provide cleared d3d textures.
       shared_image->SetCleared();
diff --git a/media/renderers/video_resource_updater.cc b/media/renderers/video_resource_updater.cc
index 65c6da0..7cbd5af 100644
--- a/media/renderers/video_resource_updater.cc
+++ b/media/renderers/video_resource_updater.cc
@@ -544,8 +544,7 @@
     gfx::Rect quad_rect,
     gfx::Rect visible_quad_rect,
     const gfx::MaskFilterInfo& mask_filter_info,
-    gfx::Rect clip_rect,
-    bool is_clipped,
+    base::Optional<gfx::Rect> clip_rect,
     bool contents_opaque,
     float draw_opacity,
     int sorting_context_id) {
@@ -553,10 +552,9 @@
 
   viz::SharedQuadState* shared_quad_state =
       render_pass->CreateAndAppendSharedQuadState();
-  shared_quad_state->SetAll(transform, quad_rect, visible_quad_rect,
-                            mask_filter_info, clip_rect, is_clipped,
-                            contents_opaque, draw_opacity,
-                            SkBlendMode::kSrcOver, sorting_context_id);
+  shared_quad_state->SetAll(
+      transform, quad_rect, visible_quad_rect, mask_filter_info, clip_rect,
+      contents_opaque, draw_opacity, SkBlendMode::kSrcOver, sorting_context_id);
 
   bool needs_blending = !contents_opaque;
 
diff --git a/media/renderers/video_resource_updater.h b/media/renderers/video_resource_updater.h
index e23887c..1d17dca 100644
--- a/media/renderers/video_resource_updater.h
+++ b/media/renderers/video_resource_updater.h
@@ -124,8 +124,7 @@
                    gfx::Rect quad_rect,
                    gfx::Rect visible_quad_rect,
                    const gfx::MaskFilterInfo& mask_filter_info,
-                   gfx::Rect clip_rect,
-                   bool is_clipped,
+                   base::Optional<gfx::Rect> clip_rect,
                    bool context_opaque,
                    float draw_opacity,
                    int sorting_context_id);
diff --git a/mojo/public/tools/mojom/mojom_parser.py b/mojo/public/tools/mojom/mojom_parser.py
index 417accf2..eb90c825 100755
--- a/mojo/public/tools/mojom/mojom_parser.py
+++ b/mojo/public/tools/mojom/mojom_parser.py
@@ -27,16 +27,17 @@
 from mojom.parse import conditional_features
 
 
-# TODO(crbug.com/1187708): The multiprocessing code below seems
-# like it doesn't work on (at least) Mac Python3, and so it's
-# disabled for now until we can dig into it. Once that's working,
-# we can restore the logic to just be disabled under Python2.
-#
-# # Disable this for easier debugging.
-# # In Python 2, subprocesses just hang when exceptions are thrown :(.
-# ENABLE_MULTIPROCESSING = sys.version_info[0] > 2
-#
-ENABLE_MULTIPROCESSING = False
+# Disable this for easier debugging.
+# In Python 2, subprocesses just hang when exceptions are thrown :(.
+_ENABLE_MULTIPROCESSING = sys.version_info[0] > 2
+
+if sys.version_info < (3, 4):
+  _MULTIPROCESSING_USES_FORK = sys.platform.startswith('linux')
+else:
+  # https://docs.python.org/3/library/multiprocessing.html#:~:text=bpo-33725
+  if __name__ == '__main__' and sys.platform == 'darwin':
+    multiprocessing.set_start_method('fork')
+  _MULTIPROCESSING_USES_FORK = multiprocessing.get_start_method() == 'fork'
 
 
 def _ResolveRelativeImportPath(path, roots):
@@ -211,7 +212,7 @@
     processes = min(processes, 56)
 
   # Don't spin up processes unless there is enough work to merit doing so.
-  if not ENABLE_MULTIPROCESSING or processes < 2:
+  if not _ENABLE_MULTIPROCESSING or processes < 2:
     for result in map(target_func, args):
       yield result
     return
@@ -326,7 +327,7 @@
   _SerializeHelper.loaded_modules = loaded_modules
   _SerializeHelper.output_root_path = output_root_path
   # Doesn't seem to help past 4. Perhaps IO bound here?
-  processes = 0 if sys.platform == 'win32' else 4
+  processes = 4 if _MULTIPROCESSING_USES_FORK else 0
   map_args = mojom_files_to_parse.items()
   for _ in _Shard(_SerializeHelper, map_args, processes=processes):
     pass
diff --git a/sandbox/policy/BUILD.gn b/sandbox/policy/BUILD.gn
index a24636cb..7dfa776 100644
--- a/sandbox/policy/BUILD.gn
+++ b/sandbox/policy/BUILD.gn
@@ -58,8 +58,8 @@
       "linux/bpf_print_compositor_policy_linux.h",
       "linux/bpf_renderer_policy_linux.cc",
       "linux/bpf_renderer_policy_linux.h",
-      "linux/bpf_sharing_service_policy_linux.cc",
-      "linux/bpf_sharing_service_policy_linux.h",
+      "linux/bpf_service_policy_linux.cc",
+      "linux/bpf_service_policy_linux.h",
       "linux/bpf_speech_recognition_policy_linux.cc",
       "linux/bpf_speech_recognition_policy_linux.h",
       "linux/bpf_utility_policy_linux.cc",
diff --git a/sandbox/policy/fuchsia/sandbox_policy_fuchsia.cc b/sandbox/policy/fuchsia/sandbox_policy_fuchsia.cc
index 18e4a20..4dae92d 100644
--- a/sandbox/policy/fuchsia/sandbox_policy_fuchsia.cc
+++ b/sandbox/policy/fuchsia/sandbox_policy_fuchsia.cc
@@ -123,7 +123,7 @@
     case SandboxType::kCdm:
     case SandboxType::kPpapi:
     case SandboxType::kPrintCompositor:
-    case SandboxType::kSharingService:
+    case SandboxType::kService:
     case SandboxType::kSpeechRecognition:
     case SandboxType::kUtility:
       return &kEmptySandboxConfig;
diff --git a/sandbox/policy/linux/bpf_sharing_service_policy_linux.cc b/sandbox/policy/linux/bpf_service_policy_linux.cc
similarity index 89%
rename from sandbox/policy/linux/bpf_sharing_service_policy_linux.cc
rename to sandbox/policy/linux/bpf_service_policy_linux.cc
index 91c12ca..3ddc8bf 100644
--- a/sandbox/policy/linux/bpf_sharing_service_policy_linux.cc
+++ b/sandbox/policy/linux/bpf_service_policy_linux.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 "sandbox/policy/linux/bpf_sharing_service_policy_linux.h"
+#include "sandbox/policy/linux/bpf_service_policy_linux.h"
 
 #include <errno.h>
 
@@ -20,7 +20,7 @@
 namespace sandbox {
 namespace policy {
 
-ResultExpr SharingServiceProcessPolicy::EvaluateSyscall(int sysno) const {
+ResultExpr ServiceProcessPolicy::EvaluateSyscall(int sysno) const {
   switch (sysno) {
     case __NR_ioctl:
       return RestrictIoctl();
diff --git a/sandbox/policy/linux/bpf_service_policy_linux.h b/sandbox/policy/linux/bpf_service_policy_linux.h
new file mode 100644
index 0000000..2c7a3359
--- /dev/null
+++ b/sandbox/policy/linux/bpf_service_policy_linux.h
@@ -0,0 +1,32 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SANDBOX_POLICY_LINUX_BPF_SERVICE_POLICY_LINUX_H_
+#define SANDBOX_POLICY_LINUX_BPF_SERVICE_POLICY_LINUX_H_
+
+#include "base/macros.h"
+#include "sandbox/policy/linux/bpf_base_policy_linux.h"
+
+namespace sandbox {
+namespace policy {
+
+// This policy can be used by isolated utilities such as the Sharing
+// service to host WebRTC, and isolated javascript worklets to host
+// jitless javascript. Resources should be provided via mojo.
+// Consider UtilityProcessPolicy if this is too restrictive.
+class ServiceProcessPolicy : public BPFBasePolicy {
+ public:
+  ServiceProcessPolicy() = default;
+  ~ServiceProcessPolicy() override = default;
+
+  bpf_dsl::ResultExpr EvaluateSyscall(int system_call_number) const override;
+
+  ServiceProcessPolicy(const ServiceProcessPolicy&) = delete;
+  ServiceProcessPolicy& operator=(const ServiceProcessPolicy&) = delete;
+};
+
+}  // namespace policy
+}  // namespace sandbox
+
+#endif  // SERVICES_SERVICE_MANAGER_SANDBOX_LINUX_BPF_SERVICE_POLICY_LINUX_H_
diff --git a/sandbox/policy/linux/bpf_sharing_service_policy_linux.h b/sandbox/policy/linux/bpf_sharing_service_policy_linux.h
deleted file mode 100644
index 6c5d612..0000000
--- a/sandbox/policy/linux/bpf_sharing_service_policy_linux.h
+++ /dev/null
@@ -1,30 +0,0 @@
-// Copyright 2020 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef SANDBOX_POLICY_LINUX_BPF_SHARING_SERVICE_POLICY_LINUX_H_
-#define SANDBOX_POLICY_LINUX_BPF_SHARING_SERVICE_POLICY_LINUX_H_
-
-#include "base/macros.h"
-#include "sandbox/policy/linux/bpf_base_policy_linux.h"
-
-namespace sandbox {
-namespace policy {
-
-// This policy can be used by the Sharing service to host WebRTC.
-class SharingServiceProcessPolicy : public BPFBasePolicy {
- public:
-  SharingServiceProcessPolicy() = default;
-  ~SharingServiceProcessPolicy() override = default;
-
-  bpf_dsl::ResultExpr EvaluateSyscall(int system_call_number) const override;
-
-  SharingServiceProcessPolicy(const SharingServiceProcessPolicy&) = delete;
-  SharingServiceProcessPolicy& operator=(const SharingServiceProcessPolicy&) =
-      delete;
-};
-
-}  // namespace policy
-}  // namespace sandbox
-
-#endif  // SANDBOX_POLICY_LINUX_BPF_SHARING_SERVICE_POLICY_LINUX_H_
diff --git a/sandbox/policy/linux/sandbox_seccomp_bpf_linux.cc b/sandbox/policy/linux/sandbox_seccomp_bpf_linux.cc
index 7bdbab95..5d572af 100644
--- a/sandbox/policy/linux/sandbox_seccomp_bpf_linux.cc
+++ b/sandbox/policy/linux/sandbox_seccomp_bpf_linux.cc
@@ -47,7 +47,7 @@
 #include "sandbox/policy/linux/bpf_print_backend_policy_linux.h"
 #include "sandbox/policy/linux/bpf_print_compositor_policy_linux.h"
 #include "sandbox/policy/linux/bpf_renderer_policy_linux.h"
-#include "sandbox/policy/linux/bpf_sharing_service_policy_linux.h"
+#include "sandbox/policy/linux/bpf_service_policy_linux.h"
 #include "sandbox/policy/linux/bpf_speech_recognition_policy_linux.h"
 #include "sandbox/policy/linux/bpf_utility_policy_linux.h"
 
@@ -190,8 +190,8 @@
       return std::make_unique<NetworkProcessPolicy>();
     case SandboxType::kAudio:
       return std::make_unique<AudioProcessPolicy>();
-    case SandboxType::kSharingService:
-      return std::make_unique<SharingServiceProcessPolicy>();
+    case SandboxType::kService:
+      return std::make_unique<ServiceProcessPolicy>();
     case SandboxType::kSpeechRecognition:
       return std::make_unique<SpeechRecognitionProcessPolicy>();
 #if BUILDFLAG(IS_CHROMEOS_ASH)
@@ -252,7 +252,7 @@
 #endif  // BUILDFLAG(ENABLE_LIBASSISTANT_SANDBOX)
 #endif  // BUILDFLAG(IS_CHROMEOS_ASH)
     case SandboxType::kAudio:
-    case SandboxType::kSharingService:
+    case SandboxType::kService:
     case SandboxType::kSpeechRecognition:
     case SandboxType::kNetwork:
 #if BUILDFLAG(ENABLE_PRINTING)
diff --git a/sandbox/policy/sandbox_type.cc b/sandbox/policy/sandbox_type.cc
index ba5ad56..870a6c6 100644
--- a/sandbox/policy/sandbox_type.cc
+++ b/sandbox/policy/sandbox_type.cc
@@ -63,7 +63,7 @@
 #endif  // BUILDFLAG(ENABLE_LIBASSISTANT_SANDBOX)
 #endif
 #if !defined(OS_MAC)
-    case SandboxType::kSharingService:
+    case SandboxType::kService:
 #endif
 #if defined(OS_LINUX) || defined(OS_CHROMEOS)
     case SandboxType::kZygoteIntermediateSandbox:
@@ -134,7 +134,7 @@
 #endif  // BUILDFLAG(ENABLE_LIBASSISTANT_SANDBOX)
 #endif  // BUILDFLAG(IS_CHROMEOS_ASH)
 #if !defined(OS_MAC)
-    case SandboxType::kSharingService:
+    case SandboxType::kService:
 #endif
     case SandboxType::kSpeechRecognition:
       DCHECK(command_line->GetSwitchValueASCII(switches::kProcessType) ==
@@ -241,8 +241,8 @@
     case SandboxType::kVideoCapture:
       return switches::kVideoCaptureSandbox;
 #if !defined(OS_MAC)
-    case SandboxType::kSharingService:
-      return switches::kSharingServiceSandbox;
+    case SandboxType::kService:
+      return switches::kServiceSandbox;
 #endif
     case SandboxType::kSpeechRecognition:
       return switches::kSpeechRecognitionSandbox;
diff --git a/sandbox/policy/sandbox_type.h b/sandbox/policy/sandbox_type.h
index f755d326..14f1695 100644
--- a/sandbox/policy/sandbox_type.h
+++ b/sandbox/policy/sandbox_type.h
@@ -48,9 +48,19 @@
   // Renderer or worker process. Most common case.
   kRenderer,
 
-  // Utility processes. Used by most isolated services.
+  // Utility processes. Used by most isolated services.  Consider using
+  // kService for Chromium-code that makes limited use of OS APIs.
   kUtility,
 
+#if defined(OS_MAC)
+  // On Mac these are identical.
+  kService = kUtility,
+#else
+  // Services with limited use of OS APIs. Tighter than kUtility and
+  // suitable for most isolated mojo service endpoints.
+  kService,
+#endif
+
   // GPU process.
   kGpu,
 
@@ -96,11 +106,6 @@
   kZygoteIntermediateSandbox,
 #endif
 
-#if !defined(OS_MAC)
-  // Hosts WebRTC for Sharing Service, uses kUtility on OS_MAC.
-  kSharingService,
-#endif
-
   // The speech recognition service process.
   kSpeechRecognition,
 
diff --git a/sandbox/policy/switches.cc b/sandbox/policy/switches.cc
index 5f4af18..4862627 100644
--- a/sandbox/policy/switches.cc
+++ b/sandbox/policy/switches.cc
@@ -34,7 +34,7 @@
 #endif
 const char kPrintCompositorSandbox[] = "print_compositor";
 const char kAudioSandbox[] = "audio";
-const char kSharingServiceSandbox[] = "sharing_service";
+const char kServiceSandbox[] = "service";
 const char kSpeechRecognitionSandbox[] = "speech_recognition";
 const char kVideoCaptureSandbox[] = "video_capture";
 
diff --git a/sandbox/policy/switches.h b/sandbox/policy/switches.h
index 543816c..6754f7c 100644
--- a/sandbox/policy/switches.h
+++ b/sandbox/policy/switches.h
@@ -35,7 +35,7 @@
 #endif
 SANDBOX_POLICY_EXPORT extern const char kPrintCompositorSandbox[];
 SANDBOX_POLICY_EXPORT extern const char kAudioSandbox[];
-SANDBOX_POLICY_EXPORT extern const char kSharingServiceSandbox[];
+SANDBOX_POLICY_EXPORT extern const char kServiceSandbox[];
 SANDBOX_POLICY_EXPORT extern const char kSpeechRecognitionSandbox[];
 SANDBOX_POLICY_EXPORT extern const char kVideoCaptureSandbox[];
 
diff --git a/sandbox/policy/win/sandbox_win.cc b/sandbox/policy/win/sandbox_win.cc
index e15afef..3b6543c 100644
--- a/sandbox/policy/win/sandbox_win.cc
+++ b/sandbox/policy/win/sandbox_win.cc
@@ -1227,8 +1227,8 @@
       return "PDF Conversion";
     case SandboxType::kMediaFoundationCdm:
       return "Media Foundation CDM";
-    case SandboxType::kSharingService:
-      return "Sharing";
+    case SandboxType::kService:
+      return "Service";
     case SandboxType::kVideoCapture:
       return "Video Capture";
     case SandboxType::kIconReader:
diff --git a/services/viz/public/cpp/compositing/mojom_traits_perftest.cc b/services/viz/public/cpp/compositing/mojom_traits_perftest.cc
index 814a1b4..a860fbb 100644
--- a/services/viz/public/cpp/compositing/mojom_traits_perftest.cc
+++ b/services/viz/public/cpp/compositing/mojom_traits_perftest.cc
@@ -218,8 +218,8 @@
       shared_state1_in->SetAll(
           arbitrary_matrix1, arbitrary_rect1, arbitrary_rect1,
           gfx::MaskFilterInfo(arbitrary_rrectf1), arbitrary_rect2,
-          arbitrary_bool1, arbitrary_bool1, arbitrary_float1,
-          arbitrary_blend_mode1, arbitrary_context_id1);
+          arbitrary_bool1, arbitrary_float1, arbitrary_blend_mode1,
+          arbitrary_context_id1);
 
       auto* texture_in = pass_in->CreateAndAppendDrawQuad<TextureDrawQuad>();
       texture_in->SetAll(
@@ -261,8 +261,8 @@
       shared_state2_in->SetAll(
           arbitrary_matrix2, arbitrary_rect2, arbitrary_rect2,
           gfx::MaskFilterInfo(arbitrary_rrectf2), arbitrary_rect3,
-          arbitrary_bool1, arbitrary_bool1, arbitrary_float2,
-          arbitrary_blend_mode2, arbitrary_context_id2);
+          arbitrary_bool1, arbitrary_float2, arbitrary_blend_mode2,
+          arbitrary_context_id2);
       for (uint32_t j = 0; j < 6; ++j) {
         auto* tile_in = pass_in->CreateAndAppendDrawQuad<TileDrawQuad>();
         tile_in->SetAll(
@@ -279,8 +279,8 @@
       shared_state3_in->SetAll(
           arbitrary_matrix1, arbitrary_rect3, arbitrary_rect3,
           gfx::MaskFilterInfo(arbitrary_rrectf3), arbitrary_rect1,
-          arbitrary_bool1, arbitrary_bool1, arbitrary_float3,
-          arbitrary_blend_mode3, arbitrary_context_id3);
+          arbitrary_bool1, arbitrary_float3, arbitrary_blend_mode3,
+          arbitrary_context_id3);
       for (uint32_t j = 0; j < 5; ++j) {
         auto* solidcolor_in =
             pass_in->CreateAndAppendDrawQuad<SolidColorDrawQuad>();
diff --git a/styleguide/web/web.md b/styleguide/web/web.md
index 13cb37d..c569316 100644
--- a/styleguide/web/web.md
+++ b/styleguide/web/web.md
@@ -254,20 +254,14 @@
 
 ### URLs
 
-* Don't embed data URIs in source files. Instead, use grit's flattening.
+* Don't embed data URIs in source files. Instead, use a relative path to an icon
+  in your UI (and include this icon in the generated grd file), or use an
+  absolute URL for an icon from the shared resources at ui/webui/resources:
 
 ```css
-background-image: url(../path/to/image.svg);
+background-image: url(chrome://resources/images/path/to/image.svg);
 ```
 
-The contents of image.svg are base64-encoded and the `url()` is replaced with
-
-```css
-background-image: url(data:image/svg+xml;base64,...);
-```
-
-if `flattenhtml="true"` is specified in your .grd file.
-
 ### RTL
 
 ```css
@@ -304,7 +298,8 @@
 Guide](https://google.github.io/styleguide/jsguide.html) as well as
 [ECMAScript Features in Chromium](es.md).
 
-* Use `$('element-id')` instead of `document.getElementById`
+* Use `$('element-id')` instead of `document.getElementById`. This function can
+  be imported from util.m.js.
 
 * Use single-quotes instead of double-quotes for all strings.
     * `clang-format` now handles this automatically.
@@ -362,9 +357,42 @@
 
 Also see the [Google Polymer Style Guide](http://go/polymer-style).
 
-* Use a consistent ordering in the “prototype” object passed to `Polymer()`:
+* Elements with UI should have their HTML in a .html file and logic in a JS file
+  with the same name. The HTML should be copied into the final JS file at build
+  time, replacing the special `{__html_template__}` sequence, using the
+  html_to_js BUILD.gn rule. For example the following will paste the contents
+  of my_app.html into the final generated JS file:
+```
+  html_to_js('web_components') {
+    js_files = [ 'my_app.js' ]
+  }
+```
+
+* In new code, use class based syntax for custom elements. Example:
+```js
+import {html, PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.m.js';
+
+class MyAppElement extends PolymerElement {
+  static get is() {
+    return 'my-app';
+  }
+
+  static get template() {
+    return html`{__html_template__}`;
+  }
+
+  static get properties() {
+    return {
+      foo: String,
+    };
+  }
+}
+```
+
+* Use a consistent ordering for common methods (or, in legacy code, the
+  parameters passed to Polymer()):
     * `is`
-    * `behaviors`
+    * `behaviors` (legacy code only)
     * `properties` (public, then private)
     * `hostAttributes`
     * `listeners`, `observers`
@@ -380,14 +408,16 @@
   duplicated in less places, i.e. `@param`).
 
 ```js
-properties: {
-  foo: {type: Number, observer: 'fooChanged_'}
-},
+static get properties() {
+  return {
+    foo: {type: Number, observer: 'fooChanged_'},
+  };
+}
 
 /** @private */
-fooChanged_: function() {
+fooChanged_() {
   this.bar = this.derive(this.foo);
-},
+}
 ```
 
 * Use native `on-click` for click events instead of `on-tap`. 'tap' is a
@@ -420,16 +450,60 @@
 ## Grit processing
 
 Grit is a tool that runs at compile time to pack resources together into
-Chromium.
+Chromium. Resources are packed from grd files. Most Chromium WebUI resources
+should be located in autogenerated grd files created by the generate_grd gn
+rule.
 
 ### Preprocessing
 
-Grit can be used to selectively include or exclude code at compile-time in web
-code.  Preprocessing is enabled by adding the `preprocess="true"` attribute
-inside of a `.grd` file on `<structure>` and `<include>` nodes.
+Sometimes it is helpful to selectively include or exclude code at compile-time.
+This is done using the preprocess_if_expr gn rule, which makes use of a subset
+of grit that reads and processes files for `<if expr>` without running the
+entire grit resource packing process.  Files that require preprocessing are
+passed to the rule as in_files. Preprocessed versions with the same names will
+be written to the specified out_folder and are listed in out_manifest, which can
+be passed to the generate_grd rule to generate entries for them in a grd file.
+
+### Example
+
+The following BUILD.gn example code uses preprocess_if_expr to preprocess any
+`<if expr>` in the final my_app.js file that is generated by the earlier
+html_to_js example. It then uses the manifest from this operation and the
+in_files option to place both the final, preprocessed file and a separate (not
+preprocessed) icon into a generated grd file using generate_grd:
+
+```
+preprocess_folder = "preprocessed"
+preprocess_manifest = "preprocessed_manifest.json"
+
+# Read file from target_gen_dir, where it will be pasted by html_to_js.
+preprocess_if_expr("preprocess") {
+  deps = [ ":web_components" ]
+  in_folder = target_gen_dir
+  in_files = [ "my_app.js" ]
+  out_folder = "$target_gen_dir/$preprocess_folder"
+  out_manifest = "$target_gen_dir/$preprocess_manifest"
+}
+
+# Put the preprocessed file as well as a separate my_icon.svg file in the grd:
+generate_grd("build_grd") {
+  input_files = [ "my_icon.svg" ]
+  input_files_base_dir = rebase_path(".", "//")
+  deps = [ ":preprocess" ]
+  manifest_files = [ "$target_gen_dir/$preprocess_manifest" ]
+  grd_prefix = [ "foo" ]
+  out_grd = "$target_gen_dir/resources.grd"
+}
+```
 
 *** aside
-Note: These preprocessor statements can live in places that surprise linters or
+Note #1:
+In a few legacy resources, preprocessing is enabled by adding the
+`preprocess="true"` attribute inside of a `.grd` file on `<structure>` and
+`<include>` nodes.
+
+Note #2:
+These preprocessor statements can live in places that surprise linters or
 formatters (for example: running clang-format on a .js file with an `<if>` in
 it).  Generally, putting these language-invalid features inside of comments
 helps alleviate problems with unexpected input.
@@ -452,30 +526,16 @@
 `<include src="[path]">` reads the file at `path` and replaces the `<include>`
 tag with the file contents of `[path]`. Don't use `<include>` in new JS code;
 [it is being removed.](https://docs.google.com/document/d/1Z18WTNv28z5FW3smNEm_GtsfVD2IL-CmmAikwjw3ryo/edit?usp=sharing#heading=h.66ycuu6hfi9n)
-Instead, use JS imports in new pages and pages that use JS modules.
+Instead, use JS imports. If there is concern about importing a large number of
+JS files, the optimize_webui build rule supports bundling pages using Rollup.
 
-Grit can read and inline resources when enabled via `flattenhtml="true"`.
+Some legacy UIs use Grit to read and inline resources via `flattenhtml="true"`.
+This option should not be used in new code; instead, use JS imports and bundling
+as needed. Icons can also be placed in an iconset, to avoid importing them
+individually.
 
 *** aside
-Note: The implementation of flattening does HTML parsing and URL detection via regular
-expressions and is not guaranteed to work in all cases.
+Note: The implementation of flattening does HTML parsing and URL detection via
+regular expressions and is not guaranteed to work in all cases. In particular,
+it does not work with any generated resources.
 ***
-
-Example:
-
-```css
-.spinner {
-  background: url(../relative/file/path/to/spinner.svg);
-}
-```
-
-Is transformed to:
-
-```css
-.spinner {
-  background: url(data:image/svg+xml;... base64-encoded content ...);
-}
-```
-
-A minification tool can be specified to Grit (like Closure compiler) to
-transform the code before it's packed into a bundle.
diff --git a/testing/buildbot/chromium.mac.json b/testing/buildbot/chromium.mac.json
index bb61038..9c913d8 100644
--- a/testing/buildbot/chromium.mac.json
+++ b/testing/buildbot/chromium.mac.json
@@ -10794,6 +10794,9 @@
           "script": "//tools/perf/process_perf_results.py"
         },
         "name": "performance_test_suite",
+        "resultdb": {
+          "enable": true
+        },
         "swarming": {
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
diff --git a/testing/buildbot/chromium.win.json b/testing/buildbot/chromium.win.json
index 65c179a..f0b994c1b 100644
--- a/testing/buildbot/chromium.win.json
+++ b/testing/buildbot/chromium.win.json
@@ -5974,6 +5974,9 @@
           "script": "//tools/perf/process_perf_results.py"
         },
         "name": "performance_test_suite",
+        "resultdb": {
+          "enable": true
+        },
         "swarming": {
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
@@ -9074,6 +9077,9 @@
           "script": "//tools/perf/process_perf_results.py"
         },
         "name": "performance_test_suite",
+        "resultdb": {
+          "enable": true
+        },
         "swarming": {
           "can_use_on_swarming_builders": true,
           "hard_timeout": 960,
diff --git a/testing/buildbot/test_suites.pyl b/testing/buildbot/test_suites.pyl
index b2f2438a..f008513 100644
--- a/testing/buildbot/test_suites.pyl
+++ b/testing/buildbot/test_suites.pyl
@@ -4631,6 +4631,9 @@
           ],
           'script': '//tools/perf/process_perf_results.py',
         },
+        'resultdb': {
+          'enable': True,
+        },
       },
     },
 
diff --git a/testing/variations/fieldtrial_testing_config.json b/testing/variations/fieldtrial_testing_config.json
index 551f2e3..344596e 100644
--- a/testing/variations/fieldtrial_testing_config.json
+++ b/testing/variations/fieldtrial_testing_config.json
@@ -1865,6 +1865,28 @@
             ]
         }
     ],
+    "CnameAliasDetectionInSubresourceFilter": [
+        {
+            "platforms": [
+                "android",
+                "android_weblayer",
+                "chromeos",
+                "chromeos_lacros",
+                "linux",
+                "mac",
+                "windows"
+            ],
+            "experiments": [
+                {
+                    "name": "EnabledRendererEnabledBrowser_20210312",
+                    "enable_features": [
+                        "SendCnameAliasesToSubresourceFilterFromBrowser",
+                        "SendCnameAliasesToSubresourceFilterFromRenderer"
+                    ]
+                }
+            ]
+        }
+    ],
     "CodeCacheDeduplicationStudy": [
         {
             "platforms": [
@@ -5176,6 +5198,27 @@
             ]
         }
     ],
+    "ParkableImages": [
+        {
+            "platforms": [
+                "android",
+                "chromeos",
+                "chromeos_lacros",
+                "linux",
+                "mac",
+                "windows"
+            ],
+            "experiments": [
+                {
+                    "name": "Both_V2",
+                    "enable_features": [
+                        "ParkableImagesToDisk",
+                        "UseParkableImageSegmentReader"
+                    ]
+                }
+            ]
+        }
+    ],
     "PartitionAllocLargeThreadCacheSize": [
         {
             "platforms": [
diff --git a/third_party/android_build_tools/art/OWNERS b/third_party/android_build_tools/art/OWNERS
index ea2493b..db1598a 100644
--- a/third_party/android_build_tools/art/OWNERS
+++ b/third_party/android_build_tools/art/OWNERS
@@ -1,3 +1,3 @@
 agrieve@chromium.org
 lizeb@chromium.org
-mheikal@chomrium.org
+mheikal@chromium.org
diff --git a/third_party/blink/public/common/font_access/OWNERS b/third_party/blink/public/common/font_access/OWNERS
index 3378471..14ea266 100644
--- a/third_party/blink/public/common/font_access/OWNERS
+++ b/third_party/blink/public/common/font_access/OWNERS
@@ -1,5 +1,5 @@
 oyiptong@chromium.org
-jsbell@Chromium.org
+jsbell@chromium.org
 
 
 per-file *.mojom=set noparent
diff --git a/third_party/blink/public/mojom/timing/resource_timing.mojom b/third_party/blink/public/mojom/timing/resource_timing.mojom
index ed79625..a20d349 100644
--- a/third_party/blink/public/mojom/timing/resource_timing.mojom
+++ b/third_party/blink/public/mojom/timing/resource_timing.mojom
@@ -87,9 +87,8 @@
   // Whether the connection was reused or not.
   bool did_reuse_connection;
 
-  // Whether the execution context that created this resource entry
-  // was a secure context or not.
-  bool is_secure_context;
+  // Whether the connection used secured transport or not.
+  bool is_secure_transport;
 
   // TODO(dcheng): The way this code works is fairly confusing: it might seem
   // unusual to store policy members like |allow_timing_details| inline, rather
diff --git a/third_party/blink/renderer/bindings/generated_in_modules.gni b/third_party/blink/renderer/bindings/generated_in_modules.gni
index 717643e..3777c0cb 100644
--- a/third_party/blink/renderer/bindings/generated_in_modules.gni
+++ b/third_party/blink/renderer/bindings/generated_in_modules.gni
@@ -2419,8 +2419,8 @@
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_union_boolean_mediatrackconstraints.h",
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_union_canvasfilter_string.cc",
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_union_canvasfilter_string.h",
-  "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_union_canvasgradient_canvaspattern_string.cc",
-  "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_union_canvasgradient_canvaspattern_string.h",
+  "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_union_csscolorvalue_canvasgradient_canvaspattern_string.cc",
+  "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_union_csscolorvalue_canvasgradient_canvaspattern_string.h",
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_union_canvasrenderingcontext2d_gpucanvascontext_imagebitmaprenderingcontext_webgl2renderingcontext_webglrenderingcontext.cc",
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_union_canvasrenderingcontext2d_gpucanvascontext_imagebitmaprenderingcontext_webgl2renderingcontext_webglrenderingcontext.h",
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_union_client_messageport_serviceworker.cc",
diff --git a/third_party/blink/renderer/bindings/modules/v8/generated.gni b/third_party/blink/renderer/bindings/modules/v8/generated.gni
index 955cc76..ca02c0c 100644
--- a/third_party/blink/renderer/bindings/modules/v8/generated.gni
+++ b/third_party/blink/renderer/bindings/modules/v8/generated.gni
@@ -101,8 +101,8 @@
   "$bindings_modules_v8_output_dir/string_or_array_buffer_or_array_buffer_view_or_ndef_message_init.h",
   "$bindings_modules_v8_output_dir/string_or_canvas_filter.cc",
   "$bindings_modules_v8_output_dir/string_or_canvas_filter.h",
-  "$bindings_modules_v8_output_dir/string_or_canvas_gradient_or_canvas_pattern.cc",
-  "$bindings_modules_v8_output_dir/string_or_canvas_gradient_or_canvas_pattern.h",
+  "$bindings_modules_v8_output_dir/string_or_canvas_gradient_or_canvas_pattern_or_css_color_value.cc",
+  "$bindings_modules_v8_output_dir/string_or_canvas_gradient_or_canvas_pattern_or_css_color_value.h",
   "$bindings_modules_v8_output_dir/string_or_document_fragment_or_document.cc",
   "$bindings_modules_v8_output_dir/string_or_document_fragment_or_document.h",
   "$bindings_modules_v8_output_dir/string_or_string_sequence_or_constrain_dom_string_parameters.cc",
diff --git a/third_party/blink/renderer/core/animation/animation.cc b/third_party/blink/renderer/core/animation/animation.cc
index 9dcea594..72cb40f 100644
--- a/third_party/blink/renderer/core/animation/animation.cc
+++ b/third_party/blink/renderer/core/animation/animation.cc
@@ -78,6 +78,17 @@
 
 namespace {
 
+// Ensure the time is bounded such that it can be resolved to microsecond
+// accuracy. Beyond this limit, we can effectively stall an animation when
+// ticking (i.e. b + delta == b for high enough floating point value of b).
+// Furthermore, we can encounter numeric overflows when converting to a
+// time format that is backed by a 64-bit integer.
+bool SupportedTimeValue(double time_in_ms) {
+  return std::abs(time_in_ms) < std::pow(std::numeric_limits<double>::radix,
+                                         std::numeric_limits<double>::digits) /
+                                    1000;
+}
+
 enum PseudoPriority { kNone, kMarker, kBefore, kOther, kAfter };
 
 unsigned NextSequenceNumber() {
@@ -1900,6 +1911,15 @@
   if (EffectivePlaybackRate() == 0)
     reasons |= CompositorAnimations::kInvalidAnimationOrEffect;
 
+  // Animation times with large magnitudes cannot be accurately reflected by
+  // TimeTicks. These animations will stall, be finished next frame, or
+  // stuck in the before phase. In any case, there will be no visible changes
+  // after the initial frame.
+  base::Optional<AnimationTimeDelta> current_time = CurrentTimeInternal();
+  if (current_time.has_value() &&
+      !SupportedTimeValue(current_time.value().InMillisecondsF()))
+    reasons |= CompositorAnimations::kEffectHasUnsupportedTimingParameters;
+
   if (!CurrentTimeInternal())
     reasons |= CompositorAnimations::kInvalidAnimationOrEffect;
 
diff --git a/third_party/blink/renderer/core/animation/animation_test.cc b/third_party/blink/renderer/core/animation/animation_test.cc
index df9ab24..5d7cdd4 100644
--- a/third_party/blink/renderer/core/animation/animation_test.cc
+++ b/third_party/blink/renderer/core/animation/animation_test.cc
@@ -404,18 +404,37 @@
   EXPECT_TIME(10000, GetCurrentTimeMs(animation));
 }
 
-TEST_F(AnimationAnimationTestNoCompositing, SetCurrentTimeMax) {
+TEST_F(AnimationAnimationTestCompositing, SetCurrentTimeMax) {
+  ResetWithCompositedAnimation();
+  EXPECT_EQ(CompositorAnimations::kNoFailure,
+            animation->CheckCanStartAnimationOnCompositor(nullptr));
   double limit = std::numeric_limits<double>::max();
   animation->setCurrentTime(CSSNumberish::FromDouble(limit));
   CSSNumberish current_time;
   animation->currentTime(current_time);
   ExpectRelativeErrorWithinEpsilon(limit, current_time.GetAsDouble());
-
+  EXPECT_TRUE(animation->CheckCanStartAnimationOnCompositor(nullptr) &
+              CompositorAnimations::kEffectHasUnsupportedTimingParameters);
   SimulateFrame(100000);
   animation->currentTime(current_time);
   ExpectRelativeErrorWithinEpsilon(limit, current_time.GetAsDouble());
 }
 
+TEST_F(AnimationAnimationTestCompositing, SetCurrentTimeAboveMaxTimeDelta) {
+  // Similar to the SetCurrentTimeMax test. The limit is much less, but still
+  // too large to be expressed as a 64-bit int and thus not able to run on the
+  // compositor.
+  ResetWithCompositedAnimation();
+  EXPECT_EQ(CompositorAnimations::kNoFailure,
+            animation->CheckCanStartAnimationOnCompositor(nullptr));
+  double limit = 1e30;
+  animation->setCurrentTime(CSSNumberish::FromDouble(limit));
+  CSSNumberish current_time;
+  animation->currentTime(current_time);
+  EXPECT_TRUE(animation->CheckCanStartAnimationOnCompositor(nullptr) &
+              CompositorAnimations::kEffectHasUnsupportedTimingParameters);
+}
+
 TEST_F(AnimationAnimationTestNoCompositing, SetCurrentTimeSetsStartTime) {
   EXPECT_TIME(0, GetStartTimeMs(animation));
   animation->setCurrentTime(CSSNumberish::FromDouble(1000));
diff --git a/third_party/blink/renderer/core/core_initializer.h b/third_party/blink/renderer/core/core_initializer.h
index 8d6b1f52..c21241a 100644
--- a/third_party/blink/renderer/core/core_initializer.h
+++ b/third_party/blink/renderer/core/core_initializer.h
@@ -53,6 +53,7 @@
 class MediaControls;
 class Page;
 class PictureInPictureController;
+struct ScreenInfos;
 class Settings;
 class ShadowRoot;
 class WebLocalFrameClient;
@@ -125,7 +126,9 @@
 
   virtual void DidChangeManifest(LocalFrame&) = 0;
   virtual void NotifyOrientationChanged(LocalFrame&) = 0;
-  virtual void NotifyScreensChanged(LocalFrame&) = 0;
+  // Called with an updated set of ScreenInfos for a local root frame
+  // during a visual property update.
+  virtual void NotifyScreensChanged(LocalFrame& frame, const ScreenInfos&) = 0;
 
  protected:
   // CoreInitializer is only instantiated by subclass ModulesInitializer.
diff --git a/third_party/blink/renderer/core/css/cssom/css_color_value.h b/third_party/blink/renderer/core/css/cssom/css_color_value.h
index 1683d674..2b7c55c8 100644
--- a/third_party/blink/renderer/core/css/cssom/css_color_value.h
+++ b/third_party/blink/renderer/core/css/cssom/css_color_value.h
@@ -28,13 +28,12 @@
 
   StyleValueType GetType() const override { return kColorType; }
 
+  virtual Color ToColor() const = 0;
+
  protected:
   static CSSNumericValue* ToNumberOrPercentage(const CSSNumberish&);
   static CSSNumericValue* ToPercentage(const CSSNumberish&);
   static float ComponentToColorInput(CSSNumericValue*);
-
- private:
-  virtual Color ToColor() const = 0;
 };
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/css/cssom/css_hsl.h b/third_party/blink/renderer/core/css/cssom/css_hsl.h
index b8ace13..e7391d4 100644
--- a/third_party/blink/renderer/core/css/cssom/css_hsl.h
+++ b/third_party/blink/renderer/core/css/cssom/css_hsl.h
@@ -50,13 +50,13 @@
     CSSColorValue::Trace(visitor);
   }
 
+  Color ToColor() const final;
+
  private:
   Member<CSSNumericValue> h_;
   Member<CSSNumericValue> s_;
   Member<CSSNumericValue> l_;
   Member<CSSNumericValue> alpha_;
-
-  Color ToColor() const final;
 };
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/css/cssom/css_rgb.h b/third_party/blink/renderer/core/css/cssom/css_rgb.h
index 90f2518..b4198414 100644
--- a/third_party/blink/renderer/core/css/cssom/css_rgb.h
+++ b/third_party/blink/renderer/core/css/cssom/css_rgb.h
@@ -50,13 +50,13 @@
     CSSColorValue::Trace(visitor);
   }
 
+  Color ToColor() const final;
+
  private:
   Member<CSSNumericValue> r_;
   Member<CSSNumericValue> g_;
   Member<CSSNumericValue> b_;
   Member<CSSNumericValue> alpha_;
-
-  Color ToColor() const final;
 };
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/editing/selection_modifier_line.cc b/third_party/blink/renderer/core/editing/selection_modifier_line.cc
index 392dfb5..e20c56f 100644
--- a/third_party/blink/renderer/core/editing/selection_modifier_line.cc
+++ b/third_party/blink/renderer/core/editing/selection_modifier_line.cc
@@ -88,7 +88,9 @@
                            : AbstractLineBox();
     }
     NGInlineCursor previous_line = cursor_;
-    previous_line.MoveToPreviousLine();
+    do {
+      previous_line.MoveToPreviousIncludingFragmentainer();
+    } while (previous_line && !previous_line.Current().IsLineBox());
     return previous_line ? AbstractLineBox(previous_line) : AbstractLineBox();
   }
 
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 961674f..7f99f28 100644
--- a/third_party/blink/renderer/core/editing/selection_modifier_test.cc
+++ b/third_party/blink/renderer/core/editing/selection_modifier_test.cc
@@ -70,6 +70,8 @@
 }
 
 TEST_F(SelectionModifierTest, MoveByLineMultiColumnSingleText) {
+  RuntimeEnabledFeaturesTestHelpers::ScopedLayoutNGBlockFragmentation
+      block_fragmentation(RuntimeEnabledFeatures::LayoutNGEnabled());
   LoadAhem();
   InsertStyleElement(
       "div { font: 10px/15px Ahem; column-count: 3; width: 20ch; }");
@@ -86,6 +88,17 @@
   EXPECT_EQ("<div>abc def ghi jkl |mno pqr</div>", MoveForwardByLine(modifier));
   EXPECT_EQ("<div>abc def ghi jkl mno |pqr</div>", MoveForwardByLine(modifier));
   EXPECT_EQ("<div>abc def ghi jkl mno pqr|</div>", MoveForwardByLine(modifier));
+
+  EXPECT_EQ("<div>abc def ghi jkl |mno pqr</div>",
+            MoveBackwardByLine(modifier));
+  EXPECT_EQ("<div>abc def ghi |jkl mno pqr</div>",
+            MoveBackwardByLine(modifier));
+  EXPECT_EQ("<div>abc def |ghi jkl mno pqr</div>",
+            MoveBackwardByLine(modifier));
+  EXPECT_EQ("<div>abc |def ghi jkl mno pqr</div>",
+            MoveBackwardByLine(modifier));
+  EXPECT_EQ("<div>|abc def ghi jkl mno pqr</div>",
+            MoveBackwardByLine(modifier));
 }
 
 TEST_F(SelectionModifierTest, MoveByLineVertical) {
diff --git a/third_party/blink/renderer/core/frame/web_frame_widget_impl.cc b/third_party/blink/renderer/core/frame/web_frame_widget_impl.cc
index d04ec5a..44f26ec 100644
--- a/third_party/blink/renderer/core/frame/web_frame_widget_impl.cc
+++ b/third_party/blink/renderer/core/frame/web_frame_widget_impl.cc
@@ -1495,7 +1495,8 @@
 
   if (screen_infos_changed) {
     LocalFrame* frame = LocalRootImpl()->GetFrame();
-    CoreInitializer::GetInstance().NotifyScreensChanged(*frame);
+    CoreInitializer::GetInstance().NotifyScreensChanged(
+        *frame, visual_properties.screen_infos);
     // TODO(crbug.com/1182855): Propagate info and events to remote frames.
   }
 
diff --git a/third_party/blink/renderer/core/layout/ng/inline/ng_inline_cursor.cc b/third_party/blink/renderer/core/layout/ng/inline/ng_inline_cursor.cc
index eabcf9e..f2c835df 100644
--- a/third_party/blink/renderer/core/layout/ng/inline/ng_inline_cursor.cc
+++ b/third_party/blink/renderer/core/layout/ng/inline/ng_inline_cursor.cc
@@ -133,7 +133,7 @@
 bool NGInlineCursor::TrySetRootFragmentItems() {
   DCHECK(root_block_flow_);
   DCHECK(!fragment_items_ || fragment_items_->Equals(items_));
-  for (; fragment_index_ <= max_fragment_index_; AdvanceFragmentIndex()) {
+  for (; fragment_index_ <= max_fragment_index_; IncrementFragmentIndex()) {
     const NGPhysicalBoxFragment* fragment =
         root_block_flow_->GetPhysicalFragment(fragment_index_);
     DCHECK(fragment);
@@ -1220,6 +1220,24 @@
   current_.item_ = &*current_.item_iter_;
 }
 
+void NGInlineCursor::MoveToPreviousFragmentainer() {
+  DCHECK(CanMoveAcrossFragmentainer());
+  if (fragment_index_) {
+    DecrementFragmentIndex();
+    if (TrySetRootFragmentItems()) {
+      MoveToItem(items_.end() - 1);
+      return;
+    }
+  }
+  MakeNull();
+}
+
+void NGInlineCursor::MoveToPreviousIncludingFragmentainer() {
+  MoveToPrevious();
+  if (!Current() && max_fragment_index_ && CanMoveAcrossFragmentainer())
+    MoveToPreviousFragmentainer();
+}
+
 void NGInlineCursor::MoveToFirstIncludingFragmentainer() {
   if (!fragment_index_) {
     MoveToFirst();
@@ -1234,7 +1252,7 @@
 void NGInlineCursor::MoveToNextFragmentainer() {
   DCHECK(CanMoveAcrossFragmentainer());
   if (fragment_index_ < max_fragment_index_) {
-    AdvanceFragmentIndex();
+    IncrementFragmentIndex();
     if (TrySetRootFragmentItems())
       return;
   }
@@ -1530,7 +1548,22 @@
   previously_consumed_block_size_ = LayoutUnit();
 }
 
-void NGInlineCursor::AdvanceFragmentIndex() {
+void NGInlineCursor::DecrementFragmentIndex() {
+  DCHECK(fragment_index_);
+  --fragment_index_;
+  previously_consumed_block_size_ = LayoutUnit();
+  if (!fragment_index_)
+    return;
+  // Note: |LayoutBox::GetPhysicalFragment(wtf_size_t)| is O(1).
+  const auto& root_box_fragment =
+      *root_block_flow_->GetPhysicalFragment(fragment_index_ - 1);
+  if (const auto* break_token =
+          To<NGBlockBreakToken>(root_box_fragment.BreakToken()))
+    previously_consumed_block_size_ = break_token->ConsumedBlockSize();
+}
+
+void NGInlineCursor::IncrementFragmentIndex() {
+  DCHECK_LE(fragment_index_, max_fragment_index_);
   fragment_index_++;
   if (!root_box_fragment_)
     return;
diff --git a/third_party/blink/renderer/core/layout/ng/inline/ng_inline_cursor.h b/third_party/blink/renderer/core/layout/ng/inline/ng_inline_cursor.h
index 3dc2a2c..6abfe385 100644
--- a/third_party/blink/renderer/core/layout/ng/inline/ng_inline_cursor.h
+++ b/third_party/blink/renderer/core/layout/ng/inline/ng_inline_cursor.h
@@ -469,6 +469,14 @@
   // Move the cursor position to previous fragment in pre-order DFS.
   void MoveToPrevious();
 
+  // Move to the previous fragmentainer.
+  // Valid when |CanMoveAcrossFragmentainer|.
+  void MoveToPreviousFragmentainer();
+
+  // Same as |MoveToPrevious|, except this moves to the previous fragmentainer
+  // if |Current| is at the end of a fragmentainer.
+  void MoveToPreviousIncludingFragmentainer();
+
   // Move the current position to previous line. It is error to call other than
   // line box.
   void MoveToPreviousLine();
@@ -621,7 +629,8 @@
   void MoveToNextCulledInlineDescendantIfNeeded();
 
   void ResetFragmentIndex();
-  void AdvanceFragmentIndex();
+  void DecrementFragmentIndex();
+  void IncrementFragmentIndex();
 
   NGInlineCursorPosition current_;
 
diff --git a/third_party/blink/renderer/core/layout/ng/inline/ng_inline_cursor_test.cc b/third_party/blink/renderer/core/layout/ng/inline/ng_inline_cursor_test.cc
index 4b50151..12643796 100644
--- a/third_party/blink/renderer/core/layout/ng/inline/ng_inline_cursor_test.cc
+++ b/third_party/blink/renderer/core/layout/ng/inline/ng_inline_cursor_test.cc
@@ -280,6 +280,8 @@
 }
 
 TEST_P(NGInlineCursorTest, CursorForMovingAcrossFragmentainer) {
+  RuntimeEnabledFeaturesTestHelpers::ScopedLayoutNGBlockFragmentation
+      block_fragmentation(true);
   LoadAhem();
   InsertStyleElement(
       "div { font: 10px/15px Ahem; column-count: 2; width: 20ch; }");
@@ -287,8 +289,6 @@
   // The HTML is rendered as:
   //    abc ghi
   //    def jkl
-  if (!GetLayoutObjectByElementId("m")->IsLayoutNGObject())
-    return;
 
   // MoveTo(LayoutObject) makes |NGInlineCursor| to be able to move across
   // fragmentainer.
@@ -472,6 +472,29 @@
                                 "#span2", "text3", "text4", "text5"));
 }
 
+TEST_P(NGInlineCursorTest, NextIncludingFragmentainer) {
+  RuntimeEnabledFeaturesTestHelpers::ScopedLayoutNGBlockFragmentation
+      block_fragmentation(true);
+  // TDOO(yosin): Remove style for <b> once NGFragmentItem don't do culled
+  // inline.
+  LoadAhem();
+  InsertStyleElement(
+      "b { background: gray; }"
+      "div { font: 10px/15px Ahem; column-count: 2; width: 20ch; }");
+  SetBodyInnerHTML("<div id=m>abc<br>def<br><b>ghi</b><br>jkl</div>");
+  NGInlineCursor cursor;
+  cursor.MoveTo(*GetElementById("m")->firstChild()->GetLayoutObject());
+  ASSERT_TRUE(cursor.IsBlockFragmented()) << cursor;
+  Vector<String> list;
+  while (cursor) {
+    list.push_back(ToDebugString(cursor));
+    cursor.MoveToNextIncludingFragmentainer();
+  }
+  EXPECT_THAT(list,
+              ElementsAre("abc", "", "#linebox", "def", "", "#linebox",
+                          "LayoutInline B", "ghi", "", "#linebox", "jkl"));
+}
+
 TEST_P(NGInlineCursorTest, NextWithEllipsis) {
   LoadAhem();
   InsertStyleElement(
@@ -940,6 +963,29 @@
                                 "abc", "#linebox"));
 }
 
+TEST_P(NGInlineCursorTest, PreviousIncludingFragmentainer) {
+  RuntimeEnabledFeaturesTestHelpers::ScopedLayoutNGBlockFragmentation
+      block_fragmentation(true);
+  // TDOO(yosin): Remove style for <b> once NGFragmentItem don't do culled
+  // inline.
+  LoadAhem();
+  InsertStyleElement(
+      "b { background: gray; }"
+      "div { font: 10px/15px Ahem; column-count: 2; width: 20ch; }");
+  SetBodyInnerHTML("<div id=m>abc<br>def<br><b>ghi</b><br>jkl</div>");
+  NGInlineCursor cursor;
+  cursor.MoveTo(*GetElementById("m")->lastChild()->GetLayoutObject());
+  ASSERT_TRUE(cursor.IsBlockFragmented()) << cursor;
+  Vector<String> list;
+  while (cursor) {
+    list.push_back(ToDebugString(cursor));
+    cursor.MoveToPreviousIncludingFragmentainer();
+  }
+  EXPECT_THAT(list, ElementsAre("jkl", "#linebox", "", "ghi", "LayoutInline B",
+                                "#linebox", "", "def", "#linebox", "", "abc",
+                                "#linebox"));
+}
+
 TEST_P(NGInlineCursorTest, PreviousInlineLeaf) {
   // TDOO(yosin): Remove <style> once NGFragmentItem don't do culled inline.
   InsertStyleElement("b { background: gray; }");
diff --git a/third_party/blink/renderer/core/layout/ng/inline/ng_line_box_fragment_builder.cc b/third_party/blink/renderer/core/layout/ng/inline/ng_line_box_fragment_builder.cc
index 1cd8dd7b..f808c61 100644
--- a/third_party/blink/renderer/core/layout/ng/inline/ng_line_box_fragment_builder.cc
+++ b/third_party/blink/renderer/core/layout/ng/inline/ng_line_box_fragment_builder.cc
@@ -38,12 +38,6 @@
   line_box_type_ = NGPhysicalLineBoxFragment::kEmptyLineBox;
 }
 
-void NGLineBoxFragmentBuilder::AddChild(const NGPhysicalFragment& child,
-                                        const LogicalOffset& child_offset) {
-  PropagateChildData(child, child_offset);
-  AddChildInternal(&child, child_offset);
-}
-
 void NGLineBoxFragmentBuilder::PropagateChildrenData(
     NGLogicalLineItems& children) {
   for (unsigned index = 0; index < children.size(); ++index) {
diff --git a/third_party/blink/renderer/core/layout/ng/inline/ng_line_box_fragment_builder.h b/third_party/blink/renderer/core/layout/ng/inline/ng_line_box_fragment_builder.h
index 1fd67638..2848421 100644
--- a/third_party/blink/renderer/core/layout/ng/inline/ng_line_box_fragment_builder.h
+++ b/third_party/blink/renderer/core/layout/ng/inline/ng_line_box_fragment_builder.h
@@ -72,8 +72,6 @@
     break_token_ = break_token;
   }
 
-  void AddChild(const NGPhysicalFragment&, const LogicalOffset&);
-
   // Propagate data in |ChildList| without adding them to this builder. When
   // adding children as fragment items, they appear in the container, but there
   // are some data that should be propagated through line box fragments.
diff --git a/third_party/blink/renderer/core/layout/ng/ng_fragment_child_iterator.cc b/third_party/blink/renderer/core/layout/ng/ng_fragment_child_iterator.cc
index 6cc93f34d..a84447d 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_fragment_child_iterator.cc
+++ b/third_party/blink/renderer/core/layout/ng/ng_fragment_child_iterator.cc
@@ -116,12 +116,16 @@
           To<NGBlockBreakToken>(previous_fragment->BreakToken());
       current_.break_token_for_fragmentainer_only_ = true;
     } else {
-      // This is a column spanner, or in the case of a fieldset, this could be a
-      // rendered legend. We'll leave |current_block_break_token_| alone here,
-      // as it will be used as in incoming break token when we get to the next
-      // column.
-      DCHECK(previous_fragment->IsRenderedLegend() ||
-             previous_fragment->IsColumnSpanAll());
+      // This is not a fragmentainer. It has to be either a column spanner, a
+      // rendered legend (if the parent fieldset also establishes a multicol
+      // container), or an out-of-flow positioned fragment whose containing
+      // block is the multicol container itself (in which case the OOF doesn't
+      // participate in the fragmentation context established by the multicol
+      // container). We'll leave |current_.block_break_token_| alone here, as it
+      // will be used as an incoming break token when we get to the next column.
+      DCHECK(previous_fragment->IsColumnSpanAll() ||
+             previous_fragment->IsRenderedLegend() ||
+             previous_fragment->IsOutOfFlowPositioned());
     }
   } else if (current_.link_.fragment->IsOutOfFlowPositioned() &&
              !To<NGPhysicalBoxFragment>(current_.link_.fragment.Get())
diff --git a/third_party/blink/renderer/core/layout/ng/ng_layout_input_node.h b/third_party/blink/renderer/core/layout/ng/ng_layout_input_node.h
index f85f411..6b9bc365 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_layout_input_node.h
+++ b/third_party/blink/renderer/core/layout/ng/ng_layout_input_node.h
@@ -88,6 +88,7 @@
     return IsFloating() || IsOutOfFlowPositioned();
   }
   bool IsReplaced() const { return box_->IsLayoutReplaced(); }
+  bool IsFrame() const { return box_->IsFrame(); }
   bool IsAbsoluteContainer() const {
     return box_->CanContainAbsolutePositionObjects();
   }
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 e243d16..57c912c 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
@@ -795,6 +795,14 @@
                                 ReplacedSizeMode mode) {
   DCHECK(node.IsReplaced());
 
+  // TODO(crbug.com/1203464): <frame> elements can be dynamically inserted
+  // into the DOM even though they really only make sense within a <frameset>.
+  // Today, outside a <frameset> they are always 0x0 (even ignoring
+  // border/padding). When outside a <frameset> they likely should create a
+  // LayoutInline instead.
+  if (node.IsFrame())
+    return LogicalSize();
+
   const ComputedStyle& style = node.Style();
   const NGBoxStrut border_padding =
       ComputeBorders(space, node) + ComputePadding(space, style);
diff --git a/third_party/blink/renderer/core/loader/cross_thread_resource_timing_info_copier.cc b/third_party/blink/renderer/core/loader/cross_thread_resource_timing_info_copier.cc
index e9da877b..e4797bf2 100644
--- a/third_party/blink/renderer/core/loader/cross_thread_resource_timing_info_copier.cc
+++ b/third_party/blink/renderer/core/loader/cross_thread_resource_timing_info_copier.cc
@@ -34,7 +34,7 @@
       info->last_redirect_end_time, info->response_end, info->context_type,
       info->request_destination, info->transfer_size, info->encoded_body_size,
       info->decoded_body_size, info->did_reuse_connection,
-      info->is_secure_context, info->allow_timing_details,
+      info->is_secure_transport, info->allow_timing_details,
       info->allow_redirect_details, info->allow_negative_values,
       CloneServerTimingInfoArray(info->server_timing));
 }
diff --git a/third_party/blink/renderer/core/page/scrolling/text_fragment_anchor.cc b/third_party/blink/renderer/core/page/scrolling/text_fragment_anchor.cc
index 45216f6..d54874f 100644
--- a/third_party/blink/renderer/core/page/scrolling/text_fragment_anchor.cc
+++ b/third_party/blink/renderer/core/page/scrolling/text_fragment_anchor.cc
@@ -65,14 +65,11 @@
   return out_selectors->size() > 0;
 }
 
-bool CheckSecurityRestrictions(LocalFrame& frame) {
+// Determines whether the text fragment should be scrolled-to in the context of
+// the current |frame|.
+bool CheckSecurityRestrictionsForScrolling(LocalFrame& frame) {
   // This algorithm checks the security restrictions detailed in
   // https://wicg.github.io/ScrollToTextFragment/#should-allow-a-text-fragment
-  // TODO(bokan): These are really only relevant for observable actions like
-  // scrolling. We should consider allowing highlighting regardless of these
-  // conditions. See the TODO in the relevant spec section:
-  // https://wicg.github.io/ScrollToTextFragment/#restricting-the-text-fragment
-
   if (!frame.Loader().GetDocumentLoader()->ConsumeTextFragmentToken())
     return false;
 
@@ -159,8 +156,12 @@
   if (!frame.GetDocument()->GetFragmentDirective())
     return nullptr;
 
-  if (!CheckSecurityRestrictions(frame))
-    return nullptr;
+  // The security checks only impact observable events like scrolling to the
+  // text fragment. Highlighting the text fragment is non-observable and thus
+  // can safely be done in those cases as well. More details available at
+  // https://wicg.github.io/ScrollToTextFragment/#restricting-the-text-fragment
+  if (!CheckSecurityRestrictionsForScrolling(frame))
+    should_scroll = false;
 
   Vector<TextFragmentSelector> selectors;
 
@@ -403,6 +404,12 @@
     if (AXObjectCache* cache = frame_->GetDocument()->ExistingAXObjectCache())
       cache->HandleScrolledToAnchor(&node);
 
+    // Set the sequential focus navigation to the start of selection.
+    // Even if this element isn't focusable, "Tab" press will
+    // start the search to find the next focusable element from this element.
+    frame_->GetDocument()->SetSequentialFocusNavigationStartingPoint(
+        range.StartPosition().NodeAsRangeFirstNode());
+
     metrics_->DidScroll();
 
     // We scrolled the text into view if the main document scrolled or the text
@@ -422,12 +429,6 @@
       EphemeralRange(ToPositionInDOMTree(range.StartPosition()),
                      ToPositionInDOMTree(range.EndPosition()));
   frame_->GetDocument()->Markers().AddTextFragmentMarker(dom_range);
-
-  // Set the sequential focus navigation to the start of selection.
-  // Even if this element isn't focusable, "Tab" press will
-  // start the search to find the next focusable element from this element.
-  frame_->GetDocument()->SetSequentialFocusNavigationStartingPoint(
-      range.StartPosition().NodeAsRangeFirstNode());
 }
 
 void TextFragmentAnchor::DidFinishSearch() {
diff --git a/third_party/blink/renderer/core/page/scrolling/text_fragment_anchor_test.cc b/third_party/blink/renderer/core/page/scrolling/text_fragment_anchor_test.cc
index 7587b04..d9a20396 100644
--- a/third_party/blink/renderer/core/page/scrolling/text_fragment_anchor_test.cc
+++ b/third_party/blink/renderer/core/page/scrolling/text_fragment_anchor_test.cc
@@ -7,6 +7,7 @@
 #include "components/shared_highlighting/core/common/shared_highlighting_features.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/blink/public/common/input/web_menu_source_type.h"
+#include "third_party/blink/public/mojom/scroll/scroll_enums.mojom-blink.h"
 #include "third_party/blink/public/platform/scheduler/test/renderer_scheduler_test_support.h"
 #include "third_party/blink/renderer/bindings/core/v8/string_or_array_buffer_or_array_buffer_view.h"
 #include "third_party/blink/renderer/bindings/core/v8/v8_font_face_descriptors.h"
@@ -29,6 +30,7 @@
 #include "third_party/blink/renderer/core/layout/layout_object.h"
 #include "third_party/blink/renderer/core/loader/document_loader.h"
 #include "third_party/blink/renderer/core/page/context_menu_controller.h"
+#include "third_party/blink/renderer/core/page/scrolling/fragment_anchor.h"
 #include "third_party/blink/renderer/core/page/scrolling/text_fragment_finder.h"
 #include "third_party/blink/renderer/core/paint/paint_layer_scrollable_area.h"
 #include "third_party/blink/renderer/core/scroll/scrollable_area.h"
@@ -1961,12 +1963,64 @@
   EXPECT_EQ(1u, GetDocument().Markers().Markers().size());
 }
 
-// Ensure we restore the text highlight on page reload
-// TODO(bokan): This test is disabled as this functionality was suppressed in
-// https://crrev.com/c/2135407; it would be better addressed by providing a
-// highlight-only function. See the TODO in
-// https://wicg.github.io/ScrollToTextFragment/#restricting-the-text-fragment
-TEST_F(TextFragmentAnchorTest, DISABLED_HighlightOnReload) {
+// Ensure we restore the highlight on page reload, but that we do not scroll to
+// the text fragment.
+TEST_F(TextFragmentAnchorTest, Reload) {
+  SimRequest request("https://example.com/test.html#:~:text=test", "text/html");
+  LoadURL("https://example.com/test.html#:~:text=test");
+  const String& html = R"HTML(
+    <!DOCTYPE html>
+    <style>
+      body {
+        height: 2200px;
+      }
+      #first {
+        position: absolute;
+        top: 1000px;
+      }
+      #second {
+        position: absolute;
+        top: 2000px;
+      }
+    </style>
+    <p id="first">This is a test page</p>
+    <p id="second">This is some more text</p>
+  )HTML";
+  request.Complete(html);
+  RunAsyncMatchingTasks();
+
+  // Render two frames to handle the async step added by the beforematch event.
+  Compositor().BeginFrame();
+  Compositor().BeginFrame();
+
+  EXPECT_EQ(1u, GetDocument().Markers().Markers().size());
+
+  // Scroll to the top before reloading.
+  GetDocument().View()->GetScrollableArea()->SetScrollOffset(
+      ScrollOffset(), mojom::blink::ScrollType::kProgrammatic);
+  Compositor().BeginFrame();
+
+  // Reload the page.
+  SimRequest reload_request("https://example.com/test.html#:~:text=test",
+                            "text/html");
+  MainFrame().StartReload(WebFrameLoadType::kReload);
+  reload_request.Complete(html);
+
+  // Render two frames to handle the async step added by the beforematch event.
+  Compositor().BeginFrame();
+  Compositor().BeginFrame();
+
+  // Make sure the text fragment is highlighted.
+  EXPECT_EQ(*GetDocument().getElementById("first"), *GetDocument().CssTarget());
+  EXPECT_EQ(1u, GetDocument().Markers().Markers().size());
+
+  // Make sure the page is not scrolled to the highlight, but to the last place
+  // it was before the reload; in this case the top of the page.
+  EXPECT_EQ(ScrollOffset(), LayoutViewport()->GetScrollOffset());
+}
+
+// Ensure we don't restore a dismissed highlight on page reload.
+TEST_F(TextFragmentAnchorTest, ReloadAfterDismissing) {
   SimRequest request("https://example.com/test.html#:~:text=test", "text/html");
   LoadURL("https://example.com/test.html#:~:text=test");
   const String& html = R"HTML(
@@ -1991,13 +2045,20 @@
 
   EXPECT_EQ(1u, GetDocument().Markers().Markers().size());
 
-  // Tap to dismiss the highlight.
-  SimulateClick(10, 10);
-  EXPECT_EQ(0u, GetDocument().Markers().Markers().size());
+  // Dismiss the highlight.
+  EXPECT_TRUE(GetDocument().View()->GetFragmentAnchor()->Dismiss());
 
-  // Reload the page and expect the highlight to be restored.
-  SimRequest reload_request("https://example.com/test.html#:~:text=test",
-                            "text/html");
+  // Make sure the URL was updated.
+  KURL url = GetDocument()
+                 .GetFrame()
+                 ->Loader()
+                 .GetDocumentLoader()
+                 ->GetHistoryItem()
+                 ->Url();
+  EXPECT_EQ("https://example.com/test.html", url.GetString());
+
+  // Reload the page.
+  SimRequest reload_request(url.GetString(), "text/html");
   MainFrame().StartReload(WebFrameLoadType::kReload);
   reload_request.Complete(html);
 
@@ -2005,8 +2066,8 @@
   Compositor().BeginFrame();
   Compositor().BeginFrame();
 
-  EXPECT_EQ(*GetDocument().getElementById("text"), *GetDocument().CssTarget());
-  EXPECT_EQ(1u, GetDocument().Markers().Markers().size());
+  // Make sure the text fragment is not highlighted.
+  EXPECT_EQ(0u, GetDocument().Markers().Markers().size());
 }
 
 // Ensure that we can have text directives combined with non-text directives
diff --git a/third_party/blink/renderer/core/paint/ng/ng_text_fragment_painter.cc b/third_party/blink/renderer/core/paint/ng/ng_text_fragment_painter.cc
index 2d77112..4fc4a4e 100644
--- a/third_party/blink/renderer/core/paint/ng/ng_text_fragment_painter.cc
+++ b/third_party/blink/renderer/core/paint/ng/ng_text_fragment_painter.cc
@@ -30,6 +30,8 @@
 #include "third_party/blink/renderer/core/paint/text_painter_base.h"
 #include "third_party/blink/renderer/core/style/applied_text_decoration.h"
 #include "third_party/blink/renderer/core/style/computed_style.h"
+#include "third_party/blink/renderer/core/svg/svg_element.h"
+#include "third_party/blink/renderer/core/svg/svg_length_context.h"
 #include "third_party/blink/renderer/platform/fonts/character_range.h"
 #include "third_party/blink/renderer/platform/graphics/dom_node_id.h"
 #include "third_party/blink/renderer/platform/graphics/graphics_context_state_saver.h"
@@ -236,8 +238,15 @@
   // Determine text colors.
 
   Node* node = layout_object->GetNode();
+  DCHECK(!svg_inline_text ||
+         (!IsA<SVGElement>(node) && IsA<SVGElement>(node->parentNode())));
   TextPaintStyle text_style =
-      TextPainterBase::TextPaintingStyle(document, style, paint_info);
+      svg_inline_text
+          ? TextPainterBase::SvgTextPaintingStyle(
+                document, SVGLengthContext(To<SVGElement>(node->parentNode())),
+                style, paint_info)
+          : TextPainterBase::TextPaintingStyle(document, style, paint_info);
+  // TODO(crbug.com/1179585): Support SVG Paint Servers (e.g. Gradient, Pattern)
   if (UNLIKELY(selection)) {
     selection->ComputeSelectionStyle(document, style, node, paint_info,
                                      text_style);
diff --git a/third_party/blink/renderer/core/paint/text_painter_base.cc b/third_party/blink/renderer/core/paint/text_painter_base.cc
index 7a64a1a..ba824bd 100644
--- a/third_party/blink/renderer/core/paint/text_painter_base.cc
+++ b/third_party/blink/renderer/core/paint/text_painter_base.cc
@@ -13,6 +13,7 @@
 #include "third_party/blink/renderer/core/paint/text_decoration_info.h"
 #include "third_party/blink/renderer/core/style/computed_style.h"
 #include "third_party/blink/renderer/core/style/shadow_list.h"
+#include "third_party/blink/renderer/core/svg/svg_length_context.h"
 #include "third_party/blink/renderer/platform/fonts/font.h"
 #include "third_party/blink/renderer/platform/geometry/length_functions.h"
 #include "third_party/blink/renderer/platform/graphics/graphics_context.h"
@@ -73,6 +74,34 @@
 }
 
 // static
+void TextPainterBase::AdjustTextStyleForClip(TextPaintStyle& text_style) {
+  // When we use the text as a clip, we only care about the alpha, thus we
+  // make all the colors black.
+  text_style.current_color = Color::kBlack;
+  text_style.fill_color = Color::kBlack;
+  text_style.stroke_color = Color::kBlack;
+  text_style.emphasis_mark_color = Color::kBlack;
+  text_style.shadow = nullptr;
+}
+
+// static
+void TextPainterBase::AdjustTextStyleForPrint(const Document& document,
+                                              const ComputedStyle& style,
+                                              TextPaintStyle& text_style) {
+  // Adjust text color when printing with a white background.
+  bool force_background_to_white =
+      BoxPainterBase::ShouldForceWhiteBackgroundForPrintEconomy(document,
+                                                                style);
+  if (force_background_to_white) {
+    text_style.fill_color = TextColorForWhiteBackground(text_style.fill_color);
+    text_style.stroke_color =
+        TextColorForWhiteBackground(text_style.stroke_color);
+    text_style.emphasis_mark_color =
+        TextColorForWhiteBackground(text_style.emphasis_mark_color);
+  }
+}
+
+// static
 void TextPainterBase::UpdateGraphicsContext(
     GraphicsContext& context,
     const TextPaintStyle& text_style,
@@ -159,13 +188,7 @@
   text_style.color_scheme = style.UsedColorScheme();
 
   if (paint_info.phase == PaintPhase::kTextClip) {
-    // When we use the text as a clip, we only care about the alpha, thus we
-    // make all the colors black.
-    text_style.current_color = Color::kBlack;
-    text_style.fill_color = Color::kBlack;
-    text_style.stroke_color = Color::kBlack;
-    text_style.emphasis_mark_color = Color::kBlack;
-    text_style.shadow = nullptr;
+    AdjustTextStyleForClip(text_style);
   } else {
     text_style.current_color =
         style.VisitedDependentColor(GetCSSPropertyColor());
@@ -177,18 +200,54 @@
         style.VisitedDependentColor(GetCSSPropertyWebkitTextEmphasisColor());
     text_style.shadow = style.TextShadow();
 
-    // Adjust text color when printing with a white background.
-    bool force_background_to_white =
-        BoxPainterBase::ShouldForceWhiteBackgroundForPrintEconomy(document,
-                                                                  style);
-    if (force_background_to_white) {
-      text_style.fill_color =
-          TextColorForWhiteBackground(text_style.fill_color);
-      text_style.stroke_color =
-          TextColorForWhiteBackground(text_style.stroke_color);
-      text_style.emphasis_mark_color =
-          TextColorForWhiteBackground(text_style.emphasis_mark_color);
+    AdjustTextStyleForPrint(document, style, text_style);
+  }
+
+  return text_style;
+}
+
+// static
+TextPaintStyle TextPainterBase::SvgTextPaintingStyle(
+    const Document& document,
+    const SVGLengthContext& length_context,
+    const ComputedStyle& style,
+    const PaintInfo& paint_info) {
+  TextPaintStyle text_style;
+  text_style.stroke_width =
+      style.HasStroke() ? length_context.ValueForLength(style.StrokeWidth())
+                        : 0;
+  text_style.color_scheme = style.UsedColorScheme();
+
+  if (paint_info.phase == PaintPhase::kTextClip) {
+    AdjustTextStyleForClip(text_style);
+  } else {
+    text_style.current_color =
+        style.VisitedDependentColor(GetCSSPropertyColor());
+
+    const SVGPaint fill_paint = style.FillPaint();
+    if (fill_paint.IsNone()) {
+      text_style.fill_color = Color::kTransparent;
+    } else if (fill_paint.HasColor()) {
+      const Color color = style.VisitedDependentColor(GetCSSPropertyFill());
+      const float alpha = style.FillOpacity();
+      text_style.fill_color = ScaleAlpha(color.Rgb(), alpha);
+    } else {
+      text_style.fill_color = Color::kBlack;
     }
+
+    if (style.StrokePaint().HasColor()) {
+      const Color color = style.VisitedDependentColor(GetCSSPropertyStroke());
+      const float alpha = style.StrokeOpacity();
+      text_style.stroke_color = ScaleAlpha(color.Rgb(), alpha);
+    } else {
+      text_style.stroke_color = Color::kTransparent;
+    }
+
+    text_style.emphasis_mark_color =
+        style.VisitedDependentColor(GetCSSPropertyWebkitTextEmphasisColor());
+    text_style.shadow = style.TextShadow();
+
+    AdjustTextStyleForPrint(document, style, text_style);
   }
 
   return text_style;
diff --git a/third_party/blink/renderer/core/paint/text_painter_base.h b/third_party/blink/renderer/core/paint/text_painter_base.h
index cdcb917..ca5e8c6a 100644
--- a/third_party/blink/renderer/core/paint/text_painter_base.h
+++ b/third_party/blink/renderer/core/paint/text_painter_base.h
@@ -25,6 +25,7 @@
 class GraphicsContext;
 class GraphicsContextStateSaver;
 class Node;
+class SVGLengthContext;
 class TextDecorationOffsetBase;
 struct PaintInfo;
 
@@ -81,6 +82,10 @@
   static TextPaintStyle TextPaintingStyle(const Document&,
                                           const ComputedStyle&,
                                           const PaintInfo&);
+  static TextPaintStyle SvgTextPaintingStyle(const Document&,
+                                             const SVGLengthContext&,
+                                             const ComputedStyle&,
+                                             const PaintInfo&);
   static TextPaintStyle SelectionPaintingStyle(
       const Document&,
       const ComputedStyle&,
@@ -93,6 +98,10 @@
                                   RotationDirection);
 
  protected:
+  static void AdjustTextStyleForClip(TextPaintStyle&);
+  static void AdjustTextStyleForPrint(const Document&,
+                                      const ComputedStyle&,
+                                      TextPaintStyle&);
   void UpdateGraphicsContext(const TextPaintStyle& style,
                              GraphicsContextStateSaver& saver) {
     UpdateGraphicsContext(graphics_context_, style, horizontal_, saver);
diff --git a/third_party/blink/renderer/core/timing/performance.cc b/third_party/blink/renderer/core/timing/performance.cc
index a675d27..ce2114c 100644
--- a/third_party/blink/renderer/core/timing/performance.cc
+++ b/third_party/blink/renderer/core/timing/performance.cc
@@ -181,7 +181,7 @@
   return nullptr;
 }
 
-MemoryInfo* Performance::memory() const {
+MemoryInfo* Performance::memory(ScriptState*) const {
   return nullptr;
 }
 
@@ -565,9 +565,10 @@
   result->encoded_body_size = final_response.EncodedBodyLength();
   result->decoded_body_size = final_response.DecodedBodyLength();
   result->did_reuse_connection = final_response.ConnectionReused();
-  // TODO(crbug.com/1153336) Use network::IsUrlPotentiallyTrustworthy().
-  result->is_secure_context =
-      SecurityOrigin::IsSecure(final_response.ResponseUrl());
+  // Use SecurityOrigin::Create to handle cases like blob:https://.
+  result->is_secure_transport = base::Contains(
+      url::GetSecureSchemes(),
+      SecurityOrigin::Create(final_response.ResponseUrl())->Protocol().Ascii());
   result->allow_negative_values = info.NegativeAllowed();
 
   if (result->allow_timing_details) {
diff --git a/third_party/blink/renderer/core/timing/performance.h b/third_party/blink/renderer/core/timing/performance.h
index 27e583d..54f3a7d 100644
--- a/third_party/blink/renderer/core/timing/performance.h
+++ b/third_party/blink/renderer/core/timing/performance.h
@@ -98,7 +98,7 @@
   // Overriden by WindowPerformance but not by WorkerPerformance.
   virtual PerformanceTiming* timing() const;
   virtual PerformanceNavigation* navigation() const;
-  virtual MemoryInfo* memory() const;
+  virtual MemoryInfo* memory(ScriptState*) const;
   virtual ScriptPromise measureUserAgentSpecificMemory(
       ScriptState*,
       ExceptionState& exception_state) const;
diff --git a/third_party/blink/renderer/core/timing/performance.idl b/third_party/blink/renderer/core/timing/performance.idl
index 44502c5..b40462bc 100644
--- a/third_party/blink/renderer/core/timing/performance.idl
+++ b/third_party/blink/renderer/core/timing/performance.idl
@@ -65,7 +65,7 @@
 
     // TODO(foolip): There is no spec for the Memory Info API, see blink-dev:
     // https://groups.google.com/a/chromium.org/d/msg/blink-dev/g5YRCGpC9vs/b4OJz71NmPwJ
-    [Exposed=Window, Measure] readonly attribute MemoryInfo memory;
+    [Exposed=Window, Measure, CallWith=ScriptState] readonly attribute MemoryInfo memory;
 
     [MeasureAs=MeasureMemory, Exposed=Window, CallWith=ScriptState, RuntimeEnabled=MeasureMemory, RaisesException] Promise<MemoryMeasurement> measureUserAgentSpecificMemory();
 
diff --git a/third_party/blink/renderer/core/timing/performance_navigation_timing.cc b/third_party/blink/renderer/core/timing/performance_navigation_timing.cc
index 28ed5866..2e0ed206 100644
--- a/third_party/blink/renderer/core/timing/performance_navigation_timing.cc
+++ b/third_party/blink/renderer/core/timing/performance_navigation_timing.cc
@@ -57,8 +57,8 @@
                : g_empty_atom,
           time_origin,
           cross_origin_isolated_capability,
-          // TODO(crbug.com/1153336) Use network::IsUrlPotentiallyTrustworthy().
-          SecurityOrigin::IsSecure(window->Url()),
+          base::Contains(url::GetSecureSchemes(),
+                         window->Url().Protocol().Ascii()),
           std::move(server_timing),
           window),
       ExecutionContextClient(window),
diff --git a/third_party/blink/renderer/core/timing/performance_resource_timing.cc b/third_party/blink/renderer/core/timing/performance_resource_timing.cc
index 61c4f96..eb3d907 100644
--- a/third_party/blink/renderer/core/timing/performance_resource_timing.cc
+++ b/third_party/blink/renderer/core/timing/performance_resource_timing.cc
@@ -87,7 +87,7 @@
       allow_timing_details_(info.allow_timing_details),
       allow_redirect_details_(info.allow_redirect_details),
       allow_negative_value_(info.allow_negative_values),
-      is_secure_context_(info.is_secure_context),
+      is_secure_transport_(info.is_secure_transport),
       server_timing_(
           PerformanceServerTiming::FromParsedServerTiming(info.server_timing)),
       worker_timing_receiver_(this, context) {
@@ -105,7 +105,7 @@
     const AtomicString& name,
     base::TimeTicks time_origin,
     bool cross_origin_isolated_capability,
-    bool is_secure_context,
+    bool is_secure_transport,
     HeapVector<Member<PerformanceServerTiming>> server_timing,
     ExecutionContext* context)
     : PerformanceEntry(name, 0.0, 0.0),
@@ -113,7 +113,7 @@
       cross_origin_isolated_capability_(cross_origin_isolated_capability),
       context_type_(mojom::blink::RequestContextType::HYPERLINK),
       request_destination_(network::mojom::RequestDestination::kDocument),
-      is_secure_context_(is_secure_context),
+      is_secure_transport_(is_secure_transport),
       server_timing_(std::move(server_timing)),
       worker_timing_receiver_(this, context) {
   DCHECK(context);
@@ -316,7 +316,7 @@
 }
 
 DOMHighResTimeStamp PerformanceResourceTiming::secureConnectionStart() const {
-  if (!AllowTimingDetails() || !is_secure_context_)
+  if (!AllowTimingDetails() || !is_secure_transport_)
     return 0.0;
 
   // Step 2 of
diff --git a/third_party/blink/renderer/core/timing/performance_resource_timing.h b/third_party/blink/renderer/core/timing/performance_resource_timing.h
index 44d02ba..8149bd7 100644
--- a/third_party/blink/renderer/core/timing/performance_resource_timing.h
+++ b/third_party/blink/renderer/core/timing/performance_resource_timing.h
@@ -61,7 +61,7 @@
       const AtomicString& name,
       base::TimeTicks time_origin,
       bool cross_origin_isolated_capability,
-      bool is_secure_context,
+      bool is_secure_transport,
       HeapVector<Member<PerformanceServerTiming>> server_timing,
       ExecutionContext* context);
   PerformanceResourceTiming(
@@ -148,7 +148,7 @@
   bool allow_timing_details_ = false;
   bool allow_redirect_details_ = false;
   bool allow_negative_value_ = false;
-  bool is_secure_context_ = false;
+  bool is_secure_transport_ = false;
   HeapVector<Member<PerformanceServerTiming>> server_timing_;
   HeapVector<Member<PerformanceEntry>> worker_timing_;
 
diff --git a/third_party/blink/renderer/core/timing/window_performance.cc b/third_party/blink/renderer/core/timing/window_performance.cc
index 60f3ed04..fc8adb6 100644
--- a/third_party/blink/renderer/core/timing/window_performance.cc
+++ b/third_party/blink/renderer/core/timing/window_performance.cc
@@ -189,7 +189,7 @@
   return navigation_.Get();
 }
 
-MemoryInfo* WindowPerformance::memory() const {
+MemoryInfo* WindowPerformance::memory(ScriptState* script_state) const {
   // The performance.memory() API has been improved so that we report precise
   // values when the process is locked to a site. The intent (which changed
   // course over time about what changes would be implemented) can be found at
@@ -201,10 +201,10 @@
                                            : MemoryInfo::Precision::Bucketized);
   // Record Web Memory UKM.
   const uint64_t kBytesInKB = 1024;
-  ukm::builders::PerformanceAPI_Memory_Legacy(
-      GetExecutionContext()->UkmSourceID())
+  auto* execution_context = ExecutionContext::From(script_state);
+  ukm::builders::PerformanceAPI_Memory_Legacy(execution_context->UkmSourceID())
       .SetJavaScript(memory_info->usedJSHeapSize() / kBytesInKB)
-      .Record(GetExecutionContext()->UkmRecorder());
+      .Record(execution_context->UkmRecorder());
   return memory_info;
 }
 
diff --git a/third_party/blink/renderer/core/timing/window_performance.h b/third_party/blink/renderer/core/timing/window_performance.h
index 7c33989..d493faa 100644
--- a/third_party/blink/renderer/core/timing/window_performance.h
+++ b/third_party/blink/renderer/core/timing/window_performance.h
@@ -64,7 +64,7 @@
   PerformanceTiming* timing() const override;
   PerformanceNavigation* navigation() const override;
 
-  MemoryInfo* memory() const override;
+  MemoryInfo* memory(ScriptState*) const override;
 
   EventCounts* eventCounts() override;
 
diff --git a/third_party/blink/renderer/modules/canvas/canvas2d/base_rendering_context_2d.cc b/third_party/blink/renderer/modules/canvas/canvas2d/base_rendering_context_2d.cc
index 8940f36..7fc1509 100644
--- a/third_party/blink/renderer/modules/canvas/canvas2d/base_rendering_context_2d.cc
+++ b/third_party/blink/renderer/modules/canvas/canvas2d/base_rendering_context_2d.cc
@@ -12,6 +12,7 @@
 #include "base/metrics/histogram_functions.h"
 #include "base/numerics/checked_math.h"
 #include "third_party/blink/public/common/features.h"
+#include "third_party/blink/renderer/core/css/cssom/css_color_value.h"
 #include "third_party/blink/renderer/core/css/parser/css_parser.h"
 #include "third_party/blink/renderer/core/html/canvas/text_metrics.h"
 #include "third_party/blink/renderer/core/html/media/html_video_element.h"
@@ -171,7 +172,7 @@
 
 static inline void ConvertCanvasStyleToUnionType(
     CanvasStyle* style,
-    StringOrCanvasGradientOrCanvasPattern& return_value) {
+    StringOrCanvasGradientOrCanvasPatternOrCSSColorValue& return_value) {
   if (CanvasGradient* gradient = style->GetCanvasGradient()) {
     return_value.SetCanvasGradient(gradient);
     return;
@@ -180,11 +181,11 @@
     return_value.SetCanvasPattern(pattern);
     return;
   }
-  return_value.SetString(style->GetColor());
+  return_value.SetString(style->GetColorAsString());
 }
 
 void BaseRenderingContext2D::IdentifiabilityMaybeUpdateForStyleUnion(
-    const StringOrCanvasGradientOrCanvasPattern& style) {
+    const StringOrCanvasGradientOrCanvasPatternOrCSSColorValue& style) {
   if (style.IsString()) {
     identifiability_study_helper_.MaybeUpdateBuilder(
         IdentifiabilityBenignStringToken(style.GetAsString()));
@@ -207,12 +208,12 @@
 }
 
 void BaseRenderingContext2D::strokeStyle(
-    StringOrCanvasGradientOrCanvasPattern& return_value) const {
+    StringOrCanvasGradientOrCanvasPatternOrCSSColorValue& return_value) const {
   ConvertCanvasStyleToUnionType(GetState().StrokeStyle(), return_value);
 }
 
 void BaseRenderingContext2D::setStrokeStyle(
-    const StringOrCanvasGradientOrCanvasPattern& style) {
+    const StringOrCanvasGradientOrCanvasPatternOrCSSColorValue& style) {
   DCHECK(!style.IsNull());
   identifiability_study_helper_.MaybeUpdateBuilder(CanvasOps::kSetStrokeStyle);
   IdentifiabilityMaybeUpdateForStyleUnion(style);
@@ -241,6 +242,12 @@
       SetOriginTaintedByContent();
 
     canvas_style = MakeGarbageCollected<CanvasStyle>(canvas_pattern);
+  } else if (style.IsCSSColorValue()) {
+    if (!RuntimeEnabledFeatures::NewCanvas2DAPIEnabled())
+      return;
+    CSSColorValue* css_color = style.GetAsCSSColorValue();
+    canvas_style =
+        MakeGarbageCollected<CanvasStyle>(css_color->ToColor().Rgb());
   }
 
   DCHECK(canvas_style);
@@ -251,12 +258,12 @@
 }
 
 void BaseRenderingContext2D::fillStyle(
-    StringOrCanvasGradientOrCanvasPattern& return_value) const {
+    StringOrCanvasGradientOrCanvasPatternOrCSSColorValue& return_value) const {
   ConvertCanvasStyleToUnionType(GetState().FillStyle(), return_value);
 }
 
 void BaseRenderingContext2D::setFillStyle(
-    const StringOrCanvasGradientOrCanvasPattern& style) {
+    const StringOrCanvasGradientOrCanvasPatternOrCSSColorValue& style) {
   DCHECK(!style.IsNull());
   ValidateStateStack();
   identifiability_study_helper_.MaybeUpdateBuilder(CanvasOps::kSetFillStyle);
@@ -286,6 +293,12 @@
       SetOriginTaintedByContent();
     }
     canvas_style = MakeGarbageCollected<CanvasStyle>(canvas_pattern);
+  } else if (style.IsCSSColorValue()) {
+    if (!RuntimeEnabledFeatures::NewCanvas2DAPIEnabled())
+      return;
+    CSSColorValue* css_color = style.GetAsCSSColorValue();
+    canvas_style =
+        MakeGarbageCollected<CanvasStyle>(css_color->ToColor().Rgb());
   }
 
   DCHECK(canvas_style);
diff --git a/third_party/blink/renderer/modules/canvas/canvas2d/base_rendering_context_2d.h b/third_party/blink/renderer/modules/canvas/canvas2d/base_rendering_context_2d.h
index 5b5b956..1e11c6455 100644
--- a/third_party/blink/renderer/modules/canvas/canvas2d/base_rendering_context_2d.h
+++ b/third_party/blink/renderer/modules/canvas/canvas2d/base_rendering_context_2d.h
@@ -9,7 +9,7 @@
 #include "base/macros.h"
 #include "third_party/blink/renderer/bindings/modules/v8/canvas_image_source.h"
 #include "third_party/blink/renderer/bindings/modules/v8/string_or_canvas_filter.h"
-#include "third_party/blink/renderer/bindings/modules/v8/string_or_canvas_gradient_or_canvas_pattern.h"
+#include "third_party/blink/renderer/bindings/modules/v8/string_or_canvas_gradient_or_canvas_pattern_or_css_color_value.h"
 #include "third_party/blink/renderer/core/geometry/dom_matrix.h"
 #include "third_party/blink/renderer/core/html/canvas/image_data.h"
 #include "third_party/blink/renderer/modules/canvas/canvas2d/canvas_gradient.h"
@@ -32,11 +32,13 @@
  public:
   ~BaseRenderingContext2D() override;
 
-  void strokeStyle(StringOrCanvasGradientOrCanvasPattern&) const;
-  void setStrokeStyle(const StringOrCanvasGradientOrCanvasPattern&);
+  void strokeStyle(StringOrCanvasGradientOrCanvasPatternOrCSSColorValue&) const;
+  void setStrokeStyle(
+      const StringOrCanvasGradientOrCanvasPatternOrCSSColorValue&);
 
-  void fillStyle(StringOrCanvasGradientOrCanvasPattern&) const;
-  void setFillStyle(const StringOrCanvasGradientOrCanvasPattern&);
+  void fillStyle(StringOrCanvasGradientOrCanvasPatternOrCSSColorValue&) const;
+  void setFillStyle(
+      const StringOrCanvasGradientOrCanvasPatternOrCSSColorValue&);
 
   double lineWidth() const;
   void setLineWidth(double);
@@ -543,7 +545,7 @@
                            base::TimeTicks start_time);
 
   void IdentifiabilityMaybeUpdateForStyleUnion(
-      const StringOrCanvasGradientOrCanvasPattern& style);
+      const StringOrCanvasGradientOrCanvasPatternOrCSSColorValue& style);
 
   RespectImageOrientationEnum RespectImageOrientationInternal(
       CanvasImageSource*);
diff --git a/third_party/blink/renderer/modules/canvas/canvas2d/canvas_rendering_context_2d.idl b/third_party/blink/renderer/modules/canvas/canvas2d/canvas_rendering_context_2d.idl
index e1d1473..afe30ee 100644
--- a/third_party/blink/renderer/modules/canvas/canvas2d/canvas_rendering_context_2d.idl
+++ b/third_party/blink/renderer/modules/canvas/canvas2d/canvas_rendering_context_2d.idl
@@ -87,8 +87,8 @@
     [MeasureAs=Canvas2DImageSmoothingQuality]attribute ImageSmoothingQuality imageSmoothingQuality; // (default "low")
 
     // colors and styles (see also the CanvasDrawingStyles interface)
-    attribute (DOMString or CanvasGradient or CanvasPattern) strokeStyle; // (default black)
-    attribute (DOMString or CanvasGradient or CanvasPattern) fillStyle; // (default black)
+    attribute (DOMString or CanvasGradient or CanvasPattern or CSSColorValue) strokeStyle; // (default black)
+    attribute (DOMString or CanvasGradient or CanvasPattern or CSSColorValue) fillStyle; // (default black)
     CanvasGradient createLinearGradient(double x0, double y0, double x1, double y1);
     [RaisesException] CanvasGradient createRadialGradient(double x0, double y0, double r0, double x1, double y1, double r1);
     [RuntimeEnabled=NewCanvas2DAPI] CanvasGradient createConicGradient(double startAngle, double cx, double cy);
diff --git a/third_party/blink/renderer/modules/canvas/canvas2d/canvas_rendering_context_2d_api_test.cc b/third_party/blink/renderer/modules/canvas/canvas2d/canvas_rendering_context_2d_api_test.cc
index 9b5fe27f..131612c 100644
--- a/third_party/blink/renderer/modules/canvas/canvas2d/canvas_rendering_context_2d_api_test.cc
+++ b/third_party/blink/renderer/modules/canvas/canvas2d/canvas_rendering_context_2d_api_test.cc
@@ -9,6 +9,7 @@
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/blink/public/common/privacy_budget/identifiability_study_settings.h"
 #include "third_party/blink/public/common/privacy_budget/identifiability_study_settings_provider.h"
+#include "third_party/blink/renderer/bindings/modules/v8/string_or_canvas_gradient_or_canvas_pattern_or_css_color_value.h"
 #include "third_party/blink/renderer/bindings/modules/v8/v8_hit_region_options.h"
 #include "third_party/blink/renderer/core/accessibility/ax_context.h"
 #include "third_party/blink/renderer/core/dom/document.h"
@@ -136,7 +137,7 @@
 
 String TrySettingStrokeStyle(CanvasRenderingContext2D* ctx,
                              const String& value) {
-  StringOrCanvasGradientOrCanvasPattern arg1, arg2, arg3;
+  StringOrCanvasGradientOrCanvasPatternOrCSSColorValue arg1, arg2, arg3;
   arg1.SetString("#666");
   ctx->setStrokeStyle(arg1);
   arg2.SetString(value);
@@ -147,7 +148,7 @@
 }
 
 String TrySettingFillStyle(CanvasRenderingContext2D* ctx, const String& value) {
-  StringOrCanvasGradientOrCanvasPattern arg1, arg2, arg3;
+  StringOrCanvasGradientOrCanvasPatternOrCSSColorValue arg1, arg2, arg3;
   arg1.SetString("#666");
   ctx->setFillStyle(arg1);
   arg2.SetString(value);
@@ -190,14 +191,14 @@
   CreateContext(kNonOpaque);
 
   {
-    StringOrCanvasGradientOrCanvasPattern value;
+    StringOrCanvasGradientOrCanvasPatternOrCSSColorValue value;
     Context2D()->strokeStyle(value);
     EXPECT_TRUE(value.IsString());
     EXPECT_EQ(String("#000000"), value.GetAsString());
   }
 
   {
-    StringOrCanvasGradientOrCanvasPattern value;
+    StringOrCanvasGradientOrCanvasPatternOrCSSColorValue value;
     Context2D()->fillStyle(value);
     EXPECT_TRUE(value.IsString());
     EXPECT_EQ(String("#000000"), value.GetAsString());
@@ -500,7 +501,7 @@
   StudyParticipationRaii study_participation_raii;
   CreateContext(kNonOpaque);
 
-  StringOrCanvasGradientOrCanvasPattern style;
+  StringOrCanvasGradientOrCanvasPatternOrCSSColorValue style;
   style.SetString("blue");
   Context2D()->setStrokeStyle(style);
   EXPECT_EQ(INT64_C(2059186787917525779),
@@ -514,7 +515,7 @@
   StudyParticipationRaii study_participation_raii;
   CreateContext(kNonOpaque);
 
-  StringOrCanvasGradientOrCanvasPattern style;
+  StringOrCanvasGradientOrCanvasPatternOrCSSColorValue style;
   style.SetString("blue");
   Context2D()->setFillStyle(style);
   EXPECT_EQ(INT64_C(-6322980727372024031),
@@ -534,7 +535,7 @@
   Context2D()->setFont("Helvetica");
   Context2D()->setTextBaseline("bottom");
   Context2D()->setTextAlign("right");
-  StringOrCanvasGradientOrCanvasPattern style;
+  StringOrCanvasGradientOrCanvasPatternOrCSSColorValue style;
   style.SetString("red");
   Context2D()->setFillStyle(style);
   Context2D()->fillText("Bye", 4.0, 3.0);
diff --git a/third_party/blink/renderer/modules/canvas/canvas2d/canvas_rendering_context_2d_test.cc b/third_party/blink/renderer/modules/canvas/canvas2d/canvas_rendering_context_2d_test.cc
index dadfbad..47be6d90 100644
--- a/third_party/blink/renderer/modules/canvas/canvas2d/canvas_rendering_context_2d_test.cc
+++ b/third_party/blink/renderer/modules/canvas/canvas2d/canvas_rendering_context_2d_test.cc
@@ -17,6 +17,7 @@
 #include "third_party/blink/renderer/bindings/core/v8/v8_binding_for_core.h"
 #include "third_party/blink/renderer/bindings/core/v8/v8_binding_for_testing.h"
 #include "third_party/blink/renderer/bindings/core/v8/v8_image_bitmap_options.h"
+#include "third_party/blink/renderer/bindings/modules/v8/string_or_canvas_gradient_or_canvas_pattern_or_css_color_value.h"
 #include "third_party/blink/renderer/core/dom/document.h"
 #include "third_party/blink/renderer/core/frame/frame_test_helpers.h"
 #include "third_party/blink/renderer/core/frame/local_dom_window.h"
@@ -171,8 +172,8 @@
       visitor->Trace(alpha_gradient_);
     }
 
-    StringOrCanvasGradientOrCanvasPattern opaque_gradient_;
-    StringOrCanvasGradientOrCanvasPattern alpha_gradient_;
+    StringOrCanvasGradientOrCanvasPatternOrCSSColorValue opaque_gradient_;
+    StringOrCanvasGradientOrCanvasPatternOrCSSColorValue alpha_gradient_;
   };
 
   // TODO(Oilpan): avoid tedious part-object wrapper by supporting on-heap
@@ -187,10 +188,10 @@
   FakeImageSource alpha_bitmap_;
   scoped_refptr<viz::TestContextProvider> test_context_provider_;
 
-  StringOrCanvasGradientOrCanvasPattern& OpaqueGradient() {
+  StringOrCanvasGradientOrCanvasPatternOrCSSColorValue& OpaqueGradient() {
     return wrap_gradients_->opaque_gradient_;
   }
-  StringOrCanvasGradientOrCanvasPattern& AlphaGradient() {
+  StringOrCanvasGradientOrCanvasPatternOrCSSColorValue& AlphaGradient() {
     return wrap_gradients_->alpha_gradient_;
   }
 };
@@ -250,7 +251,7 @@
   alpha_gradient->addColorStop(1, String("rgba(0, 0, 255, 0.5)"),
                                exception_state);
   EXPECT_FALSE(exception_state.HadException());
-  StringOrCanvasGradientOrCanvasPattern wrapped_alpha_gradient;
+  StringOrCanvasGradientOrCanvasPatternOrCSSColorValue wrapped_alpha_gradient;
   AlphaGradient().SetCanvasGradient(alpha_gradient);
 
   global_memory_cache_ =
diff --git a/third_party/blink/renderer/modules/canvas/canvas2d/canvas_style.cc b/third_party/blink/renderer/modules/canvas/canvas2d/canvas_style.cc
index 28847c8..67330e1 100644
--- a/third_party/blink/renderer/modules/canvas/canvas2d/canvas_style.cc
+++ b/third_party/blink/renderer/modules/canvas/canvas2d/canvas_style.cc
@@ -31,6 +31,7 @@
 #include "base/memory/scoped_refptr.h"
 #include "third_party/blink/renderer/core/css/css_property_names.h"
 #include "third_party/blink/renderer/core/css/css_property_value_set.h"
+#include "third_party/blink/renderer/core/css/cssom/css_color_value.h"
 #include "third_party/blink/renderer/core/css/parser/css_parser.h"
 #include "third_party/blink/renderer/core/dom/node_computed_style.h"
 #include "third_party/blink/renderer/core/html/canvas/html_canvas_element.h"
diff --git a/third_party/blink/renderer/modules/canvas/canvas2d/canvas_style.h b/third_party/blink/renderer/modules/canvas/canvas2d/canvas_style.h
index 9a5f3b7..ccdce74 100644
--- a/third_party/blink/renderer/modules/canvas/canvas2d/canvas_style.h
+++ b/third_party/blink/renderer/modules/canvas/canvas2d/canvas_style.h
@@ -44,7 +44,7 @@
   explicit CanvasStyle(CanvasGradient*);
   explicit CanvasStyle(CanvasPattern*);
 
-  String GetColor() const {
+  String GetColorAsString() const {
     DCHECK_EQ(type_, kColorRGBA);
     return Color(rgba_).Serialized();
   }
diff --git a/third_party/blink/renderer/modules/canvas/offscreencanvas2d/offscreen_canvas_rendering_context_2d.idl b/third_party/blink/renderer/modules/canvas/offscreencanvas2d/offscreen_canvas_rendering_context_2d.idl
index f14896c..bac0f9a 100644
--- a/third_party/blink/renderer/modules/canvas/offscreencanvas2d/offscreen_canvas_rendering_context_2d.idl
+++ b/third_party/blink/renderer/modules/canvas/offscreencanvas2d/offscreen_canvas_rendering_context_2d.idl
@@ -49,8 +49,8 @@
     [MeasureAs=Canvas2DImageSmoothingQuality] attribute ImageSmoothingQuality imageSmoothingQuality; // (default "low")
 
     // colors and styles
-    attribute (DOMString or CanvasGradient or CanvasPattern) strokeStyle; // (default black)
-    attribute (DOMString or CanvasGradient or CanvasPattern) fillStyle; // (default black)
+    attribute (DOMString or CanvasGradient or CanvasPattern or CSSColorValue) strokeStyle; // (default black)
+    attribute (DOMString or CanvasGradient or CanvasPattern or CSSColorValue) fillStyle; // (default black)
     CanvasGradient createLinearGradient(double x0, double y0, double x1, double y1);
     [RaisesException] CanvasGradient createRadialGradient(double x0, double y0, double r0, double x1, double y1, double r1);
     [RuntimeEnabled=NewCanvas2DAPI] CanvasGradient createConicGradient(double startAngle, double centerX, double centerY);
diff --git a/third_party/blink/renderer/modules/csspaint/paint_rendering_context_2d.idl b/third_party/blink/renderer/modules/csspaint/paint_rendering_context_2d.idl
index 27649cb9..114ed21 100644
--- a/third_party/blink/renderer/modules/csspaint/paint_rendering_context_2d.idl
+++ b/third_party/blink/renderer/modules/csspaint/paint_rendering_context_2d.idl
@@ -43,8 +43,8 @@
     [RuntimeEnabled=CanvasImageSmoothing] attribute ImageSmoothingQuality imageSmoothingQuality; // (default "low")
 
     // colors and styles (see also the CanvasDrawingStyles interface)
-    attribute (DOMString or CanvasGradient or CanvasPattern) strokeStyle; // (default black)
-    attribute (DOMString or CanvasGradient or CanvasPattern) fillStyle; // (default black)
+    attribute (DOMString or CanvasGradient or CanvasPattern or CSSColorValue) strokeStyle; // (default black)
+    attribute (DOMString or CanvasGradient or CanvasPattern or CSSColorValue) fillStyle; // (default black)
     CanvasGradient createLinearGradient(double x0, double y0, double x1, double y1);
     [RaisesException] CanvasGradient createRadialGradient(double x0, double y0, double r0, double x1, double y1, double r1);
     [CallWith=ScriptState, RaisesException] CanvasPattern? createPattern(CanvasImageSource image, [TreatNullAs=EmptyString] DOMString repetitionType);
diff --git a/third_party/blink/renderer/modules/csspaint/paint_rendering_context_2d_test.cc b/third_party/blink/renderer/modules/csspaint/paint_rendering_context_2d_test.cc
index e5d564c0..69977ed9 100644
--- a/third_party/blink/renderer/modules/csspaint/paint_rendering_context_2d_test.cc
+++ b/third_party/blink/renderer/modules/csspaint/paint_rendering_context_2d_test.cc
@@ -3,6 +3,7 @@
 // found in the LICENSE file.
 
 #include "third_party/blink/renderer/modules/csspaint/paint_rendering_context_2d.h"
+#include "third_party/blink/renderer/bindings/modules/v8/string_or_canvas_gradient_or_canvas_pattern_or_css_color_value.h"
 
 #include "testing/gtest/include/gtest/gtest.h"
 
@@ -16,7 +17,7 @@
 void TrySettingStrokeStyle(PaintRenderingContext2D* ctx,
                            const String& expected,
                            const String& value) {
-  StringOrCanvasGradientOrCanvasPattern result, arg, dummy;
+  StringOrCanvasGradientOrCanvasPatternOrCSSColorValue result, arg, dummy;
   dummy.SetString("red");
   arg.SetString(value);
   ctx->setStrokeStyle(dummy);
diff --git a/third_party/blink/renderer/modules/modules_initializer.cc b/third_party/blink/renderer/modules/modules_initializer.cc
index 4a3ecb6f..6210d9b 100644
--- a/third_party/blink/renderer/modules/modules_initializer.cc
+++ b/third_party/blink/renderer/modules/modules_initializer.cc
@@ -321,12 +321,15 @@
       ->NotifyOrientationChanged();
 }
 
-void ModulesInitializer::NotifyScreensChanged(LocalFrame& frame) {
+void ModulesInitializer::NotifyScreensChanged(LocalFrame& frame,
+                                              const ScreenInfos& screen_infos) {
+  auto* window = frame.DomWindow();
   if (auto* supplement =
-          Supplement<LocalDOMWindow>::From<WindowScreens>(*frame.DomWindow())) {
+          Supplement<LocalDOMWindow>::From<WindowScreens>(window)) {
     // screens() may be null if permission has not been granted.
-    if (auto* screens = supplement->screens())
-      screens->ScreenInfosChanged();
+    if (auto* screens = supplement->screens()) {
+      screens->ScreenInfosChanged(window, screen_infos);
+    }
   }
 }
 
diff --git a/third_party/blink/renderer/modules/modules_initializer.h b/third_party/blink/renderer/modules/modules_initializer.h
index 6aeec50..5353144 100644
--- a/third_party/blink/renderer/modules/modules_initializer.h
+++ b/third_party/blink/renderer/modules/modules_initializer.h
@@ -51,7 +51,7 @@
 
   void DidChangeManifest(LocalFrame&) override;
   void NotifyOrientationChanged(LocalFrame&) override;
-  void NotifyScreensChanged(LocalFrame&) override;
+  void NotifyScreensChanged(LocalFrame&, const ScreenInfos&) override;
 };
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/modules/screen_enumeration/screens.cc b/third_party/blink/renderer/modules/screen_enumeration/screens.cc
index f678b3b..9f96bd09 100644
--- a/third_party/blink/renderer/modules/screen_enumeration/screens.cc
+++ b/third_party/blink/renderer/modules/screen_enumeration/screens.cc
@@ -19,10 +19,7 @@
     : ExecutionContextLifecycleObserver(window) {
   LocalFrame* frame = window->GetFrame();
   const auto& screen_infos = frame->GetChromeClient().GetScreenInfos(*frame);
-  for (const auto& screen_info : screen_infos.screen_infos) {
-    screens_.push_back(
-        MakeGarbageCollected<ScreenAdvanced>(window, screen_info.display_id));
-  }
+  ScreenInfosChanged(window, screen_infos);
 }
 
 const HeapVector<Member<ScreenAdvanced>>& Screens::screens() const {
@@ -33,6 +30,8 @@
   if (!DomWindow())
     return nullptr;
 
+  DCHECK(!screens_.IsEmpty());
+
   LocalFrame* frame = DomWindow()->GetFrame();
   const auto& current_info = frame->GetChromeClient().GetScreenInfo(*frame);
   for (const auto& screen : screens_) {
@@ -62,11 +61,40 @@
   ExecutionContextLifecycleObserver::Trace(visitor);
 }
 
-void Screens::ScreenInfosChanged() {
-  // TODO(crbug.com/879300): Add or remove `screens_` members as needed. Fire
-  // Screen.change instead of Screens.change for per-screen attribute changes.
-  // This should not fire an event if exposed information has not changed.
-  DispatchEvent(*Event::Create(event_type_names::kChange));
+void Screens::ScreenInfosChanged(LocalDOMWindow* window,
+                                 const ScreenInfos& infos) {
+  bool added_or_removed = false;
+
+  // O(displays) should be small, so O(n^2) check in both directions
+  // instead of keeping some more efficient cache of display ids.
+
+  // Check if any screens have been removed and remove them from screens_.
+  for (WTF::wtf_size_t i = 0; i < screens_.size();
+       /*conditionally incremented*/) {
+    if (base::Contains(infos.screen_infos, screens_[i]->DisplayId(),
+                       &ScreenInfo::display_id)) {
+      ++i;
+    } else {
+      screens_.EraseAt(i);
+      added_or_removed = true;
+      // Recheck this index.
+    }
+  }
+
+  // Check if any screens have been added, and append them to the end of
+  // screens_.
+  for (const auto& info : infos.screen_infos) {
+    if (!base::Contains(screens_, info.display_id,
+                        &ScreenAdvanced::DisplayId)) {
+      screens_.push_back(
+          MakeGarbageCollected<ScreenAdvanced>(window, info.display_id));
+      added_or_removed = true;
+    }
+  }
+
+  if (added_or_removed) {
+    DispatchEvent(*Event::Create(event_type_names::kChange));
+  }
 }
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/modules/screen_enumeration/screens.h b/third_party/blink/renderer/modules/screen_enumeration/screens.h
index 645ce66..4283ba6 100644
--- a/third_party/blink/renderer/modules/screen_enumeration/screens.h
+++ b/third_party/blink/renderer/modules/screen_enumeration/screens.h
@@ -15,6 +15,7 @@
 
 class LocalDOMWindow;
 class ScreenAdvanced;
+struct ScreenInfos;
 
 // Interface exposing multi-screen information.
 // https://github.com/webscreens/window-placement
@@ -40,7 +41,7 @@
   void Trace(Visitor*) const override;
 
   // Called when the underlying multi-screen information changes.
-  void ScreenInfosChanged();
+  void ScreenInfosChanged(LocalDOMWindow* window, const ScreenInfos& infos);
 
  private:
   HeapVector<Member<ScreenAdvanced>> screens_;
diff --git a/third_party/blink/renderer/modules/screen_enumeration/screens.idl b/third_party/blink/renderer/modules/screen_enumeration/screens.idl
index 5807196a..854b213 100644
--- a/third_party/blink/renderer/modules/screen_enumeration/screens.idl
+++ b/third_party/blink/renderer/modules/screen_enumeration/screens.idl
@@ -8,14 +8,14 @@
   Exposed=Window,
   SecureContext,
   RuntimeEnabled=WindowPlacement
-] interface Screens {
+] interface Screens : EventTarget {
   // The set of available screens with additional per-screen info.
   readonly attribute FrozenArray<ScreenAdvanced> screens;
 
   // Current screen reference; corresponds with window.screen.
   readonly attribute ScreenAdvanced currentScreen;
 
-  // Change event fired when `screens` or `currentScreen` changes.
+  // Change event fired when the set of screens changes,
   // NOTE: Does not fire on changes to attributes of individual Screens.
   [HighEntropy, Measure] attribute EventHandler onchange;
 };
diff --git a/third_party/blink/renderer/modules/xr/BUILD.gn b/third_party/blink/renderer/modules/xr/BUILD.gn
index c8fc56a..90fa92d7 100644
--- a/third_party/blink/renderer/modules/xr/BUILD.gn
+++ b/third_party/blink/renderer/modules/xr/BUILD.gn
@@ -29,6 +29,10 @@
     "xr_depth_manager.h",
     "xr_dom_overlay_state.cc",
     "xr_dom_overlay_state.h",
+    "xr_enter_fullscreen_observer.cc",
+    "xr_enter_fullscreen_observer.h",
+    "xr_exit_fullscreen_observer.cc",
+    "xr_exit_fullscreen_observer.h",
     "xr_frame.cc",
     "xr_frame.h",
     "xr_frame_provider.cc",
diff --git a/third_party/blink/renderer/modules/xr/xr_enter_fullscreen_observer.cc b/third_party/blink/renderer/modules/xr/xr_enter_fullscreen_observer.cc
new file mode 100644
index 0000000..08022833
--- /dev/null
+++ b/third_party/blink/renderer/modules/xr/xr_enter_fullscreen_observer.cc
@@ -0,0 +1,104 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/modules/xr/xr_enter_fullscreen_observer.h"
+
+#include "third_party/blink/renderer/bindings/core/v8/v8_fullscreen_options.h"
+#include "third_party/blink/renderer/core/dom/document.h"
+#include "third_party/blink/renderer/core/dom/element.h"
+#include "third_party/blink/renderer/core/frame/viewport_data.h"
+#include "third_party/blink/renderer/core/fullscreen/fullscreen.h"
+#include "third_party/blink/renderer/core/fullscreen/scoped_allow_fullscreen.h"
+
+namespace blink {
+XrEnterFullscreenObserver::XrEnterFullscreenObserver() {
+  DVLOG(2) << __func__;
+}
+
+XrEnterFullscreenObserver::~XrEnterFullscreenObserver() = default;
+
+void XrEnterFullscreenObserver::Invoke(ExecutionContext* execution_context,
+                                       Event* event) {
+  DVLOG(2) << __func__ << ": event type=" << event->type();
+
+  // This handler should only be called once, it's unregistered after use.
+  DCHECK(on_completed_);
+
+  auto& doc = fullscreen_element_->GetDocument();
+
+  doc.removeEventListener(event_type_names::kFullscreenchange, this, true);
+  doc.removeEventListener(event_type_names::kFullscreenerror, this, true);
+
+  if (event->type() == event_type_names::kFullscreenchange) {
+    // Succeeded, force the content to expand all the way (because that's what
+    // the XR content will do), and then notify of this success.
+    doc.GetViewportData().SetExpandIntoDisplayCutout(true);
+    std::move(on_completed_).Run(true);
+  }
+  if (event->type() == event_type_names::kFullscreenerror) {
+    // Notify our callback that we failed to enter fullscreen.
+    std::move(on_completed_).Run(false);
+  }
+}
+
+void XrEnterFullscreenObserver::RequestFullscreen(
+    Element* fullscreen_element,
+    base::OnceCallback<void(bool)> on_completed) {
+  DCHECK(!on_completed_);
+  DCHECK(fullscreen_element);
+  on_completed_ = std::move(on_completed);
+  fullscreen_element_ = fullscreen_element;
+
+  bool wait_for_fullscreen_change = true;
+
+  if (fullscreen_element_ ==
+      Fullscreen::FullscreenElementFrom(fullscreen_element_->GetDocument())) {
+    // It's possible that the requested element is already fullscreen, in which
+    // case we must not wait for a fullscreenchange event since it won't arrive.
+    // This can happen if the site used Fullscreen API to place the element into
+    // fullscreen mode before requesting the session, and if the session can
+    // proceed without needing a permission prompt. (Showing a dialog exits
+    // fullscreen mode.)
+    //
+    // We still need to do the RequestFullscreen call to apply the kForXrOverlay
+    // property which sets the background transparent.
+    DVLOG(2) << __func__ << ": requested element already fullscreen";
+    wait_for_fullscreen_change = false;
+  }
+
+  if (wait_for_fullscreen_change) {
+    // Set up event listeners for success and failure.
+    fullscreen_element_->GetDocument().addEventListener(
+        event_type_names::kFullscreenchange, this, true);
+    fullscreen_element_->GetDocument().addEventListener(
+        event_type_names::kFullscreenerror, this, true);
+  }
+
+  // Use the event-generating unprefixed version of RequestFullscreen to ensure
+  // that the fullscreen event listener is informed once this completes.
+  FullscreenOptions* options = FullscreenOptions::Create();
+  options->setNavigationUI("hide");
+
+  // Grant fullscreen API permission for the following call. Requesting the
+  // immersive session had required a user activation state, but that may have
+  // expired by now due to the user taking time to respond to the consent
+  // prompt.
+  ScopedAllowFullscreen scope(ScopedAllowFullscreen::kXrOverlay);
+
+  Fullscreen::RequestFullscreen(*fullscreen_element_, options,
+                                FullscreenRequestType::kUnprefixed |
+                                    FullscreenRequestType::kForXrOverlay);
+
+  if (!wait_for_fullscreen_change) {
+    // Element was already fullscreen, proceed with session creation.
+    std::move(on_completed_).Run(true);
+  }
+}
+
+void XrEnterFullscreenObserver::Trace(Visitor* visitor) const {
+  visitor->Trace(fullscreen_element_);
+  NativeEventListener::Trace(visitor);
+}
+
+}  // namespace blink
diff --git a/third_party/blink/renderer/modules/xr/xr_enter_fullscreen_observer.h b/third_party/blink/renderer/modules/xr/xr_enter_fullscreen_observer.h
new file mode 100644
index 0000000..8b8756c0
--- /dev/null
+++ b/third_party/blink/renderer/modules/xr/xr_enter_fullscreen_observer.h
@@ -0,0 +1,41 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_XR_XR_ENTER_FULLSCREEN_OBSERVER_H_
+#define THIRD_PARTY_BLINK_RENDERER_MODULES_XR_XR_ENTER_FULLSCREEN_OBSERVER_H_
+
+#include "base/callback.h"
+#include "third_party/blink/renderer/core/dom/element.h"
+#include "third_party/blink/renderer/core/dom/events/event.h"
+#include "third_party/blink/renderer/core/dom/events/native_event_listener.h"
+#include "third_party/blink/renderer/core/execution_context/execution_context.h"
+
+namespace blink {
+
+// Native event listener for fullscreen change / error events for use when
+// entering fullscreen.
+class XrEnterFullscreenObserver : public NativeEventListener {
+ public:
+  XrEnterFullscreenObserver();
+  ~XrEnterFullscreenObserver() override;
+
+  // NativeEventListener
+  void Invoke(ExecutionContext*, Event*) override;
+
+  // Attempt to enter fullscreen with |element| as the root. |on_completed| will
+  // be notified with whether or not fullscreen was successfully entered.
+  void RequestFullscreen(Element* element,
+                         base::OnceCallback<void(bool)> on_completed);
+
+  void Trace(Visitor*) const override;
+
+ private:
+  Member<Element> fullscreen_element_;
+  base::OnceCallback<void(bool)> on_completed_;
+  DISALLOW_COPY_AND_ASSIGN(XrEnterFullscreenObserver);
+};
+
+}  // namespace blink
+
+#endif  // THIRD_PARTY_BLINK_RENDERER_MODULES_XR_XR_ENTER_FULLSCREEN_OBSERVER_H_
diff --git a/third_party/blink/renderer/modules/xr/xr_exit_fullscreen_observer.cc b/third_party/blink/renderer/modules/xr/xr_exit_fullscreen_observer.cc
new file mode 100644
index 0000000..c53c84e6
--- /dev/null
+++ b/third_party/blink/renderer/modules/xr/xr_exit_fullscreen_observer.cc
@@ -0,0 +1,58 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/modules/xr/xr_exit_fullscreen_observer.h"
+
+#include "third_party/blink/renderer/core/dom/document.h"
+#include "third_party/blink/renderer/core/dom/element.h"
+#include "third_party/blink/renderer/core/frame/viewport_data.h"
+#include "third_party/blink/renderer/core/fullscreen/fullscreen.h"
+
+namespace blink {
+XrExitFullscreenObserver::XrExitFullscreenObserver() {
+  DVLOG(2) << __func__;
+}
+
+XrExitFullscreenObserver::~XrExitFullscreenObserver() = default;
+
+void XrExitFullscreenObserver::Invoke(ExecutionContext* execution_context,
+                                      Event* event) {
+  DVLOG(2) << __func__ << ": event type=" << event->type();
+
+  document_->removeEventListener(event_type_names::kFullscreenchange, this,
+                                 true);
+
+  if (event->type() == event_type_names::kFullscreenchange) {
+    // Succeeded, proceed with session shutdown. Expanding into the fullscreen
+    // cutout is only valid for fullscreen mode which we just exited (cf.
+    // MediaControlsDisplayCutoutDelegate::DidExitFullscreen), so we can
+    // unconditionally turn this off here.
+    document_->GetViewportData().SetExpandIntoDisplayCutout(false);
+    std::move(on_exited_).Run();
+  }
+}
+
+void XrExitFullscreenObserver::ExitFullscreen(Document* document,
+                                              base::OnceClosure on_exited) {
+  DVLOG(2) << __func__;
+  document_ = document;
+  on_exited_ = std::move(on_exited);
+
+  document->addEventListener(event_type_names::kFullscreenchange, this, true);
+  // "ua_originated" means that the browser process already exited
+  // fullscreen. Set it to false because we need the browser process
+  // to get notified that it needs to exit fullscreen. Use
+  // FullyExitFullscreen to ensure that we return to non-fullscreen mode.
+  // ExitFullscreen only unfullscreens a single element, potentially
+  // leaving others in fullscreen mode.
+  constexpr bool kUaOriginated = false;
+
+  Fullscreen::FullyExitFullscreen(*document, kUaOriginated);
+}
+
+void XrExitFullscreenObserver::Trace(Visitor* visitor) const {
+  visitor->Trace(document_);
+  NativeEventListener::Trace(visitor);
+}
+}  // namespace blink
diff --git a/third_party/blink/renderer/modules/xr/xr_exit_fullscreen_observer.h b/third_party/blink/renderer/modules/xr/xr_exit_fullscreen_observer.h
new file mode 100644
index 0000000..2e3928e
--- /dev/null
+++ b/third_party/blink/renderer/modules/xr/xr_exit_fullscreen_observer.h
@@ -0,0 +1,40 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_XR_XR_EXIT_FULLSCREEN_OBSERVER_H_
+#define THIRD_PARTY_BLINK_RENDERER_MODULES_XR_XR_EXIT_FULLSCREEN_OBSERVER_H_
+
+#include "base/callback.h"
+#include "third_party/blink/renderer/core/dom/document.h"
+#include "third_party/blink/renderer/core/dom/events/event.h"
+#include "third_party/blink/renderer/core/dom/events/native_event_listener.h"
+#include "third_party/blink/renderer/core/execution_context/execution_context.h"
+
+namespace blink {
+
+// Native event listener used when waiting for fullscreen mode to fully exit.
+// Typically used to either clear the way for starting fullscreen mode for AR
+// when exiting an existing fullscreen, or for ensuring that fullscreen has
+// exited when ending an AR session.
+class XrExitFullscreenObserver : public NativeEventListener {
+ public:
+  XrExitFullscreenObserver();
+  ~XrExitFullscreenObserver() override;
+
+  // NativeEventListener
+  void Invoke(ExecutionContext*, Event*) override;
+
+  void ExitFullscreen(Document* doc, base::OnceClosure on_exited);
+
+  void Trace(Visitor*) const override;
+
+ private:
+  Member<Document> document_;
+  base::OnceClosure on_exited_;
+  DISALLOW_COPY_AND_ASSIGN(XrExitFullscreenObserver);
+};
+
+}  // namespace blink
+
+#endif  // THIRD_PARTY_BLINK_RENDERER_MODULES_XR_XR_EXIT_FULLSCREEN_OBSERVER_H_
diff --git a/third_party/blink/renderer/modules/xr/xr_system.cc b/third_party/blink/renderer/modules/xr/xr_system.cc
index 5fc8ca3..7dc5b990 100644
--- a/third_party/blink/renderer/modules/xr/xr_system.cc
+++ b/third_party/blink/renderer/modules/xr/xr_system.cc
@@ -11,7 +11,6 @@
 #include "third_party/blink/public/common/browser_interface_broker_proxy.h"
 #include "third_party/blink/public/mojom/permissions_policy/permissions_policy.mojom-blink.h"
 #include "third_party/blink/renderer/bindings/core/v8/script_promise_resolver.h"
-#include "third_party/blink/renderer/bindings/core/v8/v8_fullscreen_options.h"
 #include "third_party/blink/renderer/bindings/modules/v8/v8_xr_depth_state_init.h"
 #include "third_party/blink/renderer/bindings/modules/v8/v8_xr_tracked_image_init.h"
 #include "third_party/blink/renderer/core/dom/document.h"
@@ -23,7 +22,6 @@
 #include "third_party/blink/renderer/core/frame/settings.h"
 #include "third_party/blink/renderer/core/frame/viewport_data.h"
 #include "third_party/blink/renderer/core/fullscreen/fullscreen.h"
-#include "third_party/blink/renderer/core/fullscreen/scoped_allow_fullscreen.h"
 #include "third_party/blink/renderer/core/html/html_element.h"
 #include "third_party/blink/renderer/core/imagebitmap/image_bitmap.h"
 #include "third_party/blink/renderer/core/inspector/console_message.h"
@@ -31,6 +29,8 @@
 #include "third_party/blink/renderer/core/loader/document_loader.h"
 #include "third_party/blink/renderer/modules/event_modules.h"
 #include "third_party/blink/renderer/modules/event_target_modules.h"
+#include "third_party/blink/renderer/modules/xr/xr_enter_fullscreen_observer.h"
+#include "third_party/blink/renderer/modules/xr/xr_exit_fullscreen_observer.h"
 #include "third_party/blink/renderer/modules/xr/xr_frame_provider.h"
 #include "third_party/blink/renderer/modules/xr/xr_session.h"
 #include "third_party/blink/renderer/modules/xr/xr_session_viewport_scaler.h"
@@ -760,153 +760,6 @@
   visitor->Trace(dom_overlay_element_);
 }
 
-XRSystem::OverlayFullscreenEventManager::OverlayFullscreenEventManager(
-    XRSystem* xr,
-    XRSystem::PendingRequestSessionQuery* query,
-    device::mojom::blink::RequestSessionResultPtr result)
-    : xr_(xr), query_(query), result_(std::move(result)) {
-  DVLOG(2) << __func__;
-}
-
-XRSystem::OverlayFullscreenEventManager::~OverlayFullscreenEventManager() =
-    default;
-
-void XRSystem::OverlayFullscreenEventManager::Invoke(
-    ExecutionContext* execution_context,
-    Event* event) {
-  DVLOG(2) << __func__ << ": event type=" << event->type();
-
-  // This handler should only be called once, it's unregistered after use.
-  DCHECK(query_);
-  DCHECK(result_);
-
-  Element* element = query_->DOMOverlayElement();
-  element->GetDocument().removeEventListener(
-      event_type_names::kFullscreenchange, this, true);
-  element->GetDocument().removeEventListener(event_type_names::kFullscreenerror,
-                                             this, true);
-
-  if (event->type() == event_type_names::kFullscreenchange) {
-    // Succeeded, proceed with session creation.
-    element->GetDocument().GetViewportData().SetExpandIntoDisplayCutout(true);
-    xr_->OnRequestSessionReturned(query_, std::move(result_));
-  }
-
-  if (event->type() == event_type_names::kFullscreenerror) {
-    // Failed, reject the session
-    xr_->OnRequestSessionReturned(
-        query_, device::mojom::blink::RequestSessionResult::NewFailureReason(
-                    device::mojom::RequestSessionError::FULLSCREEN_ERROR));
-  }
-}
-
-void XRSystem::OverlayFullscreenEventManager::RequestFullscreen() {
-  Element* element = query_->DOMOverlayElement();
-  DCHECK(element);
-
-  bool wait_for_fullscreen_change = true;
-
-  if (element == Fullscreen::FullscreenElementFrom(element->GetDocument())) {
-    // It's possible that the requested element is already fullscreen, in which
-    // case we must not wait for a fullscreenchange event since it won't arrive.
-    // This can happen if the site used Fullscreen API to place the element into
-    // fullscreen mode before requesting the session, and if the session can
-    // proceed without needing a permission prompt. (Showing a dialog exits
-    // fullscreen mode.)
-    //
-    // We still need to do the RequestFullscreen call to apply the kForXrOverlay
-    // property which sets the background transparent.
-    DVLOG(2) << __func__ << ": requested element already fullscreen";
-    wait_for_fullscreen_change = false;
-  }
-
-  if (wait_for_fullscreen_change) {
-    // Set up event listeners for success and failure.
-    element->GetDocument().addEventListener(event_type_names::kFullscreenchange,
-                                            this, true);
-    element->GetDocument().addEventListener(event_type_names::kFullscreenerror,
-                                            this, true);
-  }
-
-  // Use the event-generating unprefixed version of RequestFullscreen to ensure
-  // that the fullscreen event listener is informed once this completes.
-  FullscreenOptions* options = FullscreenOptions::Create();
-  options->setNavigationUI("hide");
-
-  // Grant fullscreen API permission for the following call. Requesting the
-  // immersive session had required a user activation state, but that may have
-  // expired by now due to the user taking time to respond to the consent
-  // prompt.
-  ScopedAllowFullscreen scope(ScopedAllowFullscreen::kXrOverlay);
-
-  Fullscreen::RequestFullscreen(*element, options,
-                                FullscreenRequestType::kUnprefixed |
-                                    FullscreenRequestType::kForXrOverlay);
-
-  if (!wait_for_fullscreen_change) {
-    // Element was already fullscreen, proceed with session creation.
-    xr_->OnRequestSessionReturned(query_, std::move(result_));
-  }
-}
-
-void XRSystem::OverlayFullscreenEventManager::Trace(Visitor* visitor) const {
-  visitor->Trace(xr_);
-  visitor->Trace(query_);
-  EventListener::Trace(visitor);
-}
-
-XRSystem::OverlayFullscreenExitObserver::OverlayFullscreenExitObserver(
-    XRSystem* xr)
-    : xr_(xr) {
-  DVLOG(2) << __func__;
-}
-
-XRSystem::OverlayFullscreenExitObserver::~OverlayFullscreenExitObserver() =
-    default;
-
-void XRSystem::OverlayFullscreenExitObserver::Invoke(
-    ExecutionContext* execution_context,
-    Event* event) {
-  DVLOG(2) << __func__ << ": event type=" << event->type();
-
-  document_->removeEventListener(event_type_names::kFullscreenchange, this,
-                                 true);
-
-  if (event->type() == event_type_names::kFullscreenchange) {
-    // Succeeded, proceed with session shutdown. Expanding into the fullscreen
-    // cutout is only valid for fullscreen mode which we just exited (cf.
-    // MediaControlsDisplayCutoutDelegate::DidExitFullscreen), so we can
-    // unconditionally turn this off here.
-    document_->GetViewportData().SetExpandIntoDisplayCutout(false);
-    xr_->ExitPresent(std::move(on_exited_));
-  }
-}
-
-void XRSystem::OverlayFullscreenExitObserver::ExitFullscreen(
-    Document* document,
-    base::OnceClosure on_exited) {
-  DVLOG(2) << __func__;
-  document_ = document;
-  on_exited_ = std::move(on_exited);
-
-  document->addEventListener(event_type_names::kFullscreenchange, this, true);
-  // "ua_originated" means that the browser process already exited
-  // fullscreen. Set it to false because we need the browser process
-  // to get notified that it needs to exit fullscreen. Use
-  // FullyExitFullscreen to ensure that we return to non-fullscreen mode.
-  // ExitFullscreen only unfullscreens a single element, potentially
-  // leaving others in fullscreen mode.
-  constexpr bool kUaOriginated = false;
-
-  Fullscreen::FullyExitFullscreen(*document, kUaOriginated);
-}
-
-void XRSystem::OverlayFullscreenExitObserver::Trace(Visitor* visitor) const {
-  visitor->Trace(xr_);
-  visitor->Trace(document_);
-  EventListener::Trace(visitor);
-}
-
 device::mojom::blink::XRSessionOptionsPtr XRSystem::XRSessionOptionsFromQuery(
     const PendingRequestSessionQuery& query) {
   device::mojom::blink::XRSessionOptionsPtr session_options =
@@ -1058,8 +911,12 @@
       DVLOG(3) << __func__ << ": fullscreen_element=" << fullscreen_element;
       if (fullscreen_element) {
         fullscreen_exit_observer_ =
-            MakeGarbageCollected<OverlayFullscreenExitObserver>(this);
-        fullscreen_exit_observer_->ExitFullscreen(doc, std::move(on_exited));
+            MakeGarbageCollected<XrExitFullscreenObserver>();
+        // Once we exit fullscreen, we'll need to come back here to finish
+        // shutting down the session.
+        fullscreen_exit_observer_->ExitFullscreen(
+            doc, WTF::Bind(&XRSystem::ExitPresent, WrapWeakPersistent(this),
+                           std::move(on_exited)));
         return;
       }
     }
@@ -1244,7 +1101,7 @@
     DVLOG(2) << __func__ << ": has_remote_ancestor=" << has_remote_ancestor;
     if (has_remote_ancestor) {
       fullscreen_exit_observer_ =
-          MakeGarbageCollected<OverlayFullscreenExitObserver>(this);
+          MakeGarbageCollected<XrExitFullscreenObserver>();
 
       base::OnceClosure callback =
           WTF::Bind(&XRSystem::DoRequestSession, WrapWeakPersistent(this),
@@ -1605,16 +1462,31 @@
   if (result->is_success()) {
     // Success. Now request fullscreen mode and continue with
     // OnRequestSessionReturned once that completes.
-    fullscreen_event_manager_ =
-        MakeGarbageCollected<OverlayFullscreenEventManager>(this, query,
-                                                            std::move(result));
-    fullscreen_event_manager_->RequestFullscreen();
+    fullscreen_enter_observer_ =
+        MakeGarbageCollected<XrEnterFullscreenObserver>();
+    fullscreen_enter_observer_->RequestFullscreen(
+        query->DOMOverlayElement(),
+        WTF::Bind(&XRSystem::OnFullscreenConfigured, WrapPersistent(this),
+                  WrapPersistent(query), std::move(result)));
   } else {
     // Session request failed, continue processing that normally.
     OnRequestSessionReturned(query, std::move(result));
   }
 }
 
+void XRSystem::OnFullscreenConfigured(
+    PendingRequestSessionQuery* query,
+    device::mojom::blink::RequestSessionResultPtr result,
+    bool fullscreen_succeeded) {
+  if (fullscreen_succeeded) {
+    OnRequestSessionReturned(query, std::move(result));
+  } else {
+    OnRequestSessionReturned(
+        query, device::mojom::blink::RequestSessionResult::NewFailureReason(
+                   device::mojom::RequestSessionError::FULLSCREEN_ERROR));
+  }
+}
+
 void XRSystem::OnRequestSessionReturned(
     PendingRequestSessionQuery* query,
     device::mojom::blink::RequestSessionResultPtr result) {
@@ -1630,9 +1502,8 @@
   }
 
   // Clean up the fullscreen event manager which may have been added for
-  // DOM overlay setup. We're done with it, and it contains a reference
-  // to the query and the DOM overlay element.
-  fullscreen_event_manager_ = nullptr;
+  // DOM overlay setup.
+  fullscreen_enter_observer_ = nullptr;
 
   if (!result->is_success()) {
     // |service_| does not support the requested mode. Attempt to create a
@@ -1885,7 +1756,7 @@
   visitor->Trace(receiver_);
   visitor->Trace(outstanding_support_queries_);
   visitor->Trace(outstanding_request_queries_);
-  visitor->Trace(fullscreen_event_manager_);
+  visitor->Trace(fullscreen_enter_observer_);
   visitor->Trace(fullscreen_exit_observer_);
   Supplement<Navigator>::Trace(visitor);
   ExecutionContextLifecycleObserver::Trace(visitor);
diff --git a/third_party/blink/renderer/modules/xr/xr_system.h b/third_party/blink/renderer/modules/xr/xr_system.h
index 3719d02..6f52a7d 100644
--- a/third_party/blink/renderer/modules/xr/xr_system.h
+++ b/third_party/blink/renderer/modules/xr/xr_system.h
@@ -16,6 +16,8 @@
 #include "third_party/blink/renderer/core/dom/events/native_event_listener.h"
 #include "third_party/blink/renderer/core/execution_context/execution_context_lifecycle_observer.h"
 #include "third_party/blink/renderer/core/page/focus_changed_observer.h"
+#include "third_party/blink/renderer/modules/xr/xr_enter_fullscreen_observer.h"
+#include "third_party/blink/renderer/modules/xr/xr_exit_fullscreen_observer.h"
 #include "third_party/blink/renderer/modules/xr/xr_session.h"
 #include "third_party/blink/renderer/platform/graphics/color.h"
 #include "third_party/blink/renderer/platform/heap/handle.h"
@@ -329,53 +331,6 @@
     DISALLOW_COPY_AND_ASSIGN(PendingSupportsSessionQuery);
   };
 
-  // Native event listener for fullscreen change / error events when starting an
-  // immersive-ar session that uses DOM Overlay mode. See
-  // OnRequestSessionReturned().
-  class OverlayFullscreenEventManager : public NativeEventListener {
-   public:
-    OverlayFullscreenEventManager(
-        XRSystem* xr,
-        XRSystem::PendingRequestSessionQuery*,
-        device::mojom::blink::RequestSessionResultPtr);
-    ~OverlayFullscreenEventManager() override;
-
-    // NativeEventListener
-    void Invoke(ExecutionContext*, Event*) override;
-
-    void RequestFullscreen();
-    void OnSessionStarting();
-
-    void Trace(Visitor*) const override;
-
-   private:
-    Member<XRSystem> xr_;
-    Member<PendingRequestSessionQuery> query_;
-    device::mojom::blink::RequestSessionResultPtr result_;
-    DISALLOW_COPY_AND_ASSIGN(OverlayFullscreenEventManager);
-  };
-
-  // Native event listener used when waiting for fullscreen mode to fully exit
-  // when starting or ending an XR session.
-  class OverlayFullscreenExitObserver : public NativeEventListener {
-   public:
-    explicit OverlayFullscreenExitObserver(XRSystem* xr);
-    ~OverlayFullscreenExitObserver() override;
-
-    // NativeEventListener
-    void Invoke(ExecutionContext*, Event*) override;
-
-    void ExitFullscreen(Document* doc, base::OnceClosure on_exited);
-
-    void Trace(Visitor*) const override;
-
-   private:
-    Member<XRSystem> xr_;
-    Member<Document> document_;
-    base::OnceClosure on_exited_;
-    DISALLOW_COPY_AND_ASSIGN(OverlayFullscreenExitObserver);
-  };
-
   // Helper, logs message to the console as well as DVLOGs.
   void AddConsoleMessage(mojom::blink::ConsoleMessageLevel error_level,
                          const String& message);
@@ -407,6 +362,10 @@
   void OnRequestSessionSetupForDomOverlay(
       PendingRequestSessionQuery*,
       device::mojom::blink::RequestSessionResultPtr result);
+  void OnFullscreenConfigured(
+      PendingRequestSessionQuery* query,
+      device::mojom::blink::RequestSessionResultPtr result,
+      bool fullscreen_succeeded);
   void OnRequestSessionReturned(
       PendingRequestSessionQuery*,
       device::mojom::blink::RequestSessionResultPtr result);
@@ -483,11 +442,11 @@
   // In DOM overlay mode, use a fullscreen event listener to detect when
   // transition to fullscreen mode completes or fails, and reject/resolve
   // the pending request session promise accordingly.
-  Member<OverlayFullscreenEventManager> fullscreen_event_manager_;
+  Member<XrEnterFullscreenObserver> fullscreen_enter_observer_;
   // DOM overlay mode uses a separate temporary fullscreen event listener
   // if it needs to wait for fullscreen mode to fully exit when ending
   // the session.
-  Member<OverlayFullscreenExitObserver> fullscreen_exit_observer_;
+  Member<XrExitFullscreenObserver> fullscreen_exit_observer_;
 
   bool is_context_destroyed_ = false;
   bool did_service_ever_disconnect_ = false;
diff --git a/third_party/blink/renderer/platform/BUILD.gn b/third_party/blink/renderer/platform/BUILD.gn
index 8894c857..c23c451a 100644
--- a/third_party/blink/renderer/platform/BUILD.gn
+++ b/third_party/blink/renderer/platform/BUILD.gn
@@ -1941,6 +1941,13 @@
 
 test("blink_platform_unittests") {
   deps = [ ":blink_platform_unittests_sources" ]
+
+  if (is_fuchsia && enable_blink_heap_use_v8_oilpan) {
+    # Oilpan reuses V8's v8::PageAllocator which generally requires JIT
+    # permissions.
+    additional_manifest_fragments =
+        [ "//build/config/fuchsia/test/jit_capabilities.test-cmx" ]
+  }
 }
 
 source_set("blink_platform_unittests_sources") {
diff --git a/third_party/blink/renderer/platform/graphics/canvas_resource_dispatcher.cc b/third_party/blink/renderer/platform/graphics/canvas_resource_dispatcher.cc
index 4efffeb5..8cc8572 100644
--- a/third_party/blink/renderer/platform/graphics/canvas_resource_dispatcher.cc
+++ b/third_party/blink/renderer/platform/graphics/canvas_resource_dispatcher.cc
@@ -232,7 +232,6 @@
 
   const gfx::Rect bounds(size_.Width(), size_.Height());
   constexpr viz::CompositorRenderPassId kRenderPassId{1};
-  constexpr bool is_clipped = false;
   auto pass =
       viz::CompositorRenderPass::Create(/*shared_quad_state_list_size=*/1u,
                                         /*quad_list_size=*/1u);
@@ -242,8 +241,8 @@
                gfx::Transform());
 
   viz::SharedQuadState* sqs = pass->CreateAndAppendSharedQuadState();
-  sqs->SetAll(gfx::Transform(), bounds, bounds, gfx::MaskFilterInfo(), bounds,
-              is_clipped, is_opaque, 1.f, SkBlendMode::kSrcOver, 0);
+  sqs->SetAll(gfx::Transform(), bounds, bounds, gfx::MaskFilterInfo(),
+              base::nullopt, is_opaque, 1.f, SkBlendMode::kSrcOver, 0);
 
   viz::TransferableResource resource;
   auto frame_resource = std::make_unique<FrameResource>();
diff --git a/third_party/blink/renderer/platform/graphics/video_frame_resource_provider.cc b/third_party/blink/renderer/platform/graphics/video_frame_resource_provider.cc
index e9f1386..5d0cb43 100644
--- a/third_party/blink/renderer/platform/graphics/video_frame_resource_provider.cc
+++ b/third_party/blink/renderer/platform/graphics/video_frame_resource_provider.cc
@@ -109,16 +109,14 @@
   }
 
   gfx::Rect visible_quad_rect = quad_rect;
-  gfx::Rect clip_rect;
   gfx::MaskFilterInfo mask_filter_info;
-  bool is_clipped = false;
   float draw_opacity = 1.0f;
   int sorting_context_id = 0;
 
   resource_updater_->AppendQuads(render_pass, std::move(frame), transform,
                                  quad_rect, visible_quad_rect, mask_filter_info,
-                                 clip_rect, is_clipped, is_opaque, draw_opacity,
-                                 sorting_context_id);
+                                 /*clip_rect=*/base::nullopt, is_opaque,
+                                 draw_opacity, sorting_context_id);
 }
 
 void VideoFrameResourceProvider::ReleaseFrameResources() {
diff --git a/third_party/blink/renderer/platform/heap/BUILD.gn b/third_party/blink/renderer/platform/heap/BUILD.gn
index ed62ca9..4e507b9 100644
--- a/third_party/blink/renderer/platform/heap/BUILD.gn
+++ b/third_party/blink/renderer/platform/heap/BUILD.gn
@@ -243,6 +243,7 @@
 
 test("blink_heap_unittests") {
   deps = [ ":blink_heap_unittests_sources" ]
+
   if (is_android) {
     deps += [
       "//base:base_java",
@@ -251,6 +252,13 @@
       "//ui/android:ui_full_java",
     ]
   }
+
+  if (is_fuchsia && enable_blink_heap_use_v8_oilpan) {
+    # Oilpan reuses V8's v8::PageAllocator which generally requires JIT
+    # permissions.
+    additional_manifest_fragments =
+        [ "//build/config/fuchsia/test/jit_capabilities.test-cmx" ]
+  }
 }
 
 source_set("blink_heap_unittests_sources") {
diff --git a/third_party/blink/renderer/platform/webrtc/webrtc_video_frame_adapter.cc b/third_party/blink/renderer/platform/webrtc/webrtc_video_frame_adapter.cc
index a1a3870..041fd5a 100644
--- a/third_party/blink/renderer/platform/webrtc/webrtc_video_frame_adapter.cc
+++ b/third_party/blink/renderer/platform/webrtc/webrtc_video_frame_adapter.cc
@@ -264,8 +264,8 @@
     // allowing optimized mapping and scaling of future frames for these sizes.
     std::vector<gfx::Size> mapped_sizes;
     for (const auto& adapted_frame : adapted_frames_) {
-      const auto& coded_size = adapted_frame.video_frame->coded_size();
-      const auto& visible_rect = adapted_frame.video_frame->visible_rect();
+      const auto& coded_size = frame_->coded_size();
+      const auto& visible_rect = adapted_frame.size.visible_rect;
       // The portion of the coded size that is visible.
       double kVisiblePortionX =
           static_cast<double>(visible_rect.width()) / coded_size.width();
@@ -273,7 +273,7 @@
           static_cast<double>(visible_rect.height()) / coded_size.height();
       // The mapped size is the natural size of the entire image, not just the
       // visible portion.
-      const auto& natural_size = adapted_frame.video_frame->natural_size();
+      const auto& natural_size = adapted_frame.size.natural_size;
       mapped_sizes.emplace_back(
           std::round(natural_size.width() / kVisiblePortionX),
           std::round(natural_size.height() / kVisiblePortionY));
@@ -320,21 +320,22 @@
       return adapted_frame.frame_buffer;
   }
   // Adapt the frame for this size.
-  scoped_refptr<media::VideoFrame> video_frame = GetOrWrapFrameForSize(size);
-  rtc::scoped_refptr<webrtc::VideoFrameBuffer> frame_buffer =
-      ConvertToWebRtcVideoFrameBuffer(video_frame, shared_resources_);
-  adapted_frames_.emplace_back(size, video_frame, frame_buffer);
-  return frame_buffer;
+  adapted_frames_.push_back(AdaptBestFrame(size));
+  return adapted_frames_.back().frame_buffer;
 }
 
-scoped_refptr<media::VideoFrame> WebRtcVideoFrameAdapter::GetOrWrapFrameForSize(
+WebRtcVideoFrameAdapter::AdaptedFrame WebRtcVideoFrameAdapter::AdaptBestFrame(
     const ScaledBufferSize& size) const {
   double requested_scale_factor =
       static_cast<double>(size.natural_size.width()) /
       size.visible_rect.width();
   // Ideally we have a frame that is in the same scale as |size|. Otherwise, the
   // best frame is the smallest frame that is greater than |size|.
-  scoped_refptr<media::VideoFrame> best_frame = frame_;
+  //
+  // Search for the "best frame" amongst media::VideoFrames (pre-scaled frames).
+  // The "best frame" can either be a media::VideoFrame (a pre-scaled frame) or
+  // a webrtc::VideoFrameBuffer (a previously hard-applied frame).
+  scoped_refptr<media::VideoFrame> best_media_frame = frame_;
   double best_frame_scale_factor = 1.0;
   for (const auto& scaled_frame : scaled_frames_) {
     double scale_factor =
@@ -342,36 +343,66 @@
         frame_->coded_size().width();
     if (scale_factor >= requested_scale_factor &&
         scale_factor < best_frame_scale_factor) {
-      best_frame = scaled_frame;
+      best_media_frame = scaled_frame;
       best_frame_scale_factor = scale_factor;
       if (scale_factor == requested_scale_factor) {
         break;
       }
     }
   }
+  if (best_frame_scale_factor != requested_scale_factor) {
+    // Scaling is needed. Consider if the "best frame" is in fact a previously
+    // adapted frame. Search amongst webrtc::VideoFrameBuffers (previously
+    // hard-applied frames).
+    rtc::scoped_refptr<webrtc::VideoFrameBuffer> best_webrtc_frame;
+    for (const auto& adapted_frame : adapted_frames_) {
+      // For simplicity, ignore frames where the cropping is not identical to a
+      // previous mapping.
+      if (size.visible_rect != adapted_frame.size.visible_rect) {
+        continue;
+      }
+      double scale_factor =
+          static_cast<double>(adapted_frame.size.natural_size.width()) /
+          adapted_frame.size.visible_rect.width();
+      if (scale_factor >= requested_scale_factor &&
+          scale_factor < best_frame_scale_factor) {
+        best_webrtc_frame = adapted_frame.frame_buffer;
+        best_frame_scale_factor = scale_factor;
+      }
+    }
+    if (best_webrtc_frame) {
+      rtc::scoped_refptr<webrtc::VideoFrameBuffer> adapted_webrtc_frame =
+          best_webrtc_frame->Scale(size.natural_size.width(),
+                                   size.natural_size.height());
+      return AdaptedFrame(size, nullptr, adapted_webrtc_frame);
+    }
+  }
   // Because |size| is expressed relative to the full size'd frame, we need to
   // adjust the visible rect for the scale of the best frame.
   gfx::Rect visible_rect(size.visible_rect.x() * best_frame_scale_factor,
                          size.visible_rect.y() * best_frame_scale_factor,
                          size.visible_rect.width() * best_frame_scale_factor,
                          size.visible_rect.height() * best_frame_scale_factor);
-  if (IsApproxEquals(visible_rect, best_frame->visible_rect())) {
+  if (IsApproxEquals(visible_rect, best_media_frame->visible_rect())) {
     // Due to rounding errors it is possible for |visible_rect| to be slightly
     // off, which could either cause unnecessary cropping/scaling or cause
     // crashes if |visible_rect| is not contained within
-    // |best_frame->visible_rect()|, so we adjust it.
-    visible_rect = best_frame->visible_rect();
+    // |best_media_frame->visible_rect()|, so we adjust it.
+    visible_rect = best_media_frame->visible_rect();
   }
-  CHECK(best_frame->visible_rect().Contains(visible_rect))
+  CHECK(best_media_frame->visible_rect().Contains(visible_rect))
       << visible_rect.ToString() << " is not contained within "
-      << best_frame->visible_rect().ToString();
+      << best_media_frame->visible_rect().ToString();
   // Wrapping is only needed if we need to crop or scale the best frame.
-  if (best_frame->visible_rect() == visible_rect &&
-      best_frame->natural_size() == size.natural_size) {
-    return best_frame;
+  if (best_media_frame->visible_rect() != visible_rect ||
+      best_media_frame->natural_size() != size.natural_size) {
+    best_media_frame = media::VideoFrame::WrapVideoFrame(
+        best_media_frame, best_media_frame->format(), visible_rect,
+        size.natural_size);
   }
-  return media::VideoFrame::WrapVideoFrame(best_frame, best_frame->format(),
-                                           visible_rect, size.natural_size);
+  rtc::scoped_refptr<webrtc::VideoFrameBuffer> adapted_webrtc_frame =
+      ConvertToWebRtcVideoFrameBuffer(best_media_frame, shared_resources_);
+  return AdaptedFrame(size, best_media_frame, adapted_webrtc_frame);
 }
 
 scoped_refptr<media::VideoFrame>
diff --git a/third_party/blink/renderer/platform/webrtc/webrtc_video_frame_adapter.h b/third_party/blink/renderer/platform/webrtc/webrtc_video_frame_adapter.h
index 4410bd8..0e8b8e5 100644
--- a/third_party/blink/renderer/platform/webrtc/webrtc_video_frame_adapter.h
+++ b/third_party/blink/renderer/platform/webrtc/webrtc_video_frame_adapter.h
@@ -234,8 +234,11 @@
       int scaled_width,
       int scaled_height) override;
 
-  // If this exact size has been hard-applied, gets the frame associated with
-  // that adaptation, otherwise null.
+  // If this exact size has been hard-applied by wrapping or using a pre-scaled
+  // media::VideoFrame, the associated media::VideoFrame is returned (the
+  // wrapping or original). If this size has not been hard-applied, or it was
+  // hard-applied by scaling a previously adapted webrtc::VideoFrameBuffer, then
+  // null is returned.
   scoped_refptr<media::VideoFrame> GetAdaptedVideoBufferForTesting(
       const ScaledBufferSize& size);
 
@@ -252,14 +255,15 @@
           frame_buffer(std::move(frame_buffer)) {}
 
     ScaledBufferSize size;
+    // If |frame_buffer| was produced without a media::VideoFrame this is null.
     scoped_refptr<media::VideoFrame> video_frame;
     rtc::scoped_refptr<webrtc::VideoFrameBuffer> frame_buffer;
   };
 
   rtc::scoped_refptr<webrtc::VideoFrameBuffer> GetOrCreateFrameBufferForSize(
       const ScaledBufferSize& size);
-  scoped_refptr<media::VideoFrame> GetOrWrapFrameForSize(
-      const ScaledBufferSize& size) const;
+  AdaptedFrame AdaptBestFrame(const ScaledBufferSize& size) const
+      EXCLUSIVE_LOCKS_REQUIRED(adapted_frames_lock_);
 
   base::Lock adapted_frames_lock_;
   const scoped_refptr<media::VideoFrame> frame_;
diff --git a/third_party/blink/renderer/platform/webrtc/webrtc_video_frame_adapter_test.cc b/third_party/blink/renderer/platform/webrtc/webrtc_video_frame_adapter_test.cc
index 2059b5c..b869a2fe 100644
--- a/third_party/blink/renderer/platform/webrtc/webrtc_video_frame_adapter_test.cc
+++ b/third_party/blink/renderer/platform/webrtc/webrtc_video_frame_adapter_test.cc
@@ -362,6 +362,178 @@
   EXPECT_EQ(adapted_frame->coded_size(), frame_480p->coded_size());
 }
 
+// When pre-scaled frames are not available we should scale from previously
+// scaled frames. E.g. scaling 720p to 480p and then to 360p should perform
+// scales "720p -> 480p" and "480p -> 360p" (NOT "720p -> 360p").
+TEST(WebRtcVideoFrameAdapterTest,
+     MapScaledFrameScalesFromClosestPreviouslyScaledFrameWithoutCropping) {
+  std::vector<webrtc::VideoFrameBuffer::Type> kNv12 = {
+      webrtc::VideoFrameBuffer::Type::kNV12};
+  const gfx::Size kSize720p(1280, 720);
+  const gfx::Rect kRect720p(0, 0, 1280, 720);
+  const gfx::Size kSize480p(853, 480);
+  const gfx::Size kSize360p(640, 360);
+
+  scoped_refptr<MockSharedResources> resources =
+      new testing::StrictMock<MockSharedResources>();
+  EXPECT_CALL(*resources, CreateFrame)
+      .WillOnce(testing::Invoke(
+          [](media::VideoPixelFormat format, const gfx::Size& coded_size,
+             const gfx::Rect& visible_rect, const gfx::Size& natural_size,
+             base::TimeDelta timestamp) {
+            return CreateTestFrame(coded_size, visible_rect, natural_size,
+                                   media::VideoFrame::STORAGE_OWNED_MEMORY,
+                                   format);
+          }));
+
+  auto frame_720p = CreateTestFrame(kSize720p, kRect720p, kSize720p,
+                                    media::VideoFrame::STORAGE_OWNED_MEMORY,
+                                    media::VideoPixelFormat::PIXEL_FORMAT_NV12);
+
+  rtc::scoped_refptr<WebRtcVideoFrameAdapter> multi_buffer(
+      new rtc::RefCountedObject<WebRtcVideoFrameAdapter>(
+          frame_720p, std::vector<scoped_refptr<media::VideoFrame>>(),
+          resources));
+
+  // Hard-apply scaling to 480p. Because a pre-scaled 480p is not available, we
+  // scale from 720p.
+  auto scaled_frame_480p =
+      multi_buffer->Scale(kSize480p.width(), kSize480p.height());
+  auto mapped_frame_480p = scaled_frame_480p->GetMappedFrameBuffer(kNv12);
+  EXPECT_EQ(mapped_frame_480p->width(), kSize480p.width());
+  EXPECT_EQ(mapped_frame_480p->height(), kSize480p.height());
+  // The 480p must have been scaled from a media::VideoFrame.
+  EXPECT_TRUE(multi_buffer->GetAdaptedVideoBufferForTesting(
+      WebRtcVideoFrameAdapter::ScaledBufferSize(kRect720p, kSize480p)));
+  // Hard-apply scaling to 360p. Because a pre-scaled 360p is not available, but
+  // we did previously scale to 480p, the most efficient scale is 480p -> 360p.
+  auto scaled_frame_360p =
+      multi_buffer->Scale(kSize360p.width(), kSize360p.height());
+  auto mapped_frame_360p = scaled_frame_360p->GetMappedFrameBuffer(kNv12);
+  EXPECT_EQ(mapped_frame_360p->width(), kSize360p.width());
+  EXPECT_EQ(mapped_frame_360p->height(), kSize360p.height());
+  // The 360p should have gotten scaled from the previously mapped 480p frame,
+  // so there should not be an associated media::VideoFrame here.
+  EXPECT_FALSE(multi_buffer->GetAdaptedVideoBufferForTesting(
+      WebRtcVideoFrameAdapter::ScaledBufferSize(kRect720p, kSize360p)));
+}
+
+TEST(WebRtcVideoFrameAdapterTest,
+     MapScaledFrameScalesFromClosestPreviouslyScaledFrameWithCropping) {
+  std::vector<webrtc::VideoFrameBuffer::Type> kNv12 = {
+      webrtc::VideoFrameBuffer::Type::kNV12};
+  const gfx::Size kFullCodedSize720p(1280, 720);
+  const gfx::Rect kFullVisibleRect(20, 20, 1240, 680);  // 20 pixel border.
+  const gfx::Size kFullNaturalSize(620, 340);           // Scaled down by 2.
+
+  scoped_refptr<MockSharedResources> resources =
+      new testing::StrictMock<MockSharedResources>();
+  EXPECT_CALL(*resources, CreateFrame)
+      .WillOnce(testing::Invoke(
+          [](media::VideoPixelFormat format, const gfx::Size& coded_size,
+             const gfx::Rect& visible_rect, const gfx::Size& natural_size,
+             base::TimeDelta timestamp) {
+            return CreateTestFrame(coded_size, visible_rect, natural_size,
+                                   media::VideoFrame::STORAGE_OWNED_MEMORY,
+                                   format);
+          }));
+
+  // Create a full frame with soft-applied cropping and scaling.
+  auto full_frame =
+      CreateTestFrame(kFullCodedSize720p, kFullVisibleRect, kFullNaturalSize,
+                      media::VideoFrame::STORAGE_OWNED_MEMORY,
+                      media::VideoPixelFormat::PIXEL_FORMAT_NV12);
+
+  rtc::scoped_refptr<WebRtcVideoFrameAdapter> multi_buffer(
+      new rtc::RefCountedObject<WebRtcVideoFrameAdapter>(
+          full_frame, std::vector<scoped_refptr<media::VideoFrame>>(),
+          resources));
+
+  // Crop and scale some more and then map it.
+  // Apply a 10 pixel border and downscale by a factor of 2 again.
+  auto scaled_frame = multi_buffer->CropAndScale(10, 10, 600, 320, 300, 160);
+  auto mapped_scaled_frame = scaled_frame->GetMappedFrameBuffer(kNv12);
+  gfx::Size kScaledFrameSize(300, 160);
+  EXPECT_EQ(mapped_scaled_frame->width(), kScaledFrameSize.width());
+  EXPECT_EQ(mapped_scaled_frame->height(), kScaledFrameSize.height());
+  // The cropping above is magnified due to scaling factors.
+  gfx::Rect kScaledFrameVisibleRect(kFullVisibleRect.x() + (10 * 2),
+                                    kFullVisibleRect.y() + (10 * 2), (600 * 2),
+                                    (320 * 2));
+  EXPECT_TRUE(multi_buffer->GetAdaptedVideoBufferForTesting(
+      WebRtcVideoFrameAdapter::ScaledBufferSize(kScaledFrameVisibleRect,
+                                                kScaledFrameSize)));
+
+  // Downscale by another factor of two.
+  gfx::Size kTinyFrameSize(kScaledFrameSize.width() / 2,
+                           kScaledFrameSize.height() / 2);
+  auto tiny_frame =
+      scaled_frame->Scale(kTinyFrameSize.width(), kTinyFrameSize.height());
+  auto mapped_tiny_frame = tiny_frame->GetMappedFrameBuffer(kNv12);
+  EXPECT_EQ(mapped_tiny_frame->width(), kTinyFrameSize.width());
+  EXPECT_EQ(mapped_tiny_frame->height(), kTinyFrameSize.height());
+  // Because we do not have any pre-scaled images, but we have mapped frames,
+  // subsequent downscales should be based on the previous mappings rather than
+  // the full frame.
+  EXPECT_FALSE(multi_buffer->GetAdaptedVideoBufferForTesting(
+      WebRtcVideoFrameAdapter::ScaledBufferSize(kScaledFrameVisibleRect,
+                                                kTinyFrameSize)));
+}
+
+TEST(WebRtcVideoFrameAdapterTest,
+     MapScaledFrameDoesNotScaleFromPreviouslyScaledFrameWithOtherCrop) {
+  std::vector<webrtc::VideoFrameBuffer::Type> kNv12 = {
+      webrtc::VideoFrameBuffer::Type::kNV12};
+  const gfx::Size kSize720p(1280, 720);
+  const gfx::Rect kRect720p(0, 0, 1280, 720);
+  const gfx::Rect kCroppedRect(1272, 720);  // Crop only a few pixels.
+  const gfx::Size kSize480p(853, 480);
+  const gfx::Size kSize360p(640, 360);
+
+  scoped_refptr<MockSharedResources> resources =
+      new testing::StrictMock<MockSharedResources>();
+  EXPECT_CALL(*resources, CreateFrame)
+      .Times(2)
+      .WillRepeatedly(testing::Invoke(
+          [](media::VideoPixelFormat format, const gfx::Size& coded_size,
+             const gfx::Rect& visible_rect, const gfx::Size& natural_size,
+             base::TimeDelta timestamp) {
+            return CreateTestFrame(coded_size, visible_rect, natural_size,
+                                   media::VideoFrame::STORAGE_OWNED_MEMORY,
+                                   format);
+          }));
+
+  auto frame_720p = CreateTestFrame(kSize720p, kRect720p, kSize720p,
+                                    media::VideoFrame::STORAGE_OWNED_MEMORY,
+                                    media::VideoPixelFormat::PIXEL_FORMAT_NV12);
+
+  rtc::scoped_refptr<WebRtcVideoFrameAdapter> multi_buffer(
+      new rtc::RefCountedObject<WebRtcVideoFrameAdapter>(
+          frame_720p, std::vector<scoped_refptr<media::VideoFrame>>(),
+          resources));
+
+  // Hard-apply scaling to 480p WITH cropping.
+  auto scaled_frame_480p = multi_buffer->CropAndScale(
+      kCroppedRect.x(), kCroppedRect.y(), kCroppedRect.width(),
+      kCroppedRect.height(), kSize480p.width(), kSize480p.height());
+  auto mapped_frame_480p = scaled_frame_480p->GetMappedFrameBuffer(kNv12);
+  EXPECT_EQ(mapped_frame_480p->width(), kSize480p.width());
+  EXPECT_EQ(mapped_frame_480p->height(), kSize480p.height());
+  // The 480p must have been scaled from a media::VideoFrame.
+  EXPECT_TRUE(multi_buffer->GetAdaptedVideoBufferForTesting(
+      WebRtcVideoFrameAdapter::ScaledBufferSize(kCroppedRect, kSize480p)));
+  // Hard-apply scaling to 360p WITHOUT cropping.
+  auto scaled_frame_360p =
+      multi_buffer->Scale(kSize360p.width(), kSize360p.height());
+  auto mapped_frame_360p = scaled_frame_360p->GetMappedFrameBuffer(kNv12);
+  EXPECT_EQ(mapped_frame_360p->width(), kSize360p.width());
+  EXPECT_EQ(mapped_frame_360p->height(), kSize360p.height());
+  // Because the previously mapped 480p buffer has cropping it cannot be used
+  // for scaling, so 360p is produced from the 720p frame.
+  EXPECT_TRUE(multi_buffer->GetAdaptedVideoBufferForTesting(
+      WebRtcVideoFrameAdapter::ScaledBufferSize(kRect720p, kSize360p)));
+}
+
 TEST(WebRtcVideoFrameAdapterTest, CanApplyCropAndScale) {
   std::vector<webrtc::VideoFrameBuffer::Type> kNv12 = {
       webrtc::VideoFrameBuffer::Type::kNV12};
diff --git a/third_party/blink/renderer/platform/widget/compositing/android_webview/synchronous_layer_tree_frame_sink.cc b/third_party/blink/renderer/platform/widget/compositing/android_webview/synchronous_layer_tree_frame_sink.cc
index d9be235..4043172 100644
--- a/third_party/blink/renderer/platform/widget/compositing/android_webview/synchronous_layer_tree_frame_sink.cc
+++ b/third_party/blink/renderer/platform/widget/compositing/android_webview/synchronous_layer_tree_frame_sink.cc
@@ -361,9 +361,9 @@
         embed_render_pass->CreateAndAppendDrawQuad<viz::SurfaceDrawQuad>();
     shared_quad_state->SetAll(
         child_transform, gfx::Rect(child_size), gfx::Rect(child_size),
-        gfx::MaskFilterInfo(), gfx::Rect() /* clip_rect */,
-        false /* is_clipped */, are_contents_opaque /* are_contents_opaque */,
-        1.f /* opacity */, SkBlendMode::kSrcOver, 0 /* sorting_context_id */);
+        gfx::MaskFilterInfo(), base::nullopt /* clip_rect */,
+        are_contents_opaque /* are_contents_opaque */, 1.f /* opacity */,
+        SkBlendMode::kSrcOver, 0 /* sorting_context_id */);
     surface_quad->SetNew(
         shared_quad_state, gfx::Rect(child_size), gfx::Rect(child_size),
         viz::SurfaceRange(
diff --git a/third_party/blink/tools/BUILD.gn b/third_party/blink/tools/BUILD.gn
index fc4788a..8897b591 100644
--- a/third_party/blink/tools/BUILD.gn
+++ b/third_party/blink/tools/BUILD.gn
@@ -20,6 +20,9 @@
     # the test runner codebase, manifest file, and the tests themselves.
     "//third_party/blink/web_tests/external/",
 
+    # The web_tests/platform directory contains platform specific WPT baselines.
+    "//third_party/blink/web_tests/platform/",
+
     # Include a single file from wpt_internal only to initialize that directory.
     # We don't need to run these tests but still need to create a MANIFEST here.
     # See crbug.com/1018829 for more details.
diff --git a/third_party/blink/tools/blinkpy/web_tests/port/android.py b/third_party/blink/tools/blinkpy/web_tests/port/android.py
index b3ce9d4..fa11335 100644
--- a/third_party/blink/tools/blinkpy/web_tests/port/android.py
+++ b/third_party/blink/tools/blinkpy/web_tests/port/android.py
@@ -295,8 +295,9 @@
     SUPPORTED_VERSIONS = ('android')
 
     FALLBACK_PATHS = {
-        'kitkat':
-        ['android'] + linux.LinuxPort.latest_platform_fallback_path()
+        # Don't use the current android platform specific baselines since
+        # they are no longer maintained.
+        'pie': linux.LinuxPort.latest_platform_fallback_path()
     }
 
     BUILD_REQUIREMENTS_URL = 'https://www.chromium.org/developers/how-tos/android-build-instructions'
@@ -305,7 +306,7 @@
         super(AndroidPort, self).__init__(
             host, port_name, options=options, **kwargs)
         self._operating_system = 'android'
-        self._version = 'kitkat'
+        self._version = 'pie'
         fs = host.filesystem
         self._local_port = factory.PortFactory(host).get(**kwargs)
         if apk or product:
diff --git a/third_party/blink/web_tests/NeverFixTests b/third_party/blink/web_tests/NeverFixTests
index 0a9fee2d..775990c9 100644
--- a/third_party/blink/web_tests/NeverFixTests
+++ b/third_party/blink/web_tests/NeverFixTests
@@ -250,19 +250,12 @@
 [ Mac ] external/wpt/html/dom/elements/requirements-relating-to-bidirectional-algorithm-formatting-characters/dir-isolation-009c.html [ Skip ]
 
 # Failing css-transforms-2 web platform tests.
-crbug.com/753080 external/wpt/css/css-transforms/backface-visibility-hidden-001.html [ Skip ]
-crbug.com/753080 external/wpt/css/css-transforms/rotate_x_45deg.html [ Skip ]
-crbug.com/753080 external/wpt/css/css-transforms/rotate_y_45deg.html [ Skip ]
 crbug.com/753080 external/wpt/css/css-transforms/transform-3d-rotateY-stair-below-001.xht [ Skip ]
 crbug.com/753080 external/wpt/css/css-transforms/transform3d-image-scale-001.html [ Skip ]
 crbug.com/753080 external/wpt/css/css-transforms/transform3d-image-scale-002.html [ Skip ]
-crbug.com/753080 external/wpt/css/css-transforms/transform3d-matrix3d-001.html [ Skip ]
 crbug.com/753080 external/wpt/css/css-transforms/transform3d-perspective-003.html [ Skip ]
 crbug.com/753080 external/wpt/css/css-transforms/transform3d-perspective-004.html [ Skip ]
 crbug.com/753080 external/wpt/css/css-transforms/transform3d-perspective-005.html [ Skip ]
-crbug.com/753080 external/wpt/css/css-transforms/transform3d-preserve3d-010.html [ Skip ]
-crbug.com/753080 external/wpt/css/css-transforms/transform3d-preserve3d-013.html [ Skip ]
-crbug.com/753080 external/wpt/css/css-transforms/transform3d-rotatex-perspective-003.html [ Skip ]
 crbug.com/753080 external/wpt/css/css-transforms/transform3d-sorting-002.html [ Skip ]
 crbug.com/753080 external/wpt/css/css-transforms/transform3d-sorting-004.html [ Skip ]
 crbug.com/753080 external/wpt/css/css-transforms/transform3d-sorting-006.html [ Skip ]
diff --git a/third_party/blink/web_tests/TestExpectations b/third_party/blink/web_tests/TestExpectations
index ebaa51f..c31aede 100644
--- a/third_party/blink/web_tests/TestExpectations
+++ b/third_party/blink/web_tests/TestExpectations
@@ -1084,13 +1084,9 @@
 crbug.com/1146973 virtual/layout_ng_block_frag/external/wpt/css/css-break/out-of-flow-in-multicolumn-009.html [ Failure ]
 crbug.com/1079031 virtual/layout_ng_block_frag/external/wpt/css/css-multicol/change-out-of-flow-type-and-remove-inner-multicol-crash.html [ Crash Failure ]
 crbug.com/996655 virtual/layout_ng_block_frag/external/wpt/css/css-multicol/going-out-of-flow-after-spanner.html [ Crash Failure Pass ]
-crbug.com/1079031 virtual/layout_ng_block_frag/external/wpt/css/css-multicol/multicol-fill-auto-002.xht [ Crash Failure ]
-crbug.com/1079031 virtual/layout_ng_block_frag/external/wpt/css/css-multicol/multicol-rule-002.xht [ Crash Failure Timeout ]
-crbug.com/1079031 virtual/layout_ng_block_frag/external/wpt/css/css-multicol/multicol-rule-fraction-003.xht [ Crash Failure ]
 crbug.com/874051 virtual/layout_ng_block_frag/external/wpt/css/css-multicol/multicol-span-all-fieldset-002.html [ Failure Crash ]
 crbug.com/1130451 virtual/layout_ng_block_frag/external/wpt/css/css-multicol/multicol-span-all-list-item-001.html [ Failure ]
 crbug.com/1130451 virtual/layout_ng_block_frag/external/wpt/css/css-multicol/multicol-span-all-list-item-002.html [ Failure ]
-crbug.com/1079031 virtual/layout_ng_block_frag/external/wpt/css/css-multicol/multicol-width-003.xht [ Crash Failure Timeout ]
 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 ]
@@ -4454,7 +4450,6 @@
 crbug.com/1179585 virtual/layout_ng_svg_text/svg/custom/mouse-move-on-svg-container-standalone.svg [ Failure ]
 crbug.com/1179585 virtual/layout_ng_svg_text/svg/custom/mouse-move-on-svg-root.xhtml [ Failure ]
 crbug.com/1179585 virtual/layout_ng_svg_text/svg/custom/mouse-move-on-svg-root-standalone.svg [ Failure ]
-crbug.com/1179585 virtual/layout_ng_svg_text/svg/custom/non-opaque-filters.svg [ Failure ]
 crbug.com/1179585 virtual/layout_ng_svg_text/svg/custom/path-textPath-simulation.svg [ Failure ]
 crbug.com/1179585 virtual/layout_ng_svg_text/svg/custom/pattern-cycle-detection.svg [ Failure ]
 crbug.com/1179585 virtual/layout_ng_svg_text/svg/custom/pattern-deep-referencing.svg [ Failure ]
@@ -4480,12 +4475,12 @@
 crbug.com/1179585 virtual/layout_ng_svg_text/svg/custom/style-attribute-font-size.svg [ Failure ]
 crbug.com/1179585 virtual/layout_ng_svg_text/svg/custom/SVGMatrix-interface.svg [ Failure ]
 crbug.com/1179585 virtual/layout_ng_svg_text/svg/custom/svg-root-with-opacity.html [ Pass Failure ]
-crbug.com/1179585 virtual/layout_ng_svg_text/svg/custom/text-clip.svg [ Failure ]
+crbug.com/1179585 [ Mac ] virtual/layout_ng_svg_text/svg/custom/text-clip.svg [ Failure ]
 crbug.com/1179585 virtual/layout_ng_svg_text/svg/custom/text-decoration-visibility.svg [ Failure ]
 crbug.com/1179585 virtual/layout_ng_svg_text/svg/custom/text-dom-01-f.svg [ Failure Crash ]
-crbug.com/1179585 virtual/layout_ng_svg_text/svg/custom/text-filter.svg [ Failure ]
+crbug.com/1179585 [ Mac ] virtual/layout_ng_svg_text/svg/custom/text-filter.svg [ Failure ]
 crbug.com/1179585 virtual/layout_ng_svg_text/svg/custom/text-hit-test.svg [ Failure ]
-crbug.com/1179585 virtual/layout_ng_svg_text/svg/custom/text-image-opacity.svg [ Failure ]
+crbug.com/1179585 [ Mac ] virtual/layout_ng_svg_text/svg/custom/text-image-opacity.svg [ Failure ]
 crbug.com/1179585 virtual/layout_ng_svg_text/svg/custom/text-letter-spacing.svg [ Failure ]
 crbug.com/1179585 virtual/layout_ng_svg_text/svg/custom/text-linking.svg [ Failure ]
 crbug.com/1179585 virtual/layout_ng_svg_text/svg/custom/text-match-highlight.html [ Failure ]
@@ -4501,7 +4496,7 @@
 crbug.com/1179585 virtual/layout_ng_svg_text/svg/custom/use-dynamic-append.svg [ Failure ]
 crbug.com/1179585 virtual/layout_ng_svg_text/svg/custom/use-event-handler-on-referenced-element.svg [ Failure ]
 crbug.com/1179585 virtual/layout_ng_svg_text/svg/custom/use-events-crash.svg [ Failure ]
-crbug.com/1179585 virtual/layout_ng_svg_text/svg/custom/use-font-face-crash.svg [ Failure ]
+crbug.com/1179585 [ Mac ] virtual/layout_ng_svg_text/svg/custom/use-font-face-crash.svg [ Failure ]
 crbug.com/1179585 virtual/layout_ng_svg_text/svg/custom/use-modify-container-in-target.svg [ Failure ]
 crbug.com/1179585 virtual/layout_ng_svg_text/svg/custom/use-modify-target-container.svg [ Failure ]
 crbug.com/1179585 virtual/layout_ng_svg_text/svg/custom/use-modify-target-symbol.svg [ Failure ]
@@ -4543,7 +4538,6 @@
 crbug.com/1179585 virtual/layout_ng_svg_text/svg/filters/feDisplacementMap.svg [ Pass Timeout ]
 crbug.com/1179585 virtual/layout_ng_svg_text/svg/filters/filter-on-filter-for-text.svg [ Failure ]
 crbug.com/1179585 virtual/layout_ng_svg_text/svg/filters/filter-on-tspan.svg [ Failure ]
-crbug.com/1179585 virtual/layout_ng_svg_text/svg/filters/sourceAlpha.svg [ Failure ]
 crbug.com/1179585 virtual/layout_ng_svg_text/svg/foreignObject/transformed-text-invalidation.html [ Failure ]
 crbug.com/1179585 virtual/layout_ng_svg_text/svg/hittest/rotated-text.svg [ Failure ]
 crbug.com/1179585 virtual/layout_ng_svg_text/svg/hittest/singular-transform-3.html [ Failure ]
@@ -4557,22 +4551,20 @@
 crbug.com/1179585 virtual/layout_ng_svg_text/svg/hittest/text-with-text-path.svg [ Failure ]
 crbug.com/1179585 virtual/layout_ng_svg_text/svg/hixie/dynamic/002.xml [ Failure ]
 crbug.com/1179585 virtual/layout_ng_svg_text/svg/hixie/error/002.xml [ Failure ]
-crbug.com/1179585 virtual/layout_ng_svg_text/svg/hixie/error/010.xml [ Failure ]
-crbug.com/1179585 virtual/layout_ng_svg_text/svg/hixie/error/011.xml [ Failure ]
-crbug.com/1179585 virtual/layout_ng_svg_text/svg/hixie/error/017.xml [ Failure ]
+crbug.com/1179585 [ Mac ] virtual/layout_ng_svg_text/svg/hixie/error/010.xml [ Failure ]
+crbug.com/1179585 [ Mac ] virtual/layout_ng_svg_text/svg/hixie/error/011.xml [ Failure ]
 crbug.com/1179585 virtual/layout_ng_svg_text/svg/hixie/links/002.xml [ Failure ]
 crbug.com/1179585 virtual/layout_ng_svg_text/svg/hixie/perf/001.xml [ Failure Crash ]
 crbug.com/1179585 virtual/layout_ng_svg_text/svg/hixie/perf/002.xml [ Failure Crash ]
 crbug.com/1179585 virtual/layout_ng_svg_text/svg/hixie/perf/003.xml [ Failure Crash ]
 crbug.com/1179585 virtual/layout_ng_svg_text/svg/hixie/perf/004.xml [ Pass Timeout Crash Failure ]
 crbug.com/1179585 virtual/layout_ng_svg_text/svg/hixie/perf/007.xml [ Failure Crash ]
-crbug.com/1179585 virtual/layout_ng_svg_text/svg/hixie/text/001.xml [ Failure ]
-crbug.com/1179585 virtual/layout_ng_svg_text/svg/hixie/text/002.xml [ Failure ]
+crbug.com/1179585 [ Mac ] virtual/layout_ng_svg_text/svg/hixie/text/002.xml [ Failure ]
 crbug.com/1179585 virtual/layout_ng_svg_text/svg/hixie/viewbox/preserveAspectRatio/001.xml [ Failure ]
 crbug.com/1179585 virtual/layout_ng_svg_text/svg/hixie/viewbox/preserveAspectRatio/002.xml [ Failure ]
 crbug.com/1179585 virtual/layout_ng_svg_text/svg/masking/mask-of-root.html [ Failure ]
-crbug.com/1179585 [ Linux ] virtual/layout_ng_svg_text/svg/paintorder/paintorder-text-decorations.svg [ Failure ]
-crbug.com/1179585 [ Linux ] virtual/layout_ng_svg_text/svg/paintorder/paintorder-text.svg [ Failure ]
+crbug.com/1179585 virtual/layout_ng_svg_text/svg/paintorder/paintorder-text-decorations.svg [ Failure ]
+crbug.com/1179585 virtual/layout_ng_svg_text/svg/paintorder/paintorder-text.svg [ Failure ]
 crbug.com/1179585 virtual/layout_ng_svg_text/svg/stroke/dasharray-dashoffset-text.html [ Failure ]
 crbug.com/1179585 virtual/layout_ng_svg_text/svg/stroke/dasharray-text.html [ Failure ]
 crbug.com/1179585 virtual/layout_ng_svg_text/svg/stroke/non-scaling-stroke-text-decoration.html [ Failure ]
@@ -4644,11 +4636,9 @@
 crbug.com/1179585 virtual/layout_ng_svg_text/svg/text/svgtextcontentelement-glyphqueries-rtl.html [ Failure ]
 crbug.com/1179585 virtual/layout_ng_svg_text/svg/text/svgtextcontentelement-methods-parameters.html [ Failure ]
 crbug.com/1179585 virtual/layout_ng_svg_text/svg/text/text-decorations-in-scaled-pattern.svg [ Failure ]
-crbug.com/1179585 virtual/layout_ng_svg_text/svg/text/text-fill-opacity.svg [ Failure ]
 crbug.com/1179585 virtual/layout_ng_svg_text/svg/text/text-getSubStringLength.html [ Failure ]
 crbug.com/1179585 virtual/layout_ng_svg_text/svg/text/text-gradient-positioning.svg [ Failure ]
 crbug.com/1179585 virtual/layout_ng_svg_text/svg/text/text-layout-crash.html [ Failure ]
-crbug.com/1179585 virtual/layout_ng_svg_text/svg/text/text-midpoint-split-bug.svg [ Failure ]
 crbug.com/1179585 virtual/layout_ng_svg_text/svg/text/text-outline-2.html [ Failure ]
 crbug.com/1179585 virtual/layout_ng_svg_text/svg/text/textPathBoundsBug.svg [ Failure ]
 crbug.com/1179585 virtual/layout_ng_svg_text/svg/text/text-path-middle-align.svg [ Failure ]
@@ -4684,12 +4674,9 @@
 crbug.com/1179585 virtual/layout_ng_svg_text/svg/text/tspan-multiple-outline-vertical.svg [ Failure ]
 crbug.com/1179585 virtual/layout_ng_svg_text/svg/text/tspan-outline.html [ Failure ]
 crbug.com/1179585 virtual/layout_ng_svg_text/svg/text/word-spacing-leading-ws.html [ Failure ]
-crbug.com/1179585 virtual/layout_ng_svg_text/svg/transforms/change-transform-to-none-text.html [ Failure ]
-crbug.com/1179585 virtual/layout_ng_svg_text/svg/transforms/text-transform-origin.html [ Failure ]
 crbug.com/1179585 virtual/layout_ng_svg_text/svg/transforms/text-with-mask-with-svg-transform.svg [ Failure ]
 crbug.com/1179585 virtual/layout_ng_svg_text/svg/transforms/text-with-pattern-inside-transformed-html.xhtml [ Failure ]
 crbug.com/1179585 virtual/layout_ng_svg_text/svg/transforms/text-with-pattern-with-svg-transform.svg [ Failure ]
-crbug.com/1179585 virtual/layout_ng_svg_text/svg/transforms/text-with-transform-and-zoom.html [ Failure ]
 crbug.com/1179585 virtual/layout_ng_svg_text/svg/transforms/transformed-text-fill-pattern.html [ Failure ]
 crbug.com/1179585 virtual/layout_ng_svg_text/svg/W3C-I18N/g-dirLTR-ubNone.svg [ Failure ]
 crbug.com/1179585 virtual/layout_ng_svg_text/svg/W3C-I18N/g-dirLTR-ubOverride.svg [ Failure ]
diff --git a/third_party/blink/web_tests/WebGPUExpectations b/third_party/blink/web_tests/WebGPUExpectations
index 0342ded9..4e31478 100644
--- a/third_party/blink/web_tests/WebGPUExpectations
+++ b/third_party/blink/web_tests/WebGPUExpectations
@@ -29,6 +29,7 @@
 wpt_internal/webgpu/cts.html?q=webgpu:api,operation,command_buffer,copyTextureToTexture:color_textures,compressed,non_array:format="bc7-rgba-unorm";* [ Failure ]
 wpt_internal/webgpu/cts.html?q=webgpu:api,validation,createTexture:sampleCount,various_sampleCount_with_all_formats:* [ Failure Crash ]
 crbug.com/dawn/759 [ Linux ] wpt_internal/webgpu/cts.html?q=webgpu:api,operation,rendering,draw:vertex_attributes,basic:* [ Failure ]
+crbug.com/1203413 [ Mac ] wpt_internal/webgpu/cts.html?worker=1&q=webgpu:api,validation,buffer,mapping:* [ Crash ]
 
 # Fails or crashes on numerous combinations of backends, hardware, and validation layers
 wpt_internal/webgpu/cts.html?q=webgpu:api,operation,render_pass,resolve:* [ Skip ]
@@ -180,9 +181,8 @@
 # Failure in both D3D12 and Vulkan validation layers
 wpt_internal/webgpu/cts.html?q=webgpu:api,validation,encoding,cmds,index_access:* [ Skip ]
 
-# Assert in debug builds
-crbug.com/dawn/614 wpt_internal/webgpu/cts.html?q=webgpu:api,validation,createBindGroup:buffer_binding_must_contain_exactly_one_buffer_of_its_type:bindingType="writeonly-storage-texture";resourceType="sampledTexMS" [ Crash ]
-crbug.com/dawn/614 wpt_internal/webgpu/cts.html?q=webgpu:api,validation,createBindGroup:buffer_binding_must_contain_exactly_one_buffer_of_its_type:bindingType="readonly-storage-texture";resourceType="sampledTexMS" [ Crash ]
+# Assert in debug build
+crbug.com/dawn/614 wpt_internal/webgpu/cts.html?q=webgpu:api,validation,createBindGroup:* [ Crash ]
 
 # Crash with validation layers (Vulkan/D3D12).
 crbug.com/dawn/615 wpt_internal/webgpu/cts.html?q=webgpu:api,operation,vertex_state,index_format:* [ Skip ]
diff --git a/third_party/blink/web_tests/external/wpt/animation-worklet/idlharness.any.worker-expected.txt b/third_party/blink/web_tests/external/wpt/animation-worklet/idlharness.any.worker-expected.txt
index 6025a84..c8ff398 100644
--- a/third_party/blink/web_tests/external/wpt/animation-worklet/idlharness.any.worker-expected.txt
+++ b/third_party/blink/web_tests/external/wpt/animation-worklet/idlharness.any.worker-expected.txt
@@ -15,7 +15,7 @@
 PASS Animation interface: existence and properties of interface object
 PASS WorkletGlobalScope interface: existence and properties of interface object
 PASS Worklet interface: existence and properties of interface object
-FAIL CSS namespace: operation escape(CSSOMString) Cannot read property 'hasOwnProperty' of undefined
-FAIL CSS namespace: attribute animationWorklet Cannot read property 'hasOwnProperty' of undefined
+PASS CSS namespace: operation escape(CSSOMString)
+FAIL CSS namespace: attribute animationWorklet assert_own_property: CSS does not have property "animationWorklet" expected property "animationWorklet" missing
 Harness: the test ran to completion.
 
diff --git a/third_party/blink/web_tests/external/wpt/css/css-transforms/backface-visibility-hidden-001.html b/third_party/blink/web_tests/external/wpt/css/css-transforms/backface-visibility-hidden-001.html
index e9a463f..3ccfc21 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-transforms/backface-visibility-hidden-001.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-transforms/backface-visibility-hidden-001.html
@@ -6,6 +6,7 @@
     <link rel="help" href="http://www.w3.org/TR/css-transforms-1/#transform-property">
     <link rel="help" href="http://www.w3.org/TR/css-transforms-2/#propdef-backface-visibility">
     <link rel="match" href="reference/backface-visibility-hidden-ref.html">
+    <meta name=fuzzy content="0-25;0-200">
     <meta name="assert" content="When the value of backface visibility property is 'hidden', the back side of a transformed element is invisible when facing the viewer.">
     <style type="text/css">
         .greenSquare {
diff --git a/third_party/blink/web_tests/external/wpt/css/css-transforms/rotate_x_45deg.html b/third_party/blink/web_tests/external/wpt/css/css-transforms/rotate_x_45deg.html
index fa68e2ab..654659c 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-transforms/rotate_x_45deg.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-transforms/rotate_x_45deg.html
@@ -5,6 +5,7 @@
   <link rel="author" title="Ebay Inc." href="mailto:xiatian@ebay.com"/>
   <link rel="help" href="http://www.w3.org/TR/css-transforms-2/#3d-transform-rendering"/>
   <link rel="match" href="rotate_x_45deg-ref.html"/>
+  <meta name=fuzzy content="159;200">
   <meta name="flags" content="" />
   <meta name="assert" content="Rotate 45 degree in y axis"/>
   <style type="text/css">
diff --git a/third_party/blink/web_tests/external/wpt/css/css-transforms/rotate_y_45deg.html b/third_party/blink/web_tests/external/wpt/css/css-transforms/rotate_y_45deg.html
index 77a8b88..2569cc7 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-transforms/rotate_y_45deg.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-transforms/rotate_y_45deg.html
@@ -5,6 +5,7 @@
   <link rel="author" title="Ebay Inc." href="mailto:xiatian@ebay.com"/>
   <link rel="help" href="http://www.w3.org/TR/css-transforms-2/#3d-transform-rendering"/>
   <link rel="match" href="rotate_y_45deg-ref.html"/>
+  <meta name=fuzzy content="159;200">
   <meta name="flags" content="" />
   <meta name="assert" content="Rotate 45 degree in y axis"/>
   <style type="text/css">
diff --git a/third_party/blink/web_tests/external/wpt/css/css-transforms/transform3d-matrix3d-001.html b/third_party/blink/web_tests/external/wpt/css/css-transforms/transform3d-matrix3d-001.html
index aa32234b..e513eeb50 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-transforms/transform3d-matrix3d-001.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-transforms/transform3d-matrix3d-001.html
@@ -10,6 +10,7 @@
     various matrix3d()s are equivalent to other transform functions.'>
     <link rel="match" href="transform3d-matrix3d-001-ref.html">
     <link rel="mismatch" href="transform-lime-square-ref.html">
+    <meta name=fuzzy content="transform3d-matrix3d-001-ref.html:0-100;950-980">
   </head>
   <body>
     <div style="transform: matrix3d(1,2,0,0, 3,4,0,0, 0,0,1,0, 5,6,0,1);
diff --git a/third_party/blink/web_tests/external/wpt/css/css-transforms/transform3d-preserve3d-010.html b/third_party/blink/web_tests/external/wpt/css/css-transforms/transform3d-preserve3d-010.html
index 89a8819..d00f71c 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-transforms/transform3d-preserve3d-010.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-transforms/transform3d-preserve3d-010.html
@@ -9,6 +9,7 @@
     the X-axis, should be equivalent to shrinking the height by a factor of
     sqrt(2), i.e., 1.41421356....">
     <link rel="match" href="transform-lime-square-ref.html">
+    <meta name=fuzzy content="0-200;0-100">
     <style>
       div {
         transform: rotatex(22.5deg);
diff --git a/third_party/blink/web_tests/external/wpt/css/css-transforms/transform3d-preserve3d-013.html b/third_party/blink/web_tests/external/wpt/css/css-transforms/transform3d-preserve3d-013.html
index 2114286..3aac76d 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-transforms/transform3d-preserve3d-013.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-transforms/transform3d-preserve3d-013.html
@@ -9,6 +9,7 @@
     'hidden'.  (Note that the ref is nontrivial, because the scrollbar has to
     be scaled appropriately.)">
     <link rel="match" href="transform3d-preserve3d-013-ref.html">
+    <meta name=fuzzy content="0-100;0-1750">
   </head>
   <body>
     <div style="transform: rotatex(45deg); transform-origin: top;
diff --git a/third_party/blink/web_tests/external/wpt/css/css-transforms/transform3d-rotatex-perspective-003.html b/third_party/blink/web_tests/external/wpt/css/css-transforms/transform3d-rotatex-perspective-003.html
index 620f3b9..fb0f9e1 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-transforms/transform3d-rotatex-perspective-003.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-transforms/transform3d-rotatex-perspective-003.html
@@ -11,6 +11,8 @@
     not affect anything in the test.">
     <link rel="match" href="transform3d-rotatex-perspective-ref.html">
     <link rel="mismatch" href="transform3d-rotatex-perspective-notref.html">
+    <meta name=fuzzy content="0-25;0-1000">
+
     <style>
       #container {
         position: relative;
diff --git a/third_party/blink/web_tests/external/wpt/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.CSSHSL.html b/third_party/blink/web_tests/external/wpt/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.CSSHSL.html
new file mode 100644
index 0000000..9850817c
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.CSSHSL.html
@@ -0,0 +1,51 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.fillStyle.CSSHSL</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.fillStyle.CSSHSL</h1>
+<p class="desc">CSSHSL works as color input</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("CSSHSL works as color input");
+_addTest(function(canvas, ctx) {
+
+ctx.fillStyle = new CSSHSL(CSS.deg(180), 0.5, 0.5);
+ctx.fillRect(0, 0, 100, 50);
+_assertPixelApprox(canvas, 50,25, 64,191,191,255, "50,25", "64,191,191,255", 3);
+
+const color = new CSSHSL(CSS.deg(180), 1, 1);
+ctx.fillStyle = color;
+_assertSame(ctx.fillStyle, '#ffffff', "ctx.fillStyle", "'#ffffff'");
+ctx.fillRect(0, 0, 100, 50);
+_assertPixel(canvas, 50,25, 255,255,255,255, "50,25", "255,255,255,255");
+color.l = 0.5;
+ctx.fillStyle = color;
+_assertSame(ctx.fillStyle, '#00ffff', "ctx.fillStyle", "'#00ffff'");
+ctx.fillRect(0, 0, 100, 50);
+_assertPixel(canvas, 50,25, 0,255,255,255, "50,25", "0,255,255,255");
+
+ctx.fillStyle = new CSSRGB(1, 0, 1).toHSL();
+_assertSame(ctx.fillStyle, '#ff00ff', "ctx.fillStyle", "'#ff00ff'");
+ctx.fillRect(0, 0, 100, 50);
+_assertPixel(canvas, 50,25, 255,0,255,255, "50,25", "255,0,255,255");
+
+color.h = CSS.deg(120);
+color.s = 1;
+color.l = 0.5;
+ctx.fillStyle = color;
+ctx.fillRect(0, 0, 100, 50);
+
+
+});
+</script>
+
diff --git a/third_party/blink/web_tests/external/wpt/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.CSSRGB.html b/third_party/blink/web_tests/external/wpt/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.CSSRGB.html
new file mode 100644
index 0000000..f916b8f
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/html/canvas/element/fill-and-stroke-styles/2d.fillStyle.CSSRGB.html
@@ -0,0 +1,60 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>Canvas test: 2d.fillStyle.CSSRGB</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
+<body class="show_output">
+
+<h1>2d.fillStyle.CSSRGB</h1>
+<p class="desc">CSSRGB works as color input</p>
+
+
+<p class="output">Actual output:</p>
+<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
+<ul id="d"></ul>
+<script>
+var t = async_test("CSSRGB works as color input");
+_addTest(function(canvas, ctx) {
+
+ctx.fillStyle = new CSSRGB(1, 0, 1);
+_assertSame(ctx.fillStyle, '#ff00ff', "ctx.fillStyle", "'#ff00ff'");
+ctx.fillRect(0, 0, 100, 50);
+_assertPixel(canvas, 50,25, 255,0,255,255, "50,25", "255,0,255,255");
+
+const color = new CSSRGB(0, CSS.percent(50), 0);
+ctx.fillStyle = color;
+_assertSame(ctx.fillStyle, '#008000', "ctx.fillStyle", "'#008000'");
+ctx.fillRect(0, 0, 100, 50);
+_assertPixel(canvas, 50,25, 0,128,0,255, "50,25", "0,128,0,255");
+color.g = 0;
+ctx.fillStyle = color;
+_assertSame(ctx.fillStyle, '#000000', "ctx.fillStyle", "'#000000'");
+ctx.fillRect(0, 0, 100, 50);
+_assertPixel(canvas, 50,25, 0,0,0,255, "50,25", "0,0,0,255");
+
+color.alpha = 0;
+ctx.fillStyle = color;
+_assertSame(ctx.fillStyle, 'rgba(0, 0, 0, 0)', "ctx.fillStyle", "'rgba(0, 0, 0, 0)'");
+ctx.reset();
+color.alpha = 0.5;
+ctx.fillStyle = color;
+ctx.fillRect(0, 0, 100, 50);
+_assertPixel(canvas, 50,25, 0,0,0,128, "50,25", "0,0,0,128");
+
+ctx.fillStyle = new CSSHSL(CSS.deg(0), 1, 1).toRGB();
+_assertSame(ctx.fillStyle, '#ffffff', "ctx.fillStyle", "'#ffffff'");
+ctx.fillRect(0, 0, 100, 50);
+_assertPixel(canvas, 50,25, 255,255,255,255, "50,25", "255,255,255,255");
+
+color.alpha = 1;
+color.g = 1;
+ctx.fillStyle = color;
+ctx.fillRect(0, 0, 100, 50);
+
+
+});
+</script>
+
diff --git a/third_party/blink/web_tests/external/wpt/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.CSSHSL.html b/third_party/blink/web_tests/external/wpt/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.CSSHSL.html
new file mode 100644
index 0000000..d89a51f
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/html/canvas/offscreen/fill-and-stroke-styles/2d.fillStyle.CSSHSL.html
@@ -0,0 +1,45 @@
+<!DOCTYPE html>
+<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
+<title>OffscreenCanvas test: 2d.fillStyle.CSSHSL</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/canvas/resources/canvas-tests.js"></script>
+
+<h1>2d.fillStyle.CSSHSL</h1>
+<p class="desc">CSSHSL works as color input</p>
+
+
+<script>
+var t = async_test("CSSHSL works as color input");
+var t_pass = t.done.bind(t);
+var t_fail = t.step_func(function(reason) {
+    throw reason;
+});
+t.step(function() {
+
+var offscreenCanvas = new OffscreenCanvas(100, 50);
+var ctx = offscreenCanvas.getContext('2d');
+
+ctx.fillStyle = new CSSHSL(CSS.deg(180), 0.5, 0.5);
+ctx.fillRect(0, 0, 100, 50);
+_assertPixelApprox(offscreenCanvas, 50,25, 64,191,191,255, "50,25", "64,191,191,255", 3);
+
+const color = new CSSHSL(CSS.deg(180), 1, 1);
+ctx.fillStyle = color;
+_assertSame(ctx.fillStyle, '#ffffff', "ctx.fillStyle", "'#ffffff'");
+ctx.fillRect(0, 0, 100, 50);
+_assertPixel(offscreenCanvas, 50,25, 255,255,255,255, "50,25", "255,255,255,255");
+color.l = 0.5;
+ctx.fillStyle = color;
+_assertSame(ctx.fillStyle, '#00ffff', "ctx.fillStyle", "'#00ffff'");
+ctx.fillRect(0, 0, 100, 50);
+_assertPixel(offscreenCanvas, 50,25, 0,255,255,255, "50,25", "0,255,255,255");
+
+ctx.fillStyle = new CSSRGB(1, 0, 1).toHSL();
+_assertSame(ctx.fillStyle, '#ff00ff', "ctx.fillStyle", "'#ff00ff'");
+ctx.fillRect(0, 0, 100, 50);
+_assertPixel(offscreenCanvas, 50,25, 255,0,255,255, "50,25", "255,0,255,255");
+t.done();
+
+});
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/html/canvas/tools/yaml/element/fill-and-stroke-styles.yaml b/third_party/blink/web_tests/external/wpt/html/canvas/tools/yaml/element/fill-and-stroke-styles.yaml
index b85d1af..7b82933 100644
--- a/third_party/blink/web_tests/external/wpt/html/canvas/tools/yaml/element/fill-and-stroke-styles.yaml
+++ b/third_party/blink/web_tests/external/wpt/html/canvas/tools/yaml/element/fill-and-stroke-styles.yaml
@@ -2138,3 +2138,75 @@
     }, 250);
   expected: green
 
+- name: 2d.fillStyle.CSSRGB
+  desc: CSSRGB works as color input
+  testing:
+  - 2d.colours.CSSRGB
+  code: |
+    ctx.fillStyle = new CSSRGB(1, 0, 1);
+    @assert ctx.fillStyle === '#ff00ff';
+    ctx.fillRect(0, 0, 100, 50);
+    @assert pixel 50,25 == 255,0,255,255;
+
+    const color = new CSSRGB(0, CSS.percent(50), 0);
+    ctx.fillStyle = color;
+    @assert ctx.fillStyle === '#008000';
+    ctx.fillRect(0, 0, 100, 50);
+    @assert pixel 50,25 == 0,128,0,255;
+    color.g = 0;
+    ctx.fillStyle = color;
+    @assert ctx.fillStyle === '#000000';
+    ctx.fillRect(0, 0, 100, 50);
+    @assert pixel 50,25 == 0,0,0,255;
+
+    color.alpha = 0;
+    ctx.fillStyle = color;
+    @assert ctx.fillStyle === 'rgba(0, 0, 0, 0)';
+    ctx.reset();
+    color.alpha = 0.5;
+    ctx.fillStyle = color;
+    ctx.fillRect(0, 0, 100, 50);
+    @assert pixel 50,25 == 0,0,0,128;
+
+    ctx.fillStyle = new CSSHSL(CSS.deg(0), 1, 1).toRGB();
+    @assert ctx.fillStyle === '#ffffff';
+    ctx.fillRect(0, 0, 100, 50);
+    @assert pixel 50,25 == 255,255,255,255;
+
+    color.alpha = 1;
+    color.g = 1;
+    ctx.fillStyle = color;
+    ctx.fillRect(0, 0, 100, 50);
+  expected: green
+
+- name: 2d.fillStyle.CSSHSL
+  desc: CSSHSL works as color input
+  testing:
+  - 2d.colours.CSSHSL
+  code: |
+    ctx.fillStyle = new CSSHSL(CSS.deg(180), 0.5, 0.5);
+    ctx.fillRect(0, 0, 100, 50);
+    @assert pixel 50,25 ==~ 64,191,191,255 +/- 3;
+
+    const color = new CSSHSL(CSS.deg(180), 1, 1);
+    ctx.fillStyle = color;
+    @assert ctx.fillStyle === '#ffffff';
+    ctx.fillRect(0, 0, 100, 50);
+    @assert pixel 50,25 == 255,255,255,255;
+    color.l = 0.5;
+    ctx.fillStyle = color;
+    @assert ctx.fillStyle === '#00ffff';
+    ctx.fillRect(0, 0, 100, 50);
+    @assert pixel 50,25 == 0,255,255,255;
+
+    ctx.fillStyle = new CSSRGB(1, 0, 1).toHSL();
+    @assert ctx.fillStyle === '#ff00ff';
+    ctx.fillRect(0, 0, 100, 50);
+    @assert pixel 50,25 == 255,0,255,255;
+
+    color.h = CSS.deg(120);
+    color.s = 1;
+    color.l = 0.5;
+    ctx.fillStyle = color;
+    ctx.fillRect(0, 0, 100, 50);
+  expected: green
diff --git a/third_party/blink/web_tests/http/tests/navigation-timing/secure_connection_start_zero_on_localhost.html b/third_party/blink/web_tests/http/tests/navigation-timing/secure_connection_start_zero_on_localhost.html
new file mode 100644
index 0000000..a718c5f
--- /dev/null
+++ b/third_party/blink/web_tests/http/tests/navigation-timing/secure_connection_start_zero_on_localhost.html
@@ -0,0 +1,19 @@
+<!DOCTYPE html>
+<html>
+  <head>
+    <meta charset="utf-8" />
+    <title>secureConnectionStart on non-https localhost</title>
+    <script src="/resources/testharness.js"></script>
+    <script src="/resources/testharnessreport.js"></script>
+  </head>
+  <body>
+    <script>
+      window.onload = () => {
+          test(t => {
+              const entry = performance.getEntriesByType("navigation")[0];
+              assert_equals(entry.secureConnectionStart, 0, "secureConnectionStart is non-zero");
+          }, "Test that secureConnectionStart is zero");
+      };
+    </script>
+  </body>
+</html>
diff --git a/third_party/blink/web_tests/webexposed/global-interface-listing-expected.txt b/third_party/blink/web_tests/webexposed/global-interface-listing-expected.txt
index c2c7584..aa599aa 100644
--- a/third_party/blink/web_tests/webexposed/global-interface-listing-expected.txt
+++ b/third_party/blink/web_tests/webexposed/global-interface-listing-expected.txt
@@ -8139,7 +8139,7 @@
     method lock
     method unlock
     setter onchange
-interface Screens
+interface Screens : EventTarget
     attribute @@toStringTag
     getter currentScreen
     getter onchange
diff --git a/third_party/blink/web_tests/webgpu/ctshtml-template.txt b/third_party/blink/web_tests/webgpu/ctshtml-template.txt
index 601075fe..2f1a3da 100644
--- a/third_party/blink/web_tests/webgpu/ctshtml-template.txt
+++ b/third_party/blink/web_tests/webgpu/ctshtml-template.txt
@@ -32,7 +32,7 @@
 <meta name=variant content='?worker=1&q=webgpu:api,operation,rendering,basic:*'>
 <meta name=variant content='?worker=1&q=webgpu:api,operation,render_pass,storeOp:*'>
 <meta name=variant content='?worker=1&q=webgpu:api,operation,render_pass,storeop2:*'>
-<meta name=variant content='?worker=1&q=webgpu:api,operation,fences:*'>
+<meta name=variant content='?worker=1&q=webgpu:api,operation,onSubmittedWorkDone:*'>
 <meta name=variant content='?worker=1&q=webgpu:api,validation,buffer,create:*'>
 <meta name=variant content='?worker=1&q=webgpu:api,validation,buffer,destroy:*'>
 <meta name=variant content='?worker=1&q=webgpu:api,validation,buffer,mapping:*'>
diff --git a/third_party/blink/web_tests/wpt_internal/webgpu/cts.html b/third_party/blink/web_tests/wpt_internal/webgpu/cts.html
index 2f98535..6238a0b 100644
--- a/third_party/blink/web_tests/wpt_internal/webgpu/cts.html
+++ b/third_party/blink/web_tests/wpt_internal/webgpu/cts.html
@@ -33,7 +33,7 @@
 <meta name=variant content='?worker=1&q=webgpu:api,operation,rendering,basic:*'>
 <meta name=variant content='?worker=1&q=webgpu:api,operation,render_pass,storeOp:*'>
 <meta name=variant content='?worker=1&q=webgpu:api,operation,render_pass,storeop2:*'>
-<meta name=variant content='?worker=1&q=webgpu:api,operation,fences:*'>
+<meta name=variant content='?worker=1&q=webgpu:api,operation,onSubmittedWorkDone:*'>
 <meta name=variant content='?worker=1&q=webgpu:api,validation,buffer,create:*'>
 <meta name=variant content='?worker=1&q=webgpu:api,validation,buffer,destroy:*'>
 <meta name=variant content='?worker=1&q=webgpu:api,validation,buffer,mapping:*'>
@@ -150,8 +150,8 @@
 <meta name=variant content='?q=webgpu:api,operation,command_buffer,image_copy:*'>
 <meta name=variant content='?q=webgpu:api,operation,compute,basic:*'>
 <meta name=variant content='?q=webgpu:api,operation,device,lost:*'>
-<meta name=variant content='?q=webgpu:api,operation,fences:*'>
 <meta name=variant content='?q=webgpu:api,operation,memory_sync,buffer,ww:*'>
+<meta name=variant content='?q=webgpu:api,operation,onSubmittedWorkDone:*'>
 <meta name=variant content='?q=webgpu:api,operation,queue,writeBuffer:*'>
 <meta name=variant content='?q=webgpu:api,operation,render_pass,resolve:*'>
 <meta name=variant content='?q=webgpu:api,operation,render_pass,storeOp:*'>
@@ -161,32 +161,31 @@
 <meta name=variant content='?q=webgpu:api,operation,rendering,basic:*'>
 <meta name=variant content='?q=webgpu:api,operation,rendering,blending:GPUBlendComponent:component="color";srcFactor="zero";*'>
 <meta name=variant content='?q=webgpu:api,operation,rendering,blending:GPUBlendComponent:component="color";srcFactor="one";*'>
-<meta name=variant content='?q=webgpu:api,operation,rendering,blending:GPUBlendComponent:component="color";srcFactor="src-color";*'>
-<meta name=variant content='?q=webgpu:api,operation,rendering,blending:GPUBlendComponent:component="color";srcFactor="one-minus-src-color";*'>
+<meta name=variant content='?q=webgpu:api,operation,rendering,blending:GPUBlendComponent:component="color";srcFactor="src";*'>
+<meta name=variant content='?q=webgpu:api,operation,rendering,blending:GPUBlendComponent:component="color";srcFactor="one-minus-src";*'>
 <meta name=variant content='?q=webgpu:api,operation,rendering,blending:GPUBlendComponent:component="color";srcFactor="src-alpha";*'>
 <meta name=variant content='?q=webgpu:api,operation,rendering,blending:GPUBlendComponent:component="color";srcFactor="one-minus-src-alpha";*'>
-<meta name=variant content='?q=webgpu:api,operation,rendering,blending:GPUBlendComponent:component="color";srcFactor="dst-color";*'>
-<meta name=variant content='?q=webgpu:api,operation,rendering,blending:GPUBlendComponent:component="color";srcFactor="one-minus-dst-color";*'>
+<meta name=variant content='?q=webgpu:api,operation,rendering,blending:GPUBlendComponent:component="color";srcFactor="dst";*'>
+<meta name=variant content='?q=webgpu:api,operation,rendering,blending:GPUBlendComponent:component="color";srcFactor="one-minus-dst";*'>
 <meta name=variant content='?q=webgpu:api,operation,rendering,blending:GPUBlendComponent:component="color";srcFactor="dst-alpha";*'>
 <meta name=variant content='?q=webgpu:api,operation,rendering,blending:GPUBlendComponent:component="color";srcFactor="one-minus-dst-alpha";*'>
 <meta name=variant content='?q=webgpu:api,operation,rendering,blending:GPUBlendComponent:component="color";srcFactor="src-alpha-saturated";*'>
-<meta name=variant content='?q=webgpu:api,operation,rendering,blending:GPUBlendComponent:component="color";srcFactor="blend-color";*'>
-<meta name=variant content='?q=webgpu:api,operation,rendering,blending:GPUBlendComponent:component="color";srcFactor="one-minus-blend-color";*'>
+<meta name=variant content='?q=webgpu:api,operation,rendering,blending:GPUBlendComponent:component="color";srcFactor="constant";*'>
+<meta name=variant content='?q=webgpu:api,operation,rendering,blending:GPUBlendComponent:component="color";srcFactor="one-minus-constant";*'>
 <meta name=variant content='?q=webgpu:api,operation,rendering,blending:GPUBlendComponent:component="alpha";srcFactor="zero";*'>
 <meta name=variant content='?q=webgpu:api,operation,rendering,blending:GPUBlendComponent:component="alpha";srcFactor="one";*'>
-<meta name=variant content='?q=webgpu:api,operation,rendering,blending:GPUBlendComponent:component="alpha";srcFactor="src-color";*'>
-<meta name=variant content='?q=webgpu:api,operation,rendering,blending:GPUBlendComponent:component="alpha";srcFactor="one-minus-src-color";*'>
+<meta name=variant content='?q=webgpu:api,operation,rendering,blending:GPUBlendComponent:component="alpha";srcFactor="src";*'>
+<meta name=variant content='?q=webgpu:api,operation,rendering,blending:GPUBlendComponent:component="alpha";srcFactor="one-minus-src";*'>
 <meta name=variant content='?q=webgpu:api,operation,rendering,blending:GPUBlendComponent:component="alpha";srcFactor="src-alpha";*'>
 <meta name=variant content='?q=webgpu:api,operation,rendering,blending:GPUBlendComponent:component="alpha";srcFactor="one-minus-src-alpha";*'>
-<meta name=variant content='?q=webgpu:api,operation,rendering,blending:GPUBlendComponent:component="alpha";srcFactor="dst-color";*'>
-<meta name=variant content='?q=webgpu:api,operation,rendering,blending:GPUBlendComponent:component="alpha";srcFactor="one-minus-dst-color";*'>
+<meta name=variant content='?q=webgpu:api,operation,rendering,blending:GPUBlendComponent:component="alpha";srcFactor="dst";*'>
+<meta name=variant content='?q=webgpu:api,operation,rendering,blending:GPUBlendComponent:component="alpha";srcFactor="one-minus-dst";*'>
 <meta name=variant content='?q=webgpu:api,operation,rendering,blending:GPUBlendComponent:component="alpha";srcFactor="dst-alpha";*'>
 <meta name=variant content='?q=webgpu:api,operation,rendering,blending:GPUBlendComponent:component="alpha";srcFactor="one-minus-dst-alpha";*'>
 <meta name=variant content='?q=webgpu:api,operation,rendering,blending:GPUBlendComponent:component="alpha";srcFactor="src-alpha-saturated";*'>
-<meta name=variant content='?q=webgpu:api,operation,rendering,blending:GPUBlendComponent:component="alpha";srcFactor="blend-color";*'>
-<meta name=variant content='?q=webgpu:api,operation,rendering,blending:GPUBlendComponent:component="alpha";srcFactor="one-minus-blend-color";*'>
+<meta name=variant content='?q=webgpu:api,operation,rendering,blending:GPUBlendComponent:component="alpha";srcFactor="constant";*'>
+<meta name=variant content='?q=webgpu:api,operation,rendering,blending:GPUBlendComponent:component="alpha";srcFactor="one-minus-constant";*'>
 <meta name=variant content='?q=webgpu:api,operation,rendering,blending:formats,*'>
-<meta name=variant content='?q=webgpu:api,operation,rendering,blending:multiple_color_attachments,*'>
 <meta name=variant content='?q=webgpu:api,operation,rendering,blending:clamp,*'>
 <meta name=variant content='?q=webgpu:api,operation,rendering,depth:*'>
 <meta name=variant content='?q=webgpu:api,operation,rendering,draw:arguments:first=0;count=0;*'>
@@ -335,43 +334,9 @@
 <meta name=variant content='?q=webgpu:api,validation,buffer,destroy:*'>
 <meta name=variant content='?q=webgpu:api,validation,buffer,mapping:*'>
 <meta name=variant content='?q=webgpu:api,validation,capability_checks,features,query_types:createQuerySet:*'>
-<meta name=variant content='?q=webgpu:api,validation,createBindGroup:binding_count_mismatch,*'>
-<meta name=variant content='?q=webgpu:api,validation,createBindGroup:binding_must_be_present_in_layout,*'>
-<meta name=variant content='?q=webgpu:api,validation,createBindGroup:buffer_binding_must_contain_exactly_one_buffer_of_its_type:bindingType="uniform-buffer";*'>
-<meta name=variant content='?q=webgpu:api,validation,createBindGroup:buffer_binding_must_contain_exactly_one_buffer_of_its_type:bindingType="storage-buffer";*'>
-<meta name=variant content='?q=webgpu:api,validation,createBindGroup:buffer_binding_must_contain_exactly_one_buffer_of_its_type:bindingType="readonly-storage-buffer";*'>
-<meta name=variant content='?q=webgpu:api,validation,createBindGroup:buffer_binding_must_contain_exactly_one_buffer_of_its_type:bindingType="sampler";*'>
-<meta name=variant content='?q=webgpu:api,validation,createBindGroup:buffer_binding_must_contain_exactly_one_buffer_of_its_type:bindingType="comparison-sampler";*'>
-<meta name=variant content='?q=webgpu:api,validation,createBindGroup:buffer_binding_must_contain_exactly_one_buffer_of_its_type:bindingType="sampled-texture";*'>
-<meta name=variant content='?q=webgpu:api,validation,createBindGroup:buffer_binding_must_contain_exactly_one_buffer_of_its_type:bindingType="multisampled-texture";*'>
-<meta name=variant content='?q=webgpu:api,validation,createBindGroup:buffer_binding_must_contain_exactly_one_buffer_of_its_type:bindingType="writeonly-storage-texture";resourceType="uniformBuf";*'>
-<meta name=variant content='?q=webgpu:api,validation,createBindGroup:buffer_binding_must_contain_exactly_one_buffer_of_its_type:bindingType="writeonly-storage-texture";resourceType="storageBuf";*'>
-<meta name=variant content='?q=webgpu:api,validation,createBindGroup:buffer_binding_must_contain_exactly_one_buffer_of_its_type:bindingType="writeonly-storage-texture";resourceType="plainSamp";*'>
-<meta name=variant content='?q=webgpu:api,validation,createBindGroup:buffer_binding_must_contain_exactly_one_buffer_of_its_type:bindingType="writeonly-storage-texture";resourceType="compareSamp";*'>
-<meta name=variant content='?q=webgpu:api,validation,createBindGroup:buffer_binding_must_contain_exactly_one_buffer_of_its_type:bindingType="writeonly-storage-texture";resourceType="sampledTex";*'>
-<meta name=variant content='?q=webgpu:api,validation,createBindGroup:buffer_binding_must_contain_exactly_one_buffer_of_its_type:bindingType="writeonly-storage-texture";resourceType="sampledTexMS"'>
-<meta name=variant content='?q=webgpu:api,validation,createBindGroup:buffer_binding_must_contain_exactly_one_buffer_of_its_type:bindingType="writeonly-storage-texture";resourceType="storageTex";*'>
-<meta name=variant content='?q=webgpu:api,validation,createBindGroup:buffer_binding_must_contain_exactly_one_buffer_of_its_type:bindingType="writeonly-storage-texture";resourceType="errorBuf";*'>
-<meta name=variant content='?q=webgpu:api,validation,createBindGroup:buffer_binding_must_contain_exactly_one_buffer_of_its_type:bindingType="writeonly-storage-texture";resourceType="errorSamp";*'>
-<meta name=variant content='?q=webgpu:api,validation,createBindGroup:buffer_binding_must_contain_exactly_one_buffer_of_its_type:bindingType="writeonly-storage-texture";resourceType="errorTex";*'>
-<meta name=variant content='?q=webgpu:api,validation,createBindGroup:buffer_binding_must_contain_exactly_one_buffer_of_its_type:bindingType="readonly-storage-texture";resourceType="uniformBuf";*'>
-<meta name=variant content='?q=webgpu:api,validation,createBindGroup:buffer_binding_must_contain_exactly_one_buffer_of_its_type:bindingType="readonly-storage-texture";resourceType="storageBuf";*'>
-<meta name=variant content='?q=webgpu:api,validation,createBindGroup:buffer_binding_must_contain_exactly_one_buffer_of_its_type:bindingType="readonly-storage-texture";resourceType="plainSamp";*'>
-<meta name=variant content='?q=webgpu:api,validation,createBindGroup:buffer_binding_must_contain_exactly_one_buffer_of_its_type:bindingType="readonly-storage-texture";resourceType="compareSamp";*'>
-<meta name=variant content='?q=webgpu:api,validation,createBindGroup:buffer_binding_must_contain_exactly_one_buffer_of_its_type:bindingType="readonly-storage-texture";resourceType="sampledTex";*'>
-<meta name=variant content='?q=webgpu:api,validation,createBindGroup:buffer_binding_must_contain_exactly_one_buffer_of_its_type:bindingType="readonly-storage-texture";resourceType="sampledTexMS"'>
-<meta name=variant content='?q=webgpu:api,validation,createBindGroup:buffer_binding_must_contain_exactly_one_buffer_of_its_type:bindingType="readonly-storage-texture";resourceType="storageTex";*'>
-<meta name=variant content='?q=webgpu:api,validation,createBindGroup:buffer_binding_must_contain_exactly_one_buffer_of_its_type:bindingType="readonly-storage-texture";resourceType="errorBuf";*'>
-<meta name=variant content='?q=webgpu:api,validation,createBindGroup:buffer_binding_must_contain_exactly_one_buffer_of_its_type:bindingType="readonly-storage-texture";resourceType="errorSamp";*'>
-<meta name=variant content='?q=webgpu:api,validation,createBindGroup:buffer_binding_must_contain_exactly_one_buffer_of_its_type:bindingType="readonly-storage-texture";resourceType="errorTex";*'>
-<meta name=variant content='?q=webgpu:api,validation,createBindGroup:texture_binding_must_have_correct_usage,*'>
-<meta name=variant content='?q=webgpu:api,validation,createBindGroup:texture_must_have_correct_component_type,*'>
-<meta name=variant content='?q=webgpu:api,validation,createBindGroup:texture_must_have_correct_dimension,*'>
-<meta name=variant content='?q=webgpu:api,validation,createBindGroup:buffer_offset_and_size_for_bind_groups_match,*'>
-<meta name=variant content='?q=webgpu:api,validation,createBindGroup:minBindingSize,*'>
+<meta name=variant content='?q=webgpu:api,validation,createBindGroup:*'>
 <meta name=variant content='?q=webgpu:api,validation,createBindGroupLayout:some_binding_index_was_specified_more_than_once,*'>
 <meta name=variant content='?q=webgpu:api,validation,createBindGroupLayout:visibility,*'>
-<meta name=variant content='?q=webgpu:api,validation,createBindGroupLayout:bindingTypeSpecific_optional_members,*'>
 <meta name=variant content='?q=webgpu:api,validation,createBindGroupLayout:multisample_requires_2d_view_dimension,*'>
 <meta name=variant content='?q=webgpu:api,validation,createBindGroupLayout:number_of_dynamic_buffers_exceeds_the_maximum_value,*'>
 <meta name=variant content='?q=webgpu:api,validation,createBindGroupLayout:max_resources_per_stage,in_bind_group_layout,*'>
@@ -445,7 +410,6 @@
 <meta name=variant content='?q=webgpu:api,validation,encoding,queries,general:*'>
 <meta name=variant content='?q=webgpu:api,validation,encoding,queries,resolveQuerySet:*'>
 <meta name=variant content='?q=webgpu:api,validation,error_scope:*'>
-<meta name=variant content='?q=webgpu:api,validation,fences:*'>
 <meta name=variant content='?q=webgpu:api,validation,image_copy,layout_related:*'>
 <meta name=variant content='?q=webgpu:api,validation,image_copy,texture_related:*'>
 <meta name=variant content='?q=webgpu:api,validation,initialization,requestDevice:*'>
diff --git a/third_party/freetype/README.chromium b/third_party/freetype/README.chromium
index bfb5c42..1ca742b9 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-218-g270ff52f6
-Revision: 270ff52f62ecf6b38744fa5f6b95808dd7dc49d9
+Version: VER-2-10-4-226-gf631542da
+Revision: f631542dae1aaf9101135ed660cd3ff1d08ed93c
 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/ots/OWNERS b/third_party/ots/OWNERS
index 9f83263..8394af1 100644
--- a/third_party/ots/OWNERS
+++ b/third_party/ots/OWNERS
@@ -1,5 +1,5 @@
 bashi@chromium.org
 bungeman@chromium.org
-drott@chromium.rg
+drott@chromium.org
 jshin@chromium.org
 kojii@chromium.org
diff --git a/third_party/webgpu-cts/scripts/compile_src.py b/third_party/webgpu-cts/scripts/compile_src.py
index a9098db..80e26860 100755
--- a/third_party/webgpu-cts/scripts/compile_src.py
+++ b/third_party/webgpu-cts/scripts/compile_src.py
@@ -3,8 +3,9 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
-import sys
 import os
+import shutil
+import sys
 
 from tsc_ignore_errors import run_tsc_ignore_errors
 
@@ -22,6 +23,9 @@
 
 
 def compile_src(out_dir):
+    # First, clean the output directory so deleted files are pruned from old builds.
+    shutil.rmtree(out_dir)
+
     run_tsc_ignore_errors([
         '--project',
         os.path.join(webgpu_cts_dir, 'src', 'tsconfig.json'),
@@ -41,6 +45,9 @@
 
 
 def compile_src_for_node(out_dir):
+    # First, clean the output directory so deleted files are pruned from old builds.
+    shutil.rmtree(out_dir)
+
     run_tsc_ignore_errors([
         '--project',
         os.path.join(webgpu_cts_dir, 'src', 'node.tsconfig.json'),
diff --git a/third_party/webgpu-cts/ts_sources.txt b/third_party/webgpu-cts/ts_sources.txt
index 10ef309..983e4132 100644
--- a/third_party/webgpu-cts/ts_sources.txt
+++ b/third_party/webgpu-cts/ts_sources.txt
@@ -71,8 +71,8 @@
 src/webgpu/util/texture/texel_data.ts
 src/webgpu/gpu_test.ts
 src/webgpu/examples.spec.ts
-src/webgpu/api/operation/fences.spec.ts
 src/webgpu/api/operation/labels.spec.ts
+src/webgpu/api/operation/onSubmittedWorkDone.spec.ts
 src/webgpu/api/operation/uncapturederror.spec.ts
 src/webgpu/api/operation/buffers/mapping_test.ts
 src/webgpu/api/operation/buffers/map.spec.ts
@@ -134,7 +134,6 @@
 src/webgpu/api/validation/createView.spec.ts
 src/webgpu/api/validation/create_pipeline.spec.ts
 src/webgpu/api/validation/error_scope.spec.ts
-src/webgpu/api/validation/fences.spec.ts
 src/webgpu/api/validation/layout_shader_compat.spec.ts
 src/webgpu/api/validation/render_pass_descriptor.spec.ts
 src/webgpu/api/validation/vertex_access.spec.ts
@@ -161,6 +160,7 @@
 src/webgpu/api/validation/encoding/cmds/render/other.spec.ts
 src/webgpu/api/validation/encoding/cmds/render/state_tracking.spec.ts
 src/webgpu/api/validation/encoding/programmable/pipeline_bind_group_compat.spec.ts
+src/webgpu/api/validation/encoding/queries/common.ts
 src/webgpu/api/validation/encoding/queries/begin_end.spec.ts
 src/webgpu/api/validation/encoding/queries/general.spec.ts
 src/webgpu/api/validation/encoding/queries/pipeline_statistics.spec.ts
diff --git a/tools/gritsettings/resource_ids.spec b/tools/gritsettings/resource_ids.spec
index 8584fa36..bb61a96 100644
--- a/tools/gritsettings/resource_ids.spec
+++ b/tools/gritsettings/resource_ids.spec
@@ -438,6 +438,10 @@
     "META": {"sizes": {"includes": [50],}},
     "includes": [2690],
   },
+  "<(SHARED_INTERMEDIATE_DIR)/chromeos/components/demo_mode_app_ui/chromeos_demo_mode_app_resources.grd": {
+    "META": {"sizes": {"includes": [50],}},
+   "includes": [2695],
+  },
   # END chromeos/ section.
 
   # START components/ section.
diff --git a/tools/mb/mb_config.pyl b/tools/mb/mb_config.pyl
index 9b17f1c..5c10cf8 100644
--- a/tools/mb/mb_config.pyl
+++ b/tools/mb/mb_config.pyl
@@ -1034,8 +1034,6 @@
       'linux_chromium_asan_rel_ng': 'asan_lsan_release_trybot',
       # TODO(crbug.com/1200574): Remove after migration.
       'linux_chromium_asan_rel_ng_bionic': 'asan_lsan_release_trybot',
-      # TODO(crbug.com/1200904): Remove after migration
-      'linux_chromium_tsan_rel_ng_bionic': 'asan_lsan_release_trybot',
       'linux_chromium_cfi_rel_ng': 'cfi_full_cfi_icall_cfi_diag_thin_lto_release_static_dcheck_always_on_goma',
       'linux_chromium_chromeos_asan_rel_ng': 'asan_lsan_chromeos_release_trybot',
       'linux_chromium_chromeos_msan_rel_ng': 'chromeos_msan_release_bot',
@@ -1052,6 +1050,8 @@
       'linux_chromium_msan_rel_ng': 'msan_release_bot',
 
       'linux_chromium_tsan_rel_ng': 'tsan_disable_nacl_release_trybot',
+      # TODO(crbug.com/1200904): Remove after migration
+      'linux_chromium_tsan_rel_ng_bionic': 'tsan_disable_nacl_release_trybot',
 
       # This is intentionally a release_bot and not a release_trybot to match
       # the CI configuration, where no debug builder exists.
diff --git a/tools/mb/mb_config_expectations/tryserver.chromium.linux.json b/tools/mb/mb_config_expectations/tryserver.chromium.linux.json
index 249205c3..0618138 100644
--- a/tools/mb/mb_config_expectations/tryserver.chromium.linux.json
+++ b/tools/mb/mb_config_expectations/tryserver.chromium.linux.json
@@ -903,10 +903,10 @@
   "linux_chromium_tsan_rel_ng_bionic": {
     "gn_args": {
       "dcheck_always_on": true,
-      "is_asan": true,
+      "enable_nacl": false,
       "is_component_build": false,
       "is_debug": false,
-      "is_lsan": true,
+      "is_tsan": true,
       "symbol_level": 1,
       "use_goma": true
     }
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml
index 5db18c3..d8737e4e 100644
--- a/tools/metrics/histograms/enums.xml
+++ b/tools/metrics/histograms/enums.xml
@@ -17797,6 +17797,14 @@
   <int value="28" label="Google Docs (Chrome app)"/>
   <int value="29" label="Google Sheets (Chrome app)"/>
   <int value="30" label="Google Slides (Chrome app)"/>
+  <int value="31" label="YouTube (PWA)"/>
+  <int value="32" label="Google Docs (PWA)"/>
+  <int value="33" label="Google Meet (PWA)"/>
+  <int value="34" label="Google Sheets (PWA)"/>
+  <int value="35" label="Spotify (PWA)"/>
+  <int value="36" label="BeFunky (PWA)"/>
+  <int value="37" label="Clipchamp (PWA)"/>
+  <int value="38" label="GeForce NOW (PWA)"/>
 </enum>
 
 <enum name="DemoModeAppLaunchSource">
@@ -34608,6 +34616,9 @@
 </enum>
 
 <enum name="GaiaTokenRevocationStatus">
+  <obsolete>
+    Deprecated in M92.
+  </obsolete>
   <int value="0" label="Success"/>
   <int value="1" label="Connection Canceled"/>
   <int value="2" label="Connection Failed"/>
@@ -72071,6 +72082,9 @@
 </enum>
 
 <enum name="SigninRefreshTokenRevocationRequestProgress">
+  <obsolete>
+    Deprecated in M92.
+  </obsolete>
   <int value="0" label="Request Created"/>
   <int value="1" label="Request Started"/>
   <int value="2" label="Request Failed"/>
diff --git a/tools/metrics/histograms/histograms_xml/histogram_suffixes_list.xml b/tools/metrics/histograms/histograms_xml/histogram_suffixes_list.xml
index 8fa633fe..d5d123bcf 100644
--- a/tools/metrics/histograms/histograms_xml/histogram_suffixes_list.xml
+++ b/tools/metrics/histograms/histograms_xml/histogram_suffixes_list.xml
@@ -20030,6 +20030,7 @@
   <suffix name="Camera" label="Camera"/>
   <suffix name="ConnectivityDiagnostics" label="Connectivity Diagnostics"/>
   <suffix name="Crosh" label="Crosh"/>
+  <suffix name="DemoMode" label="DemoMode"/>
   <suffix name="Diagnostics" label="Diagnostics"/>
   <suffix name="Discover" label="Discovery">
     <obsolete>
diff --git a/tools/metrics/histograms/histograms_xml/others/histograms.xml b/tools/metrics/histograms/histograms_xml/others/histograms.xml
index 4f2864e2..1bb0a916 100644
--- a/tools/metrics/histograms/histograms_xml/others/histograms.xml
+++ b/tools/metrics/histograms/histograms_xml/others/histograms.xml
@@ -17523,15 +17523,21 @@
   </summary>
 </histogram>
 
-<histogram name="VoiceInteraction.AudioPermissionEvent"
+<histogram name="VoiceInteraction.AudioPermissionEvent{Timing}"
     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 each
-    time non-Assistant voice recognition is triggered with a mic button click.
+    system level transcription (as opposite to Assistant). Will be logged
+    {Timing}.
   </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/page/histograms.xml b/tools/metrics/histograms/histograms_xml/page/histograms.xml
index 9819c6c..9865b52 100644
--- a/tools/metrics/histograms/histograms_xml/page/histograms.xml
+++ b/tools/metrics/histograms/histograms_xml/page/histograms.xml
@@ -852,7 +852,8 @@
   </summary>
 </histogram>
 
-<histogram base="true" name="PageLoad.Cpu" units="units" expires_after="M92">
+<histogram base="true" name="PageLoad.Cpu" units="units"
+    expires_after="2022-04-26">
   <owner>johnidel@chromium.org</owner>
   <owner>chrome-ads-histograms@google.com</owner>
   <summary>
@@ -1558,7 +1559,7 @@
 </histogram>
 
 <histogram name="PageLoad.FrameCounts.AdFrames.PerFrame.OriginStatus"
-    enum="CrossOriginAdStatus" expires_after="M92">
+    enum="CrossOriginAdStatus" expires_after="2022-04-26">
   <owner>johnidel@chromium.org</owner>
   <owner>chrome-ads-histograms@google.com</owner>
   <summary>
diff --git a/tools/metrics/histograms/histograms_xml/signin/histograms.xml b/tools/metrics/histograms/histograms_xml/signin/histograms.xml
index e7236e3..ed7ab6ad 100644
--- a/tools/metrics/histograms/histograms_xml/signin/histograms.xml
+++ b/tools/metrics/histograms/histograms_xml/signin/histograms.xml
@@ -924,6 +924,9 @@
 
 <histogram name="Signin.RefreshTokenRevocationCompleted"
     enum="GaiaTokenRevocationStatus" expires_after="2021-03-31">
+  <obsolete>
+    Removed in M92.
+  </obsolete>
   <owner>droger@chromium.org</owner>
   <owner>msarda@chromium.org</owner>
   <summary>
@@ -937,6 +940,9 @@
 <histogram name="Signin.RefreshTokenRevocationRequestProgress"
     enum="SigninRefreshTokenRevocationRequestProgress"
     expires_after="2021-03-31">
+  <obsolete>
+    Removed in M92.
+  </obsolete>
   <owner>droger@chromium.org</owner>
   <summary>
     Tracks the progress of revocation requests for refresh tokens, and can be
@@ -947,6 +953,9 @@
 
 <histogram name="Signin.RefreshTokenRevocationStatus"
     enum="GaiaTokenRevocationStatus" expires_after="2021-03-31">
+  <obsolete>
+    Removed in M92.
+  </obsolete>
   <owner>droger@chromium.org</owner>
   <owner>msarda@chromium.org</owner>
   <summary>
diff --git a/tools/perf/benchmarks/media.py b/tools/perf/benchmarks/media.py
index 1ad7f23..593bcfdb 100644
--- a/tools/perf/benchmarks/media.py
+++ b/tools/perf/benchmarks/media.py
@@ -75,8 +75,13 @@
   # TODO(rmhasan): Remove the SUPPORTED_PLATFORMS lists.
   # SUPPORTED_PLATFORMS is deprecated, please put system specifier tags
   # from expectations.config in SUPPORTED_PLATFORM_TAGS.
-  SUPPORTED_PLATFORM_TAGS = [platforms.ANDROID_NOT_WEBVIEW]
-  SUPPORTED_PLATFORMS = [story.expectations.ANDROID_NOT_WEBVIEW]
+  SUPPORTED_PLATFORM_TAGS = [
+      platforms.ANDROID_NOT_WEBVIEW, platforms.FUCHSIA_ASTRO
+  ]
+  SUPPORTED_PLATFORMS = [
+      story.expectations.ANDROID_NOT_WEBVIEW,
+      story.expectations.FUCHSIA_WEB_ENGINE_SHELL
+  ]
 
   def CreateStorySet(self, options):
     return page_sets.MediaCasesMobileStorySet()
diff --git a/tools/perf/benchmarks/rendering.py b/tools/perf/benchmarks/rendering.py
index 7be7e06..ff293f4 100644
--- a/tools/perf/benchmarks/rendering.py
+++ b/tools/perf/benchmarks/rendering.py
@@ -33,6 +33,11 @@
 
 
 class _RenderingBenchmark(perf_benchmark.PerfBenchmark):
+  options = {
+      'capture_screen_video': True,
+      'periodic_screenshot_frequency_ms': 1000
+  }
+
   @classmethod
   def AddBenchmarkCommandLineArgs(cls, parser):
     parser.add_option('--scroll-forever', action='store_true',
diff --git a/tools/perf/core/bot_platforms.py b/tools/perf/core/bot_platforms.py
index 81c2257..fb23fee 100644
--- a/tools/perf/core/bot_platforms.py
+++ b/tools/perf/core/bot_platforms.py
@@ -471,8 +471,10 @@
     _GetBenchmarkConfig('rendering.desktop'),
     _GetBenchmarkConfig('system_health.common_desktop')
 ])
-_FUCHSIA_PERF_FYI_BENCHMARK_CONFIGS = PerfSuite(
-    [_GetBenchmarkConfig('system_health.memory_desktop')])
+_FUCHSIA_PERF_FYI_BENCHMARK_CONFIGS = PerfSuite([
+    _GetBenchmarkConfig('system_health.memory_desktop'),
+    _GetBenchmarkConfig('media.mobile')
+])
 _LINUX_PERF_CALIBRATION_BENCHMARK_CONFIGS = PerfSuite([
     _GetBenchmarkConfig('speedometer2'),
     _GetBenchmarkConfig('blink_perf.shadow_dom'),
diff --git a/tools/perf/core/perfetto_binary_roller/binary_deps.json b/tools/perf/core/perfetto_binary_roller/binary_deps.json
index 2698e616..b459e1b 100644
--- a/tools/perf/core/perfetto_binary_roller/binary_deps.json
+++ b/tools/perf/core/perfetto_binary_roller/binary_deps.json
@@ -10,7 +10,7 @@
         },
         "linux": {
             "hash": "38a7401903f838271fc4cb6cfff67c90f25c2176",
-            "remote_path": "perfetto_binaries/trace_processor_shell/linux/05329d51861d086cbbfdcf6c298b91167393e3db/trace_processor_shell"
+            "remote_path": "perfetto_binaries/trace_processor_shell/linux/14c4d4957d5e2cb7c6fd4d6b8a3f51318c1b22da/trace_processor_shell"
         }
     },
     "power_profile.sql": {
diff --git a/tools/perf/core/platforms.py b/tools/perf/core/platforms.py
index 2235b31..cbab568 100644
--- a/tools/perf/core/platforms.py
+++ b/tools/perf/core/platforms.py
@@ -10,5 +10,7 @@
 ANDROID_NOT_WEBVIEW = 'android-not-webview'
 ANDROID_WEBVIEW = 'android-webview'
 ANDROID = 'android'
+FUCHSIA_ASTRO = 'fuchsia-board-astro'
+FUCHSIA_QEMU_X64 = 'fuchsia-board-qemu-x64'
 WIN10 = 'win10'
 CHROMEOS = 'chromeos'
diff --git a/tools/perf/core/results_processor/processor.py b/tools/perf/core/results_processor/processor.py
index 4db8f43..4a26edd 100644
--- a/tools/perf/core/results_processor/processor.py
+++ b/tools/perf/core/results_processor/processor.py
@@ -99,6 +99,7 @@
   if should_compute_metrics:
     histogram_dicts = ExtractHistograms(test_results)
 
+  util.TryUploadingResultToResultSink(test_results)
   for output_format in options.output_formats:
     logging.info('Processing format: %s', output_format)
     formatter = formatters.FORMATTERS[output_format]
@@ -106,6 +107,7 @@
       output_file = formatter.ProcessHistogramDicts(histogram_dicts, options)
     else:
       output_file = formatter.ProcessIntermediateResults(test_results, options)
+
     print('View results at file://', output_file, sep='')
 
   return GenerateExitCode(test_results)
diff --git a/tools/perf/core/results_processor/util.py b/tools/perf/core/results_processor/util.py
index f670e5f..13c2c29e 100644
--- a/tools/perf/core/results_processor/util.py
+++ b/tools/perf/core/results_processor/util.py
@@ -4,7 +4,11 @@
 
 import calendar
 import datetime
+import json
 import logging
+import os
+
+import requests  # pylint: disable=import-error
 
 import multiprocessing
 from multiprocessing.dummy import Pool as ThreadPool
@@ -97,3 +101,58 @@
   test_result['status'] = 'FAIL'
   test_result['expected'] = False
   logging.error('Processing failed for test %s', test_result['testPath'])
+
+
+def TryUploadingResultToResultSink(results):
+  def buildSummaryHtml(artifacts):
+    summaryHtml = ''
+    for artifact_id, _ in artifacts.items():
+      summaryHtml += '<p><text-artifact artifact-id=' + artifact_id + '></p>'
+    return summaryHtml
+
+  def buildArtifacts(artifacts):
+    artifacts_result = {}
+    for artifact_id, artifact in artifacts.items():
+      artifacts_result[artifact_id] = {'filePath': artifact['filePath']}
+    return artifacts_result
+
+  def parse(results):
+    test_results = []
+    for test_case in results:
+      test_result = {
+          'testId': test_case['testPath'],
+          'expected': test_case['expected'],
+          'status': test_case['status']
+      }
+      # TODO: go/result-sink#test-result-json-object listed that specifying
+      # testMetadata with location info can helped with breaking down flaky
+      # tests. We don't have the file location currently in test results.
+      if 'runDuration' in test_case:
+        test_result['duration'] = '%.9fs' % float(
+            test_case['runDuration'].rstrip('s'))
+      if 'tags' in test_case:
+        test_result['tags'] = test_case['tags']
+      if 'outputArtifacts' in test_case:
+        test_result['summaryHtml'] = buildSummaryHtml(
+            test_case['outputArtifacts'])
+        test_result['artifacts'] = buildArtifacts(test_case['outputArtifacts'])
+      test_results.append(test_result)
+    return test_results
+
+  try:
+    with open(os.environ['LUCI_CONTEXT']) as f:
+      sink = json.load(f)['result_sink']
+  except KeyError:
+    return
+
+  test_results = parse(results)
+  res = requests.post(
+      url='http://%s/prpc/luci.resultsink.v1.Sink/ReportTestResults' %
+      sink['address'],
+      headers={
+          'Content-Type': 'application/json',
+          'Accept': 'application/json',
+          'Authorization': 'ResultSink %s' % sink['auth_token'],
+      },
+      data=json.dumps({'testResults': test_results}))
+  res.raise_for_status()
diff --git a/tools/perf/core/shard_maps/fuchsia-perf-fyi_map.json b/tools/perf/core/shard_maps/fuchsia-perf-fyi_map.json
index e3711288..1802f5b6 100644
--- a/tools/perf/core/shard_maps/fuchsia-perf-fyi_map.json
+++ b/tools/perf/core/shard_maps/fuchsia-perf-fyi_map.json
@@ -1,8 +1,11 @@
 {
     "0": {
         "benchmarks": {
+            "media.mobile": {
+                "abridged": false
+            },
             "system_health.memory_desktop": {
-                "end": 16,
+                "end": 11,
                 "abridged": false
             }
         }
@@ -10,8 +13,8 @@
     "1": {
         "benchmarks": {
             "system_health.memory_desktop": {
-                "begin": 16,
-                "end": 29,
+                "begin": 11,
+                "end": 26,
                 "abridged": false
             }
         }
@@ -19,8 +22,8 @@
     "2": {
         "benchmarks": {
             "system_health.memory_desktop": {
-                "begin": 29,
-                "end": 41,
+                "begin": 26,
+                "end": 34,
                 "abridged": false
             }
         }
@@ -28,8 +31,8 @@
     "3": {
         "benchmarks": {
             "system_health.memory_desktop": {
-                "begin": 41,
-                "end": 51,
+                "begin": 34,
+                "end": 46,
                 "abridged": false
             }
         }
@@ -37,8 +40,8 @@
     "4": {
         "benchmarks": {
             "system_health.memory_desktop": {
-                "begin": 51,
-                "end": 59,
+                "begin": 46,
+                "end": 53,
                 "abridged": false
             }
         }
@@ -46,7 +49,7 @@
     "5": {
         "benchmarks": {
             "system_health.memory_desktop": {
-                "begin": 59,
+                "begin": 53,
                 "end": 74,
                 "abridged": false
             }
@@ -61,17 +64,17 @@
         }
     },
     "extra_infos": {
-        "num_stories": 80,
-        "predicted_min_shard_time": 957.0,
+        "num_stories": 96,
+        "predicted_min_shard_time": 872.0,
         "predicted_min_shard_index": 5,
         "predicted_max_shard_time": 1146.0,
-        "predicted_max_shard_index": 2,
-        "shard #0": 1077.0,
-        "shard #1": 1119.0,
-        "shard #2": 1146.0,
-        "shard #3": 1128.0,
-        "shard #4": 1026.0,
-        "shard #5": 957.0,
-        "shard #6": 1140.0
+        "predicted_max_shard_index": 6,
+        "shard #0": 1027.0,
+        "shard #1": 972.0,
+        "shard #2": 981.0,
+        "shard #3": 1062.0,
+        "shard #4": 1038.0,
+        "shard #5": 872.0,
+        "shard #6": 1146.0
     }
 }
\ No newline at end of file
diff --git a/tools/perf/core/shard_maps/timing_data/fuchsia-perf-fyi_timing.json b/tools/perf/core/shard_maps/timing_data/fuchsia-perf-fyi_timing.json
index 7c0f3cd..42857f5 100644
--- a/tools/perf/core/shard_maps/timing_data/fuchsia-perf-fyi_timing.json
+++ b/tools/perf/core/shard_maps/timing_data/fuchsia-perf-fyi_timing.json
@@ -1,22 +1,18 @@
 [
     {
-        "duration": "1.0",
+        "duration": "2.0",
         "name": "system_health.memory_desktop/browse:media:googleplaystore:2021"
     },
     {
-        "duration": "1.0",
-        "name": "system_health.memory_desktop/browse:media:imgur"
-    },
-    {
         "duration": "2.0",
-        "name": "system_health.memory_desktop/browse:media:pinterest:2018"
+        "name": "system_health.memory_desktop/browse:media:imgur"
     },
     {
         "duration": "2.0",
         "name": "system_health.memory_desktop/browse:media:tumblr:2018"
     },
     {
-        "duration": "110.0",
+        "duration": "111.0",
         "name": "system_health.memory_desktop/browse:media:youtube:2019"
     },
     {
@@ -32,27 +28,19 @@
         "name": "system_health.memory_desktop/browse:news:cnn:2021"
     },
     {
-        "duration": "97.0",
+        "duration": "98.0",
         "name": "system_health.memory_desktop/browse:news:flipboard:2020"
     },
     {
-        "duration": "86.0",
+        "duration": "117.0",
         "name": "system_health.memory_desktop/browse:news:hackernews:2020"
     },
     {
-        "duration": "2.0",
-        "name": "system_health.memory_desktop/browse:news:nytimes:2020"
-    },
-    {
-        "duration": "2.0",
-        "name": "system_health.memory_desktop/browse:news:reddit:2020"
-    },
-    {
         "duration": "67.0",
         "name": "system_health.memory_desktop/browse:search:google:2020"
     },
     {
-        "duration": "1.0",
+        "duration": "2.0",
         "name": "system_health.memory_desktop/browse:search:google_india:2021"
     },
     {
@@ -68,7 +56,7 @@
         "name": "system_health.memory_desktop/browse:social:twitter:2018"
     },
     {
-        "duration": "123.0",
+        "duration": "124.0",
         "name": "system_health.memory_desktop/browse:social:twitter_infinite_scroll:2018"
     },
     {
@@ -104,11 +92,11 @@
         "name": "system_health.memory_desktop/browse:tools:gmail-search:2020"
     },
     {
-        "duration": "1.0",
+        "duration": "2.0",
         "name": "system_health.memory_desktop/browse:tools:maps:2019"
     },
     {
-        "duration": "1.0",
+        "duration": "2.0",
         "name": "system_health.memory_desktop/browse:tools:sheets:2019"
     },
     {
@@ -124,7 +112,7 @@
         "name": "system_health.memory_desktop/load:chrome:blank"
     },
     {
-        "duration": "39.0",
+        "duration": "38.0",
         "name": "system_health.memory_desktop/load:games:alphabetty:2018"
     },
     {
@@ -144,11 +132,11 @@
         "name": "system_health.memory_desktop/load:games:spychase:2018"
     },
     {
-        "duration": "35.0",
+        "duration": "36.0",
         "name": "system_health.memory_desktop/load:media:9gag"
     },
     {
-        "duration": "58.0",
+        "duration": "66.0",
         "name": "system_health.memory_desktop/load:media:dailymotion:2019"
     },
     {
@@ -172,7 +160,7 @@
         "name": "system_health.memory_desktop/load:media:google_images:2018"
     },
     {
-        "duration": "45.0",
+        "duration": "46.0",
         "name": "system_health.memory_desktop/load:media:imgur:2018"
     },
     {
@@ -180,14 +168,6 @@
         "name": "system_health.memory_desktop/load:media:soundcloud:2018"
     },
     {
-        "duration": "45.0",
-        "name": "system_health.memory_desktop/load:media:youtube:2018"
-    },
-    {
-        "duration": "24.0",
-        "name": "system_health.memory_desktop/load:media:youtubelivingroom:2020"
-    },
-    {
         "duration": "47.0",
         "name": "system_health.memory_desktop/load:news:bbc:2018"
     },
@@ -208,27 +188,23 @@
         "name": "system_health.memory_desktop/load:news:nytimes:2018"
     },
     {
-        "duration": "72.0",
+        "duration": "73.0",
         "name": "system_health.memory_desktop/load:news:qq:2018"
     },
     {
-        "duration": "52.0",
+        "duration": "53.0",
         "name": "system_health.memory_desktop/load:news:reddit:2018"
     },
     {
-        "duration": "33.0",
-        "name": "system_health.memory_desktop/load:news:wikipedia:2018"
-    },
-    {
-        "duration": "55.0",
+        "duration": "54.0",
         "name": "system_health.memory_desktop/load:search:amazon:2018"
     },
     {
-        "duration": "55.0",
+        "duration": "54.0",
         "name": "system_health.memory_desktop/load:search:baidu:2018"
     },
     {
-        "duration": "47.0",
+        "duration": "48.0",
         "name": "system_health.memory_desktop/load:search:ebay:2018"
     },
     {
@@ -236,7 +212,7 @@
         "name": "system_health.memory_desktop/load:search:flipkart:2018"
     },
     {
-        "duration": "31.0",
+        "duration": "32.0",
         "name": "system_health.memory_desktop/load:search:google:2018"
     },
     {
@@ -244,23 +220,15 @@
         "name": "system_health.memory_desktop/load:search:taobao:2018"
     },
     {
-        "duration": "54.0",
-        "name": "system_health.memory_desktop/load:search:yahoo:2018"
-    },
-    {
-        "duration": "27.0",
-        "name": "system_health.memory_desktop/load:search:yandex:2018"
-    },
-    {
-        "duration": "33.0",
+        "duration": "34.0",
         "name": "system_health.memory_desktop/load:social:instagram:2018"
     },
     {
-        "duration": "30.0",
+        "duration": "29.0",
         "name": "system_health.memory_desktop/load:social:pinterest:2019"
     },
     {
-        "duration": "45.0",
+        "duration": "47.0",
         "name": "system_health.memory_desktop/load:social:vk:2018"
     },
     {
@@ -280,14 +248,10 @@
         "name": "system_health.memory_desktop/load:tools:gmail:2019"
     },
     {
-        "duration": "40.0",
+        "duration": "39.0",
         "name": "system_health.memory_desktop/load:tools:stackoverflow:2018"
     },
     {
-        "duration": "45.0",
-        "name": "system_health.memory_desktop/load:tools:weather:2019"
-    },
-    {
         "duration": "30.0",
         "name": "system_health.memory_desktop/load_accessibility:media:wikipedia:2018"
     },
@@ -297,26 +261,6 @@
     },
     {
         "duration": "2.0",
-        "name": "system_health.memory_desktop/long_running:tools:gmail-background"
-    },
-    {
-        "duration": "2.0",
-        "name": "system_health.memory_desktop/long_running:tools:gmail-foreground"
-    },
-    {
-        "duration": "2.0",
-        "name": "system_health.memory_desktop/multitab:misc:typical24"
-    },
-    {
-        "duration": "2.0",
-        "name": "system_health.memory_desktop/multitab:misc:typical24:2018"
-    },
-    {
-        "duration": "1.0",
         "name": "system_health.memory_desktop/play:media:google_play_music"
-    },
-    {
-        "duration": "75.0",
-        "name": "system_health.memory_desktop/play:media:soundcloud:2018"
     }
 ]
\ No newline at end of file
diff --git a/tools/perf/expectations.config b/tools/perf/expectations.config
index 72b0253..a3f91343 100644
--- a/tools/perf/expectations.config
+++ b/tools/perf/expectations.config
@@ -192,6 +192,10 @@
 crbug.com/1192303 [ fuchsia ] media.desktop/video.html?src=tulip2.vp9.webm [ Skip ]
 crbug.com/1192303 [ fuchsia ] media.desktop/video.html?src=tulip2.vp9.webm_WiFi [ Skip ]
 
+# Benchmark: media.mobile
+crbug.com/1202988 [ fuchsia ] media.mobile/video.html?src=boat_1080p60fps_vp9.webm [ Skip ]
+crbug.com/1202988 [ fuchsia ] media.mobile/video.html?src=tulip2.vp9.webm&background [ Skip ]
+
 # Benchmark: power.desktop
 crbug.com/1036360 [ win7 ] power.desktop/abcnews [ Skip ]
 crbug.com/1082094 [ win10 ] power.desktop/abcnews [ Skip ]
diff --git a/tools/typescript/tsconfig_base.json b/tools/typescript/tsconfig_base.json
index 94079dc..eb5eaea3 100644
--- a/tools/typescript/tsconfig_base.json
+++ b/tools/typescript/tsconfig_base.json
@@ -11,20 +11,13 @@
     "declaration": true,
     "tsBuildInfoFile": "tsconfig.tsbuildinfo",
 
-    "alwaysStrict": true,
     "forceConsistentCasingInFileNames": true,
     "noFallthroughCasesInSwitch": true,
-    "noImplicitAny": true,
     "noImplicitReturns": true,
-    "noImplicitThis": true,
     "noPropertyAccessFromIndexSignature": true,
     "noUncheckedIndexedAccess": true,
     "noUnusedLocals": true,
     "noUnusedParameters": true,
-    "strictBindCallApply": true,
-    "strictFunctionTypes": true,
-    "strictNullChecks": true,
-    "strictPropertyInitialization": true,
     "strict": true
   }
 }
diff --git a/ui/accessibility/platform/inspect/ax_inspect_utils.cc b/ui/accessibility/platform/inspect/ax_inspect_utils.cc
index 9fc43bcd..4a190d1 100644
--- a/ui/accessibility/platform/inspect/ax_inspect_utils.cc
+++ b/ui/accessibility/platform/inspect/ax_inspect_utils.cc
@@ -39,6 +39,11 @@
     return base::NumberToString(value.GetInt());
   }
 
+  // Double.
+  if (value.is_double()) {
+    return base::NumberToString(value.GetDouble());
+  }
+
   // List: exposed as [value1, ..., valueN];
   if (value.is_list()) {
     std::string output;
diff --git a/ui/accessibility/platform/inspect/ax_inspect_utils_unittest.cc b/ui/accessibility/platform/inspect/ax_inspect_utils_unittest.cc
index b3543c9..8ce568f 100644
--- a/ui/accessibility/platform/inspect/ax_inspect_utils_unittest.cc
+++ b/ui/accessibility/platform/inspect/ax_inspect_utils_unittest.cc
@@ -26,6 +26,11 @@
   EXPECT_EQ(AXFormatValue(value), std::string("3"));
 }
 
+TEST(AXInspectUtilsTest, FormatDouble) {
+  base::Value value(3.3);
+  EXPECT_EQ(AXFormatValue(value), std::string("3.3"));
+}
+
 TEST(AXInspectUtilsTest, FormatList) {
   base::Value list(base::Value::Type::LIST);
   list.Append("item1");
diff --git a/ui/gl/DEPS b/ui/gl/DEPS
index a135d3b..ca1a801 100644
--- a/ui/gl/DEPS
+++ b/ui/gl/DEPS
@@ -1,6 +1,5 @@
 include_rules = [
   "+cc/base",
-  "+components/viz/common/resources/resource_format.h",
   "+mojo/public/cpp/bindings",
   "+third_party/khronos",
   "+third_party/libsync",
diff --git a/ui/gl/direct_composition_surface_win_unittest.cc b/ui/gl/direct_composition_surface_win_unittest.cc
index 84a089f3..3a0201ed 100644
--- a/ui/gl/direct_composition_surface_win_unittest.cc
+++ b/ui/gl/direct_composition_surface_win_unittest.cc
@@ -1214,9 +1214,8 @@
   ASSERT_TRUE(front_buffer_texture);
 
   auto front_buffer_image = base::MakeRefCounted<GLImageD3D>(
-      swap_chain_size, GL_BGRA_EXT, GL_UNSIGNED_BYTE,
-      gfx::ColorSpace::CreateSRGB(), front_buffer_texture,
-      /*array_slice=*/0, /*plane_index=*/0, swap_chain);
+      swap_chain_size, GL_BGRA_EXT, GL_UNSIGNED_BYTE, front_buffer_texture,
+      swap_chain);
   ASSERT_TRUE(front_buffer_image->Initialize());
 
   Microsoft::WRL::ComPtr<ID3D11Texture2D> back_buffer_texture;
diff --git a/ui/gl/gl_image_d3d.cc b/ui/gl/gl_image_d3d.cc
index 75fc087c..976ab720 100644
--- a/ui/gl/gl_image_d3d.cc
+++ b/ui/gl/gl_image_d3d.cc
@@ -11,30 +11,49 @@
 #ifndef EGL_ANGLE_image_d3d11_texture
 #define EGL_D3D11_TEXTURE_ANGLE 0x3484
 #define EGL_TEXTURE_INTERNAL_FORMAT_ANGLE 0x345D
-#define EGL_D3D11_TEXTURE_PLANE_ANGLE 0x3492
-#define EGL_D3D11_TEXTURE_ARRAY_SLICE_ANGLE 0x3493
 #endif /* EGL_ANGLE_image_d3d11_texture */
 
 namespace gl {
 
+namespace {
+
+bool ValidGLInternalFormat(unsigned internal_format) {
+  switch (internal_format) {
+    case GL_RGB:
+    case GL_RGBA:
+    case GL_BGRA_EXT:
+      return true;
+    default:
+      return false;
+  }
+}
+
+bool ValidGLDataType(unsigned data_type) {
+  switch (data_type) {
+    case GL_UNSIGNED_BYTE:
+    case GL_HALF_FLOAT_OES:
+      return true;
+    default:
+      return false;
+  }
+}
+
+}  // namespace
+
 GLImageD3D::GLImageD3D(const gfx::Size& size,
                        unsigned internal_format,
                        unsigned data_type,
-                       const gfx::ColorSpace& color_space,
                        Microsoft::WRL::ComPtr<ID3D11Texture2D> texture,
-                       size_t array_slice,
-                       size_t plane_index,
                        Microsoft::WRL::ComPtr<IDXGISwapChain1> swap_chain)
     : GLImage(),
       size_(size),
       internal_format_(internal_format),
       data_type_(data_type),
       texture_(std::move(texture)),
-      array_slice_(array_slice),
-      plane_index_(plane_index),
       swap_chain_(std::move(swap_chain)) {
-  GLImage::SetColorSpace(color_space);
   DCHECK(texture_);
+  DCHECK(ValidGLInternalFormat(internal_format_));
+  DCHECK(ValidGLDataType(data_type_));
 }
 
 GLImageD3D::~GLImageD3D() {
@@ -50,12 +69,7 @@
 bool GLImageD3D::Initialize() {
   DCHECK_EQ(egl_image_, EGL_NO_IMAGE_KHR);
   const EGLint attribs[] = {EGL_TEXTURE_INTERNAL_FORMAT_ANGLE,
-                            internal_format_,
-                            EGL_D3D11_TEXTURE_ARRAY_SLICE_ANGLE,
-                            array_slice_,
-                            EGL_D3D11_TEXTURE_PLANE_ANGLE,
-                            plane_index_,
-                            EGL_NONE};
+                            GetInternalFormat(), EGL_NONE};
   egl_image_ =
       eglCreateImageKHR(GLSurfaceEGL::GetHardwareDisplay(), EGL_NO_CONTEXT,
                         EGL_D3D11_TEXTURE_ANGLE,
diff --git a/ui/gl/gl_image_d3d.h b/ui/gl/gl_image_d3d.h
index fa62f42..6c5b2166 100644
--- a/ui/gl/gl_image_d3d.h
+++ b/ui/gl/gl_image_d3d.h
@@ -22,19 +22,13 @@
   // |internal_format| and |data_type| are passed to ANGLE and used for GL
   // operations.  |internal_format| may be different from the internal format
   // associated with the DXGI_FORMAT of the texture (e.g. RGB instead of
-  // BGRA_EXT for DXGI_FORMAT_B8G8R8A8_UNORM). |data_type| should match the data
-  // type accociated with the DXGI_FORMAT of the texture.  |array_slice| is used
-  // when the |texture| is a D3D11 texture array, and |plane_index| is used for
-  // specifying the plane to bind to for multi-planar YUV textures.
-  // See EGL_ANGLE_d3d_texture_client_buffer spec for format restrictions.
+  // BGRA_EXT for DXGI_FORMAT_B8G8R8A8_UNORM).  |data_type| should match the
+  // data type accociated with the DXGI_FORMAT of the texture.
   GLImageD3D(const gfx::Size& size,
              unsigned internal_format,
              unsigned data_type,
-             const gfx::ColorSpace& color_space,
              Microsoft::WRL::ComPtr<ID3D11Texture2D> texture,
-             size_t array_slice = 0,
-             size_t plane_index = 0,
-             Microsoft::WRL::ComPtr<IDXGISwapChain1> swap_chain = nullptr);
+             Microsoft::WRL::ComPtr<IDXGISwapChain1> swap_chain);
 
   // Safe downcast. Returns nullptr on failure.
   static GLImageD3D* FromGLImage(GLImage* image);
@@ -68,27 +62,20 @@
   const Microsoft::WRL::ComPtr<ID3D11Texture2D>& texture() const {
     return texture_;
   }
+
   const Microsoft::WRL::ComPtr<IDXGISwapChain1>& swap_chain() const {
     return swap_chain_;
   }
-  size_t array_slice() const { return array_slice_; }
-  size_t plane_index() const { return plane_index_; }
-
- protected:
-  const gfx::Size size_;
-  const unsigned internal_format_;  // GLenum
-  const unsigned data_type_;        // GLenum
-
-  Microsoft::WRL::ComPtr<ID3D11Texture2D> texture_;
-  const size_t array_slice_;
-  const size_t plane_index_;
-
-  Microsoft::WRL::ComPtr<IDXGISwapChain1> swap_chain_;
 
  private:
   ~GLImageD3D() override;
 
-  void* egl_image_ = nullptr;  // EGLImageKHR
+  const gfx::Size size_;
+  const unsigned internal_format_;  // GLenum
+  const unsigned data_type_;        // GLenum
+  void* egl_image_ = nullptr;       // EGLImageKHR
+  Microsoft::WRL::ComPtr<ID3D11Texture2D> texture_;
+  Microsoft::WRL::ComPtr<IDXGISwapChain1> swap_chain_;
 
   DISALLOW_COPY_AND_ASSIGN(GLImageD3D);
 };
diff --git a/ui/gl/gl_image_d3d_unittest.cc b/ui/gl/gl_image_d3d_unittest.cc
index 4b047f4d..febd42ee 100644
--- a/ui/gl/gl_image_d3d_unittest.cc
+++ b/ui/gl/gl_image_d3d_unittest.cc
@@ -52,8 +52,7 @@
     EXPECT_TRUE(SUCCEEDED(hr));
 
     auto image = base::MakeRefCounted<GLImageD3D>(
-        size, internal_format, data_type, gfx::ColorSpace::CreateSRGB(),
-        std::move(d3d11_texture));
+        size, internal_format, data_type, std::move(d3d11_texture), nullptr);
     EXPECT_TRUE(image->Initialize());
     return image;
   }
diff --git a/ui/gl/swap_chain_presenter.cc b/ui/gl/swap_chain_presenter.cc
index 07088b9b..cefbf6b 100644
--- a/ui/gl/swap_chain_presenter.cc
+++ b/ui/gl/swap_chain_presenter.cc
@@ -634,28 +634,23 @@
 }
 
 bool SwapChainPresenter::TryPresentToDecodeSwapChain(
-    Microsoft::WRL::ComPtr<ID3D11Texture2D> texture,
-    unsigned array_slice,
-    const gfx::ColorSpace& color_space,
+    GLImageDXGI* nv12_image,
     const gfx::Rect& content_rect,
-    const gfx::Size& swap_chain_size,
-    DXGI_FORMAT swap_chain_format) {
+    const gfx::Size& swap_chain_size) {
   if (ShouldUseVideoProcessorScaling())
     return false;
 
   auto not_used_reason = DecodeSwapChainNotUsedReason::kFailedToPresent;
 
   bool nv12_supported =
-      (swap_chain_format == DXGI_FORMAT_NV12) &&
       (DXGI_FORMAT_NV12 ==
        DirectCompositionSurfaceWin::GetOverlayFormatUsedForSDR());
   // TODO(sunnyps): Try using decode swap chain for uploaded video images.
-  if (texture && nv12_supported && !failed_to_present_decode_swapchain_) {
+  if (nv12_image && nv12_supported && !failed_to_present_decode_swapchain_) {
     D3D11_TEXTURE2D_DESC texture_desc = {};
-    texture->GetDesc(&texture_desc);
+    nv12_image->texture()->GetDesc(&texture_desc);
 
-    bool is_decoder_texture = (texture_desc.Format == DXGI_FORMAT_NV12) &&
-                              (texture_desc.BindFlags & D3D11_BIND_DECODER);
+    bool is_decoder_texture = texture_desc.BindFlags & D3D11_BIND_DECODER;
 
     // Decode swap chains do not support shared resources.
     // TODO(sunnyps): Find a workaround for when the decoder moves to its own
@@ -689,10 +684,8 @@
 
     if (is_decoder_texture && !is_shared_texture && !is_unitary_texture_array &&
         is_overlay_supported_transform) {
-      if (PresentToDecodeSwapChain(texture, array_slice, color_space,
-                                   content_rect, swap_chain_size)) {
+      if (PresentToDecodeSwapChain(nv12_image, content_rect, swap_chain_size))
         return true;
-      }
       ReleaseSwapChainResources();
       failed_to_present_decode_swapchain_ = true;
       not_used_reason = DecodeSwapChainNotUsedReason::kFailedToPresent;
@@ -707,7 +700,7 @@
     } else if (!is_overlay_supported_transform) {
       not_used_reason = DecodeSwapChainNotUsedReason::kIncompatibleTransform;
     }
-  } else if (!texture) {
+  } else if (!nv12_image) {
     not_used_reason = DecodeSwapChainNotUsedReason::kSoftwareFrame;
   } else if (!nv12_supported) {
     not_used_reason = DecodeSwapChainNotUsedReason::kNv12NotSupported;
@@ -721,9 +714,7 @@
 }
 
 bool SwapChainPresenter::PresentToDecodeSwapChain(
-    Microsoft::WRL::ComPtr<ID3D11Texture2D> texture,
-    unsigned array_slice,
-    const gfx::ColorSpace& color_space,
+    GLImageDXGI* nv12_image,
     const gfx::Rect& content_rect,
     const gfx::Size& swap_chain_size) {
   DCHECK(!swap_chain_size.IsEmpty());
@@ -733,7 +724,7 @@
                swap_chain_size.ToString());
 
   Microsoft::WRL::ComPtr<IDXGIResource> decode_resource;
-  texture.As(&decode_resource);
+  nv12_image->texture().As(&decode_resource);
   DCHECK(decode_resource);
 
   if (!decode_swap_chain_ || decode_resource_ != decode_resource) {
@@ -795,6 +786,10 @@
 
     content_visual_->SetContent(decode_surface_.Get());
     layer_tree_->SetNeedsRebuildVisualTree();
+  } else if (last_presented_images_[kNV12ImageIndex] == nv12_image &&
+             swap_chain_size_ == swap_chain_size) {
+    // Early out if we're presenting the same image again.
+    return true;
   }
 
   RECT source_rect = content_rect.ToRECT();
@@ -805,6 +800,10 @@
   RECT target_rect = gfx::Rect(swap_chain_size).ToRECT();
   decode_swap_chain_->SetTargetRect(&target_rect);
 
+  gfx::ColorSpace color_space = nv12_image->color_space();
+  if (!color_space.IsValid())
+    color_space = gfx::ColorSpace::CreateREC709();
+
   // TODO(sunnyps): Move this to gfx::ColorSpaceWin helper where we can access
   // internal color space state and do a better job.
   // Common color spaces have primaries and transfer function similar to BT 709
@@ -812,8 +811,7 @@
   int color_space_flags = DXGI_MULTIPLANE_OVERLAY_YCbCr_FLAG_BT709;
   // Proper Rec 709 and 601 have limited or nominal color range.
   if (color_space == gfx::ColorSpace::CreateREC709() ||
-      color_space == gfx::ColorSpace::CreateREC601() ||
-      !color_space.IsValid()) {
+      color_space == gfx::ColorSpace::CreateREC601()) {
     color_space_flags |= DXGI_MULTIPLANE_OVERLAY_YCbCr_FLAG_NOMINAL_RANGE;
   }
   // xvYCC allows colors outside nominal range to encode negative colors that
@@ -825,7 +823,8 @@
       static_cast<DXGI_MULTIPLANE_OVERLAY_YCbCr_FLAGS>(color_space_flags));
 
   UINT present_flags = DXGI_PRESENT_USE_DURATION;
-  HRESULT hr = decode_swap_chain_->PresentBuffer(array_slice, 1, present_flags);
+  HRESULT hr =
+      decode_swap_chain_->PresentBuffer(nv12_image->level(), 1, present_flags);
   // Ignore DXGI_STATUS_OCCLUDED since that's not an error but only indicates
   // that the window is occluded and we can stop rendering.
   if (FAILED(hr) && hr != DXGI_STATUS_OCCLUDED) {
@@ -833,6 +832,8 @@
     return false;
   }
 
+  last_presented_images_ = ui::DCRendererLayerParams::OverlayImages();
+  last_presented_images_[kNV12ImageIndex] = nv12_image;
   swap_chain_size_ = swap_chain_size;
   if (swap_chain_format_ == DXGI_FORMAT_NV12) {
     frames_since_color_space_change_++;
@@ -851,20 +852,14 @@
     const ui::DCRendererLayerParams& params) {
   GLImageDXGI* image_dxgi =
       GLImageDXGI::FromGLImage(params.images[kNV12ImageIndex].get());
-  GLImageD3D* image_d3d =
-      GLImageD3D::FromGLImage(params.images[kNV12ImageIndex].get());
-
   GLImageMemory* y_image_memory =
       GLImageMemory::FromGLImage(params.images[kYPlaneImageIndex].get());
   GLImageMemory* uv_image_memory =
       GLImageMemory::FromGLImage(params.images[kUVPlaneImageIndex].get());
-
   GLImageD3D* swap_chain_image =
       GLImageD3D::FromGLImage(params.images[kSwapChainImageIndex].get());
-  if (swap_chain_image && !swap_chain_image->swap_chain())
-    swap_chain_image = nullptr;
 
-  if (!image_dxgi && !image_d3d && (!y_image_memory || !uv_image_memory) &&
+  if (!image_dxgi && (!y_image_memory || !uv_image_memory) &&
       !swap_chain_image) {
     DLOG(ERROR) << "Video GLImages are missing";
     ReleaseSwapChainResources();
@@ -874,8 +869,7 @@
     return true;
   }
 
-  if ((image_dxgi && !image_dxgi->texture()) ||
-      (image_d3d && !image_d3d->texture())) {
+  if (image_dxgi && !image_dxgi->texture()) {
     // We can't proceed if |image_dxgi| has no underlying d3d11 texture.  It's
     // unclear how we get into this state, but we do observe crashes due to it.
     // Just stop here instead, and render incorrectly.
@@ -885,14 +879,11 @@
     return true;
   }
 
-  std::string image_type;
-  if (swap_chain_image) {
-    image_type = "swap chain";
-  } else if (image_d3d || image_dxgi) {
+  std::string image_type = "software video frame";
+  if (image_dxgi)
     image_type = "hardware video frame";
-  } else {
-    image_type = "software video frame";
-  }
+  if (swap_chain_image)
+    image_type = "swap chain";
 
   gfx::Transform transform = params.transform;
   gfx::Rect clip_rect = params.clip_rect;
@@ -909,29 +900,12 @@
   TRACE_EVENT2("gpu", "SwapChainPresenter::PresentToSwapChain", "image_type",
                image_type, "swap_chain_size", swap_chain_size.ToString());
 
-  Microsoft::WRL::ComPtr<ID3D11Texture2D> input_texture;
-  unsigned input_level = 0u;
-  Microsoft::WRL::ComPtr<IDXGIKeyedMutex> keyed_mutex;
-  gfx::ColorSpace input_color_space;
-  if (image_dxgi) {
-    input_texture = image_dxgi->texture();
-    input_level = image_dxgi->level();
-    // Keyed mutex may not exist.
-    keyed_mutex = image_dxgi->keyed_mutex();
-    input_color_space = image_dxgi->color_space();
-  } else if (image_d3d) {
-    input_texture = image_d3d->texture();
-    input_level = image_d3d->array_slice();
-    input_color_space = image_d3d->color_space();
-  }
-  if (!input_color_space.IsValid())
-    input_color_space = gfx::ColorSpace::CreateREC709();
-
-  bool content_is_hdr = input_color_space.IsHDR();
+  bool content_is_hdr = image_dxgi && image_dxgi->color_space().IsHDR();
   // Do not create a swap chain if swap chain size will be empty.
   if (swap_chain_size.IsEmpty()) {
     swap_chain_size_ = swap_chain_size;
     if (swap_chain_) {
+      last_presented_images_ = ui::DCRendererLayerParams::OverlayImages();
       ReleaseSwapChainResources();
       content_visual_->SetContent(nullptr);
       layer_tree_->SetNeedsRebuildVisualTree();
@@ -944,16 +918,20 @@
   // Swap chain image already has a swap chain that's presented by the client
   // e.g. for webgl/canvas low-latency/desynchronized mode.
   if (swap_chain_image) {
-    DCHECK(swap_chain_image->swap_chain());
     content_visual_->SetContent(swap_chain_image->swap_chain().Get());
-    if (last_presented_images_ != params.images) {
-      ReleaseSwapChainResources();
+    if (last_presented_images_[kSwapChainImageIndex] != swap_chain_image) {
       last_presented_images_ = params.images;
+      ReleaseSwapChainResources();
       layer_tree_->SetNeedsRebuildVisualTree();
     }
     return true;
   }
 
+  if (TryPresentToDecodeSwapChain(image_dxgi, params.content_rect,
+                                  swap_chain_size)) {
+    return true;
+  }
+
   bool swap_chain_resized = swap_chain_size_ != swap_chain_size;
 
   DXGI_FORMAT swap_chain_format =
@@ -962,21 +940,6 @@
   bool toggle_protected_video =
       protected_video_type_ != params.protected_video_type;
 
-  if (swap_chain_ && !swap_chain_resized && !swap_chain_format_changed &&
-      !toggle_protected_video && last_presented_images_ == params.images) {
-    // The swap chain is presenting the same images as last swap, which means
-    // that the images were never returned to the video decoder and should
-    // have the same contents as last time. It shouldn't need to be redrawn.
-    return true;
-  }
-
-  if (TryPresentToDecodeSwapChain(input_texture, input_level, input_color_space,
-                                  params.content_rect, swap_chain_size,
-                                  swap_chain_format)) {
-    last_presented_images_ = params.images;
-    return true;
-  }
-
   // Try reallocating swap chain if resizing fails.
   if (!swap_chain_ || swap_chain_resized || swap_chain_format_changed ||
       toggle_protected_video) {
@@ -987,9 +950,22 @@
     }
     content_visual_->SetContent(swap_chain_.Get());
     layer_tree_->SetNeedsRebuildVisualTree();
+  } else if (last_presented_images_ == params.images) {
+    // The swap chain is presenting the same images as last swap, which means
+    // that the images were never returned to the video decoder and should
+    // have the same contents as last time. It shouldn't need to be redrawn.
+    return true;
   }
+  last_presented_images_ = params.images;
 
-  if (input_texture) {
+  Microsoft::WRL::ComPtr<ID3D11Texture2D> input_texture;
+  UINT input_level;
+  Microsoft::WRL::ComPtr<IDXGIKeyedMutex> keyed_mutex;
+  if (image_dxgi) {
+    input_texture = image_dxgi->texture();
+    input_level = static_cast<UINT>(image_dxgi->level());
+    // Keyed mutex may not exist.
+    keyed_mutex = image_dxgi->keyed_mutex();
     staging_texture_.Reset();
     copy_texture_.Reset();
   } else {
@@ -1080,7 +1056,6 @@
     DLOG(ERROR) << "Present failed with error 0x" << std::hex << hr;
     return false;
   }
-  last_presented_images_ = params.images;
   frames_since_color_space_change_++;
   RecordPresentationStatistics();
   return true;
@@ -1310,7 +1285,6 @@
 }
 
 void SwapChainPresenter::ReleaseSwapChainResources() {
-  last_presented_images_ = ui::DCRendererLayerParams::OverlayImages();
   output_view_.Reset();
   swap_chain_.Reset();
   decode_surface_.Reset();
diff --git a/ui/gl/swap_chain_presenter.h b/ui/gl/swap_chain_presenter.h
index e5531420..afc17b0 100644
--- a/ui/gl/swap_chain_presenter.h
+++ b/ui/gl/swap_chain_presenter.h
@@ -19,6 +19,7 @@
 
 namespace gl {
 class DCLayerTree;
+class GLImageDXGI;
 class GLImageMemory;
 
 // SwapChainPresenter holds a swap chain, direct composition visuals, and other
@@ -160,20 +161,14 @@
   // Try presenting to a decode swap chain based on various conditions such as
   // global state (e.g. finch, NV12 support), texture flags, and transform.
   // Returns true on success.  See PresentToDecodeSwapChain() for more info.
-  bool TryPresentToDecodeSwapChain(
-      Microsoft::WRL::ComPtr<ID3D11Texture2D> texture,
-      unsigned array_slice,
-      const gfx::ColorSpace& color_space,
-      const gfx::Rect& content_rect,
-      const gfx::Size& swap_chain_size,
-      DXGI_FORMAT swap_chain_format);
+  bool TryPresentToDecodeSwapChain(GLImageDXGI* nv12_image,
+                                   const gfx::Rect& content_rect,
+                                   const gfx::Size& swap_chain_size);
 
   // Present to a decode swap chain created from compatible video decoder
   // buffers using given |nv12_image| with destination size |swap_chain_size|.
   // Returns true on success.
-  bool PresentToDecodeSwapChain(Microsoft::WRL::ComPtr<ID3D11Texture2D> texture,
-                                unsigned array_slice,
-                                const gfx::ColorSpace& color_space,
+  bool PresentToDecodeSwapChain(GLImageDXGI* nv12_image,
                                 const gfx::Rect& content_rect,
                                 const gfx::Size& swap_chain_size);
 
diff --git a/ui/views/examples/animation_example.cc b/ui/views/examples/animation_example.cc
index 4d658de3..76a49e3 100644
--- a/ui/views/examples/animation_example.cc
+++ b/ui/views/examples/animation_example.cc
@@ -6,142 +6,40 @@
 
 #include <algorithm>
 #include <memory>
-#include <numeric>
 
-#include "base/check.h"
-#include "base/containers/circular_deque.h"
-#include "base/numerics/ranges.h"
-#include "base/scoped_observation.h"
-#include "base/time/time.h"
 #include "ui/compositor/layer.h"
 #include "ui/compositor/layer_animation_element.h"
 #include "ui/compositor/layer_animation_sequence.h"
 #include "ui/compositor/layer_animator.h"
-#include "ui/gfx/animation/animation.h"
-#include "ui/gfx/animation/animation_delegate.h"
 #include "ui/gfx/geometry/point.h"
 #include "ui/gfx/geometry/size.h"
-#include "ui/gfx/geometry/vector2d_f.h"
 #include "ui/views/background.h"
+#include "ui/views/layout/animating_layout_manager.h"
+#include "ui/views/layout/layout_manager_base.h"
 #include "ui/views/view.h"
-#include "ui/views/view_observer.h"
 
 namespace views {
 namespace examples {
-namespace {
-
-class Animation : public gfx::Animation {
- public:
-  Animation(gfx::AnimationDelegate* delegate,
-            gfx::Point current_position,
-            gfx::Point target_position);
-  Animation(const Animation&) = delete;
-  Animation& operator=(const Animation&) = delete;
-  ~Animation() override = default;
-
-  // gfx::Animation:
-  double GetCurrentValue() const override;
-
-  gfx::Vector2dF GetOffsetFromCurrentAnimationStart() const;
-  void NewAnimationStarted();
-
- protected:
-  bool ShouldSendCanceledFromStop() override;
-  void Step(base::TimeTicks time_now) override;
-
- private:
-  gfx::Vector2dF delta_;
-  double t_ = 0;
-  base::TimeDelta total_time_ = base::TimeDelta::FromSeconds(1);
-  bool decelerating_ = false;
-};
-
-Animation::Animation(gfx::AnimationDelegate* delegate,
-                     gfx::Point current_position,
-                     gfx::Point target_position)
-    : gfx::Animation(base::TimeDelta::FromHz(60)),
-      delta_(target_position - current_position) {
-  set_delegate(delegate);
-  Start();
-}
-
-double Animation::GetCurrentValue() const {
-  return decelerating_ ? (t_ * (1 - t_)) : t_;
-}
-
-gfx::Vector2dF Animation::GetOffsetFromCurrentAnimationStart() const {
-  return ScaleVector2d(delta_, GetCurrentValue());
-}
-
-void Animation::NewAnimationStarted() {
-  Stop();
-  delta_ -= GetOffsetFromCurrentAnimationStart();
-  t_ = 0;
-  total_time_ -= base::TimeTicks::Now() - start_time();
-  decelerating_ = true;
-  Start();
-}
-
-bool Animation::ShouldSendCanceledFromStop() {
-  return t_ != 1;
-}
-
-void Animation::Step(base::TimeTicks time_now) {
-  const base::TimeDelta elapsed_time =
-      std::min(time_now - start_time(), total_time_);
-  t_ = gfx::Tween::CalculateValue(gfx::Tween::EASE_OUT,
-                                  elapsed_time / total_time_);
-  delegate()->AnimationProgressed(this);
-  if (t_ == 1)
-    Stop();
-}
-
-}  // namespace
 
 AnimationExample::AnimationExample() : ExampleBase("Animation") {}
 
 AnimationExample::~AnimationExample() = default;
 
-class AnimatingSquare : public View,
-                        public gfx::AnimationDelegate,
-                        public ViewObserver {
+class AnimatingSquare : public View {
  public:
-  AnimatingSquare(size_t index, View* parent);
+  explicit AnimatingSquare(size_t index);
   AnimatingSquare(const AnimatingSquare&) = delete;
   AnimatingSquare& operator=(const AnimatingSquare&) = delete;
   ~AnimatingSquare() override = default;
-
-  // gfx::AnimationDelegate:
-  void AnimationProgressed(const gfx::Animation* animation) override;
-  void AnimationEnded(const gfx::Animation* animation) override;
-
-  // ViewObserver:
-  void OnViewBoundsChanged(View* observed_view) override;
-
- private:
-  gfx::Point ComputeTargetPosition(const View* parent) const;
-
-  static constexpr int kPadding = 25;
-  static constexpr gfx::Size kSize = gfx::Size(100, 100);
-
-  size_t index_;
-  base::ScopedObservation<View, ViewObserver> view_observation_{this};
-  gfx::Point start_position_, target_position_;
-  base::circular_deque<std::unique_ptr<Animation>> animations_;
 };
 
-// static
-constexpr gfx::Size AnimatingSquare::kSize;
-
-AnimatingSquare::AnimatingSquare(size_t index, View* parent)
-    : index_(index), target_position_(ComputeTargetPosition(parent)) {
+AnimatingSquare::AnimatingSquare(size_t index) {
   SetBackground(
-      CreateSolidBackground(SkColorSetRGB((5 - index_) * 51, 0, index_ * 51)));
-  view_observation_.Observe(parent);
-  SetBoundsRect({target_position_, kSize});
+      CreateSolidBackground(SkColorSetRGB((5 - index) * 51, 0, index * 51)));
 
   SetPaintToLayer();
   layer()->SetFillsBoundsOpaquely(false);
+
   auto opacity_sequence = std::make_unique<ui::LayerAnimationSequence>();
   opacity_sequence->set_is_repeating(true);
   opacity_sequence->AddElement(ui::LayerAnimationElement::CreateOpacityElement(
@@ -151,45 +49,67 @@
   layer()->GetAnimator()->StartAnimation(opacity_sequence.release());
 }
 
-void AnimatingSquare::OnViewBoundsChanged(View* observed_view) {
-  const gfx::Point target_position = ComputeTargetPosition(observed_view);
-  if (target_position_ == target_position)
-    return;
-  start_position_ = origin();
-  target_position_ = target_position;
-  for (const auto& animation : animations_)
-    animation->NewAnimationStarted();
-  animations_.push_back(
-      std::make_unique<Animation>(this, start_position_, target_position_));
-}
+class SquaresLayoutManager : public LayoutManagerBase {
+ public:
+  SquaresLayoutManager() = default;
+  ~SquaresLayoutManager() override = default;
 
-void AnimatingSquare::AnimationProgressed(const gfx::Animation* animation) {
-  gfx::PointF position(start_position_);
-  position = std::accumulate(
-      animations_.cbegin(), animations_.cend(), position,
-      [](gfx::PointF p, const std::unique_ptr<Animation>& animation) {
-        return p + animation->GetOffsetFromCurrentAnimationStart();
-      });
-  SetPosition(gfx::ToRoundedPoint(position));
-}
+ protected:
+  // LayoutManagerBase:
+  ProposedLayout CalculateProposedLayout(
+      const SizeBounds& size_bounds) const override;
 
-void AnimatingSquare::AnimationEnded(const gfx::Animation* animation) {
-  DCHECK_EQ(animation, animations_.front().get());
-  animations_.pop_front();
-}
+ private:
+  static constexpr int kPadding = 25;
+  static constexpr gfx::Size kSize = gfx::Size(100, 100);
+};
 
-gfx::Point AnimatingSquare::ComputeTargetPosition(const View* parent) const {
-  const int views_per_row = base::ClampToRange(
-      (parent->width() - kPadding) / (kPadding + kSize.width()), 1, 5);
-  const size_t row = index_ / views_per_row;
-  const size_t column = index_ % views_per_row;
-  return {kPadding + column * (kPadding + kSize.width()),
-          kPadding + row * (kPadding + kSize.height())};
+// static
+constexpr gfx::Size SquaresLayoutManager::kSize;
+
+ProposedLayout SquaresLayoutManager::CalculateProposedLayout(
+    const SizeBounds& size_bounds) const {
+  ProposedLayout layout;
+
+  const auto& children = host_view()->children();
+  const int item_width = kSize.width() + kPadding;
+  const int item_height = kSize.height() + kPadding;
+  const int max_width = kPadding + (children.size() * item_width);
+  const int bounds_width =
+      std::max(kPadding + item_width, size_bounds.width().min_of(max_width));
+  const int views_per_row = (bounds_width - kPadding) / item_width;
+
+  for (size_t i = 0; i < children.size(); ++i) {
+    const size_t row = i / views_per_row;
+    const size_t column = i % views_per_row;
+    const gfx::Point origin(kPadding + column * item_width,
+                            kPadding + row * item_height);
+    layout.child_layouts.push_back(
+        {children[i], true, gfx::Rect(origin, kSize), SizeBounds(kSize)});
+  }
+
+  const size_t num_rows = (children.size() + views_per_row - 1) / views_per_row;
+  const int max_height = kPadding + (num_rows * item_height);
+  const int bounds_height =
+      std::max(kPadding + item_height, size_bounds.height().min_of(max_height));
+  layout.host_size = {bounds_width, bounds_height};
+  return layout;
 }
 
 void AnimationExample::CreateExampleView(View* container) {
+  container->SetBackground(CreateSolidBackground(SK_ColorWHITE));
+  container->SetPaintToLayer();
+  container->layer()->SetMasksToBounds(true);
+  container->layer()->SetFillsBoundsOpaquely(true);
+
+  container->SetLayoutManager(std::make_unique<AnimatingLayoutManager>())
+      ->SetBoundsAnimationMode(
+          AnimatingLayoutManager::BoundsAnimationMode::kAnimateBothAxes)
+      .SetAnimationDuration(base::TimeDelta::FromSeconds(1))
+      .SetTweenType(gfx::Tween::EASE_IN_OUT)
+      .SetTargetLayoutManager(std::make_unique<SquaresLayoutManager>());
   for (size_t i = 0; i < 5; ++i)
-    container->AddChildView(std::make_unique<AnimatingSquare>(i, container));
+    container->AddChildView(std::make_unique<AnimatingSquare>(i));
 }
 
 }  // namespace examples
diff --git a/ui/webui/resources/cr_components/chromeos/cellular_setup/activation_code_page.js b/ui/webui/resources/cr_components/chromeos/cellular_setup/activation_code_page.js
index 23dc486..7497af5 100644
--- a/ui/webui/resources/cr_components/chromeos/cellular_setup/activation_code_page.js
+++ b/ui/webui/resources/cr_components/chromeos/cellular_setup/activation_code_page.js
@@ -444,6 +444,11 @@
       return;
     }
 
+    // Focus on the next button after scanning is successful.
+    if (this.state_ === PageState.SCANNING_SUCCESS) {
+      this.fire('focus-default-button');
+    }
+
     this.expanded_ = false;
   },
 
diff --git a/ui/webui/resources/cr_components/customize_themes/customize_themes.html b/ui/webui/resources/cr_components/customize_themes/customize_themes.html
index e5c0dc1..2089fca6 100644
--- a/ui/webui/resources/cr_components/customize_themes/customize_themes.html
+++ b/ui/webui/resources/cr_components/customize_themes/customize_themes.html
@@ -68,6 +68,7 @@
   }
 
   #autogeneratedThemeContainer {
+    cursor: pointer;
     position: relative;
   }
 
diff --git a/weblayer/test/stub_autofill_provider.cc b/weblayer/test/stub_autofill_provider.cc
index c1c1b94a..c284781b 100644
--- a/weblayer/test/stub_autofill_provider.cc
+++ b/weblayer/test/stub_autofill_provider.cc
@@ -14,7 +14,7 @@
 StubAutofillProvider::~StubAutofillProvider() = default;
 
 void StubAutofillProvider::OnQueryFormFieldAutofill(
-    autofill::AutofillHandlerProxy* handler,
+    autofill::AndroidAutofillManager* manager,
     int32_t id,
     const autofill::FormData& form,
     const autofill::FormFieldData& field,
diff --git a/weblayer/test/stub_autofill_provider.h b/weblayer/test/stub_autofill_provider.h
index 8f648e4..3ca6d5f 100644
--- a/weblayer/test/stub_autofill_provider.h
+++ b/weblayer/test/stub_autofill_provider.h
@@ -22,7 +22,7 @@
 
   // AutofillProvider:
   void OnQueryFormFieldAutofill(
-      autofill::AutofillHandlerProxy* handler,
+      autofill::AndroidAutofillManager* manager,
       int32_t id,
       const autofill::FormData& form,
       const autofill::FormFieldData& field,