diff --git a/.gn b/.gn
index 80cd194..c894baf 100644
--- a/.gn
+++ b/.gn
@@ -80,7 +80,6 @@
   "//chrome/test:sync_integration_tests",
   "//chrome/test:sync_integration_test_support",
   "//chrome/test:sync_performance_tests",
-  "//chrome/test:test_support",
   "//chrome/test:test_support_ui",
   "//chrome/test:test_support_ui_android",
   "//chrome/test:test_support_unit",
diff --git a/DEPS b/DEPS
index 9f51ae3..646943f 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': '4fe43b3ad85500c60ca99f75073762ba7b23654b',
+  'skia_revision': 'cb41df0bfad4779a404f2ceb3e5b68ddd1eb7c07',
   # 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': '29957be0d30d8e3ea68e19497489066aa9d3176e',
+  'v8_revision': '40513edc885e6fc9a5542e88187b725792ea0696',
   # 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,11 +221,11 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling ANGLE
   # and whatever else without interference from each other.
-  'angle_revision': 'daeac23840f4fb8ef87603d84295924ab4f05510',
+  'angle_revision': '2e84e59ab3e38ddbc230a35ca31a2d6a63f7b5ee',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling SwiftShader
   # and whatever else without interference from each other.
-  'swiftshader_revision': '08762e3c6fc554c2050efa632b9f4dc1efcb4d5a',
+  'swiftshader_revision': 'dcb33711bd4bd14a9bff84db379ad59295c337a3',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling PDFium
   # 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': '967a34eee3fd34f496366ed1283ab5268d23690a',
+  'freetype_revision': 'b070264521386666dd0cbbedec469c84b26489ba',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling freetype
   # 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': '9f27b41d945b2e94033c095396e8886f571c5c86',
+  'devtools_frontend_revision': '757a52d2ec5a4eb5ac4ff35a00678ccff996796e',
   # 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': '5200c8158c7bc3230efdb32aa3773c6db5c617cf',
+  'dawn_revision': 'e2f083e4b0f5926b6ca4abe9ab13a710fac2b917',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
@@ -730,7 +730,7 @@
     'packages': [
       {
           'package': 'chromium/third_party/androidx',
-          'version': 'VYT-kV9hO7iazLkZ6_B5NPXMxTwyjcFI3hHKKWdbRvIC',
+          'version': 'WpOMGKPknIvtLmyme5NOnOsAJCTFE_iqJ3DPKH5DXv4C',
       },
     ],
     'condition': 'checkout_android',
@@ -966,7 +966,7 @@
   },
 
   'src/third_party/depot_tools':
-    Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + 'c499142087df4f7e473c411657a9fa2809e57114',
+    Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + 'cb17bc602880062ea728a78449fb87d778791dc2',
 
   'src/third_party/devtools-frontend/src':
     Var('chromium_git') + '/devtools/devtools-frontend' + '@' + Var('devtools_frontend_revision'),
@@ -1332,7 +1332,7 @@
     Var('chromium_git') + '/external/github.com/cisco/openh264' + '@' + '3dd5b80bc4f172dd82925bb259cb7c82348409c5',
 
   'src/third_party/openscreen/src':
-    Var('chromium_git') + '/openscreen' + '@' + '37e21e8e06746c3106ff4c22ca8e41d29ffc27d6',
+    Var('chromium_git') + '/openscreen' + '@' + '655e5480afbd232c73a1ea33b23dddc40eb12f01',
 
   'src/third_party/openxr/src': {
     'url': Var('chromium_git') + '/external/github.com/KhronosGroup/OpenXR-SDK' + '@' + '97cfe495bb7a3853266b646d1c79e169387f9c7a',
@@ -1427,7 +1427,7 @@
       'packages': [
           {
               'package': 'fuchsia/third_party/aemu/linux-amd64',
-              'version': 'H-kH9WxQErL_AR-Nu_ZL8hbu1D-rZmdQQUaYZYm3AOUC'
+              'version': 'pwjSs3IapHTvM0wB7z3723g8rjsQnCWikZJhQxtBetsC'
           },
       ],
       'condition': 'host_os == "linux" and checkout_fuchsia',
@@ -1536,7 +1536,7 @@
   'src/third_party/usrsctp/usrsctplib':
     Var('chromium_git') + '/external/github.com/sctplab/usrsctp' + '@' + 'acfce46e428cc084b4bd0164e1b019261a8dbeda',
 
-  'src/third_party/vulkan-deps': '{chromium_git}/vulkan-deps@d3354ebca4609f9557c2a63da2a8529555e857b6',
+  'src/third_party/vulkan-deps': '{chromium_git}/vulkan-deps@055e71b2a36738fa4c49bb126222844f67b3bfd6',
 
   'src/third_party/vulkan_memory_allocator':
     Var('chromium_git') + '/external/github.com/GPUOpen-LibrariesAndSDKs/VulkanMemoryAllocator.git' + '@' + '732a76d9d3c70d6aa487216495eeb28518349c3a',
diff --git a/WATCHLISTS b/WATCHLISTS
index 9396061f..0ec6893 100644
--- a/WATCHLISTS
+++ b/WATCHLISTS
@@ -989,6 +989,7 @@
     },
     'filesapp': {
       'filepath': 'chrome/browser/ash/drive|' \
+                  'chrome/browser/ash/file_system_provider|' \
                   'chrome/browser/chromeos/extensions/file_manager|' \
                   'chrome/browser/chromeos/extensions/file_system_provider|' \
                   'chrome/browser/chromeos/file_manager|' \
@@ -1003,7 +1004,8 @@
       'filepath': 'third_party/freetype/README.chromium',
     },
     'fsp': {
-      'filepath': 'chrome/browser/chromeos/extensions/file_system_provider|' \
+      'filepath': 'chrome/browser/ash/file_system_provider|' \
+                  'chrome/browser/chromeos/extensions/file_system_provider|' \
                   'chrome/browser/chromeos/file_system_provider|' \
                   'chrome/test/data/extensions/api_test/file_system_provider'
     },
@@ -1685,7 +1687,8 @@
                   '|chromeos/components/proximity_auth/'
     },
     'smb': {
-      'filepath': 'chrome/browser/chromeos/file_system_provider'\
+      'filepath': 'chrome/browser/ash/file_system_provider' \
+                  '|chrome/browser/chromeos/file_system_provider'\
                   '|chrome/browser/chromeos/smb_client'\
                   '|chromeos/dbus/fake_smb_provider_client'\
                   '|chromeos/dbus/smb_provider_client',
diff --git a/ash/BUILD.gn b/ash/BUILD.gn
index ad12c07..2f12c94 100644
--- a/ash/BUILD.gn
+++ b/ash/BUILD.gn
@@ -2615,6 +2615,7 @@
     ":*",
     "//ash/app_list:*",
     "//ash/shortcut_viewer:*",
+    "//chrome/test:test_support",
     "//components/exo:*",
     "//components/exo/wayland:*",
     "//ash/public/cpp/external_arc:*",
diff --git a/ash/ambient/ui/ambient_background_image_view.cc b/ash/ambient/ui/ambient_background_image_view.cc
index 516645b..43e5b712 100644
--- a/ash/ambient/ui/ambient_background_image_view.cc
+++ b/ash/ambient/ui/ambient_background_image_view.cc
@@ -181,7 +181,7 @@
       AddChildView(std::make_unique<AmbientInfoView>(delegate_));
 
   gfx::Insets shadow_insets =
-      gfx::ShadowValue::GetMargin(ambient::util::GetTextShadowValues());
+      gfx::ShadowValue::GetMargin(ambient::util::GetTextShadowValues(nullptr));
 
   // Inits the media string view. The media string view is positioned on the
   // right-top corner of the container.
diff --git a/ash/ambient/ui/ambient_info_view.cc b/ash/ambient/ui/ambient_info_view.cc
index c720a39..abdacb9 100644
--- a/ash/ambient/ui/ambient_info_view.cc
+++ b/ash/ambient/ui/ambient_info_view.cc
@@ -40,6 +40,12 @@
 
 AmbientInfoView::~AmbientInfoView() = default;
 
+void AmbientInfoView::OnThemeChanged() {
+  views::View::OnThemeChanged();
+  details_label_->SetShadows(
+      ambient::util::GetTextShadowValues(GetNativeTheme()));
+}
+
 void AmbientInfoView::UpdateImageDetails(const std::u16string& details) {
   details_label_->SetText(details);
 }
@@ -51,7 +57,7 @@
 
 void AmbientInfoView::InitLayout() {
   gfx::Insets shadow_insets =
-      gfx::ShadowValue::GetMargin(ambient::util::GetTextShadowValues());
+      gfx::ShadowValue::GetMargin(ambient::util::GetTextShadowValues(nullptr));
 
   // Full screen view with the glanceable info view and details label in the
   // lower left.
@@ -79,7 +85,6 @@
   details_label_->SetFontList(
       ambient::util::GetDefaultFontlist().DeriveWithSizeDelta(
           kDetailsFontSizeDip - kDefaultFontSizeDip));
-  details_label_->SetShadows(ambient::util::GetTextShadowValues());
   details_label_->SetPaintToLayer();
   details_label_->layer()->SetFillsBoundsOpaquely(false);
 }
diff --git a/ash/ambient/ui/ambient_info_view.h b/ash/ambient/ui/ambient_info_view.h
index 1d17d65..81f51c22 100644
--- a/ash/ambient/ui/ambient_info_view.h
+++ b/ash/ambient/ui/ambient_info_view.h
@@ -31,6 +31,9 @@
   AmbientInfoView& operator=(AmbientInfoView&) = delete;
   ~AmbientInfoView() override;
 
+  // views::View
+  void OnThemeChanged() override;
+
   void UpdateImageDetails(const std::u16string& details);
 
   void SetTextTransform(const gfx::Transform& transform);
diff --git a/ash/ambient/ui/glanceable_info_view.cc b/ash/ambient/ui/glanceable_info_view.cc
index da44d8f..858dcb4 100644
--- a/ash/ambient/ui/glanceable_info_view.cc
+++ b/ash/ambient/ui/glanceable_info_view.cc
@@ -91,6 +91,14 @@
   Show();
 }
 
+void GlanceableInfoView::OnThemeChanged() {
+  views::View::OnThemeChanged();
+  gfx::ShadowValues text_shadow_values =
+      ambient::util::GetTextShadowValues(GetNativeTheme());
+  time_view_->SetTextShadowValues(text_shadow_values);
+  temperature_->SetShadows(text_shadow_values);
+}
+
 void GlanceableInfoView::Show() {
   AmbientBackendModel* ambient_backend_model =
       delegate_->GetAmbientBackendModel();
@@ -133,8 +141,8 @@
   layout->set_main_axis_alignment(views::BoxLayout::MainAxisAlignment::kStart);
   layout->set_cross_axis_alignment(views::BoxLayout::CrossAxisAlignment::kEnd);
 
-  gfx::ShadowValues text_shadow_values = ambient::util::GetTextShadowValues();
-  gfx::Insets shadow_insets = gfx::ShadowValue::GetMargin(text_shadow_values);
+  gfx::Insets shadow_insets =
+      gfx::ShadowValue::GetMargin(ambient::util::GetTextShadowValues(nullptr));
 
   // Inits the time view.
   time_view_ = AddChildView(std::make_unique<tray::TimeView>(
@@ -145,7 +153,6 @@
       ambient::util::GetContentLayerColor(
           AshColorProvider::ContentLayerType::kTextColorPrimary),
       /*auto_color_readability_enabled=*/false);
-  time_view_->SetTextShadowValues(text_shadow_values);
   // Remove the internal spacing in `time_view_` and adjust spacing for shadows.
   time_view_->SetBorder(views::CreateEmptyBorder(
       -kUnifiedTrayTextTopPadding, -kUnifiedTrayTimeLeftPadding, 0,
@@ -168,7 +175,6 @@
   temperature_->SetEnabledColor(ambient::util::GetContentLayerColor(
       AshColorProvider::ContentLayerType::kTextColorPrimary));
   temperature_->SetFontList(GetWeatherTemperatureFontList());
-  temperature_->SetShadows(text_shadow_values);
   temperature_->SetBorder(views::CreateEmptyBorder(
       0, 0, GetTimeFontDescent() - GetTemperatureFontDescent(), 0));
 }
diff --git a/ash/ambient/ui/glanceable_info_view.h b/ash/ambient/ui/glanceable_info_view.h
index a00f2c78..26a1ddc 100644
--- a/ash/ambient/ui/glanceable_info_view.h
+++ b/ash/ambient/ui/glanceable_info_view.h
@@ -34,6 +34,9 @@
   GlanceableInfoView& operator=(const GlanceableInfoView&) = delete;
   ~GlanceableInfoView() override;
 
+  // views::View:
+  void OnThemeChanged() override;
+
   // AmbientBackendModelObserver:
   void OnWeatherInfoUpdated() override;
 
diff --git a/ash/ambient/ui/media_string_view.cc b/ash/ambient/ui/media_string_view.cc
index af1d2b69..0d80c4c 100644
--- a/ash/ambient/ui/media_string_view.cc
+++ b/ash/ambient/ui/media_string_view.cc
@@ -125,6 +125,10 @@
 
 MediaStringView::~MediaStringView() = default;
 
+void MediaStringView::OnThemeChanged() {
+  views::View::OnThemeChanged();
+  media_text_->SetShadows(ambient::util::GetTextShadowValues(GetNativeTheme()));
+}
 void MediaStringView::OnViewBoundsChanged(views::View* observed_view) {
   UpdateMaskLayer();
 }
@@ -241,10 +245,8 @@
           .DeriveWithSizeDelta(kMediaStringFontSizeDip - kDefaultFontSizeDip)
           .DeriveWithWeight(gfx::Font::Weight::MEDIUM));
   media_text_->SetElideBehavior(gfx::ElideBehavior::NO_ELIDE);
-
-  auto shadow_values = ambient::util::GetTextShadowValues();
-  media_text_->SetShadows(shadow_values);
-  gfx::Insets shadow_insets = gfx::ShadowValue::GetMargin(shadow_values);
+  gfx::Insets shadow_insets =
+      gfx::ShadowValue::GetMargin(ambient::util::GetTextShadowValues(nullptr));
   // Compensate the shadow insets to put the text middle align with the icon.
   media_text_->SetBorder(views::CreateEmptyBorder(
       /*top=*/-shadow_insets.bottom(),
@@ -321,7 +323,7 @@
     // Desired speed is 10 seconds for kMediaStringMaxWidthDip.
     const int text_width = media_text_->GetPreferredSize().width();
     const int shadow_width =
-        gfx::ShadowValue::GetMargin(ambient::util::GetTextShadowValues())
+        gfx::ShadowValue::GetMargin(ambient::util::GetTextShadowValues(nullptr))
             .width();
     const int start_x = text_layer->GetTargetTransform().To2dTranslation().x();
     const int end_x = -(text_width + shadow_width) / 2;
diff --git a/ash/ambient/ui/media_string_view.h b/ash/ambient/ui/media_string_view.h
index cce0abc..e98e973 100644
--- a/ash/ambient/ui/media_string_view.h
+++ b/ash/ambient/ui/media_string_view.h
@@ -41,6 +41,9 @@
   MediaStringView& operator=(const MediaStringView&) = delete;
   ~MediaStringView() override;
 
+  // views::View:
+  void OnThemeChanged() override;
+
   // views::ViewObserver:
   void OnViewBoundsChanged(views::View* observed_view) override;
 
diff --git a/ash/ambient/util/ambient_util.cc b/ash/ambient/util/ambient_util.cc
index 085920f..034c15e21 100644
--- a/ash/ambient/util/ambient_util.cc
+++ b/ash/ambient/util/ambient_util.cc
@@ -12,6 +12,7 @@
 #include "third_party/skia/include/core/SkColor.h"
 #include "ui/gfx/color_palette.h"
 #include "ui/gfx/shadow_value.h"
+#include "ui/native_theme/native_theme.h"
 
 namespace ash {
 namespace ambient {
@@ -19,7 +20,6 @@
 
 // Appearance of the text shadow.
 constexpr int kTextShadowElevation = 2;
-constexpr SkColor kTextShadowColor = gfx::kGoogleGrey800;
 
 bool IsShowing(LockScreen::ScreenType type) {
   return LockScreen::HasInstance() && LockScreen::Get()->screen_type() == type;
@@ -49,9 +49,20 @@
   return *font_list;
 }
 
-gfx::ShadowValues GetTextShadowValues() {
-  return gfx::ShadowValue::MakeShadowValues(kTextShadowElevation,
-                                            kTextShadowColor);
+gfx::ShadowValues GetTextShadowValues(const ui::NativeTheme* theme) {
+  // If the theme does not exist the shadow values are being created in
+  // order to calculate margins. In that case the color plays no role so set it
+  // to gfx::kPlaceholderColor.
+  // Currently an elevation of 2 falls back to MakeMdShadowValues so use
+  // kColorId_ShadowBase, which is the base shadow color for MdShadowValues,
+  // until MakeMdShadowValues is refactored to take in it’s own
+  // |key_shadow_color| and |ambient_shadow_color|.
+  // TODO(elainechien): crbug.com/1056950
+  SkColor shadow_base_color =
+      theme ? theme->GetSystemColor(ui::NativeTheme::kColorId_ShadowBase)
+            : gfx::kPlaceholderColor;
+  return gfx::ShadowValue::MakeShadowValues(
+      kTextShadowElevation, shadow_base_color, shadow_base_color);
 }
 
 bool IsAmbientModeTopicTypeAllowed(AmbientModeTopicType topic_type) {
diff --git a/ash/ambient/util/ambient_util.h b/ash/ambient/util/ambient_util.h
index 5f37332..6cd2d0d 100644
--- a/ash/ambient/util/ambient_util.h
+++ b/ash/ambient/util/ambient_util.h
@@ -12,6 +12,10 @@
 #include "ui/gfx/font_list.h"
 #include "ui/gfx/shadow_value.h"
 
+namespace ui {
+class NativeTheme;
+}
+
 namespace ash {
 
 namespace ambient {
@@ -29,8 +33,10 @@
 // Returns the default fontlist for Ambient Mode.
 ASH_EXPORT const gfx::FontList& GetDefaultFontlist();
 
-// Returns the default static text shadow for Ambient Mode.
-ASH_EXPORT gfx::ShadowValues GetTextShadowValues();
+// Returns the default static text shadow for Ambient Mode. |theme| can be a
+// nullptr if the ShadowValues returned are only used to calculate margins, in
+// which kPlaceholderColor will be used for the shadow color.
+ASH_EXPORT gfx::ShadowValues GetTextShadowValues(const ui::NativeTheme* theme);
 
 ASH_EXPORT bool IsAmbientModeTopicTypeAllowed(AmbientModeTopicType topic);
 
diff --git a/ash/app_list/app_list_controller_impl.cc b/ash/app_list/app_list_controller_impl.cc
index c78ca0a6..73ffeb4c 100644
--- a/ash/app_list/app_list_controller_impl.cc
+++ b/ash/app_list/app_list_controller_impl.cc
@@ -55,6 +55,7 @@
 #include "ash/wm/window_util.h"
 #include "base/barrier_closure.h"
 #include "base/callback_helpers.h"
+#include "base/containers/contains.h"
 #include "base/logging.h"
 #include "base/metrics/histogram_macros.h"
 #include "base/metrics/user_metrics.h"
diff --git a/ash/app_list/app_list_presenter_impl.cc b/ash/app_list/app_list_presenter_impl.cc
index 41d60fca..7e19a41 100644
--- a/ash/app_list/app_list_presenter_impl.cc
+++ b/ash/app_list/app_list_presenter_impl.cc
@@ -23,6 +23,7 @@
 #include "ash/wm/container_finder.h"
 #include "base/bind.h"
 #include "base/callback_helpers.h"
+#include "base/containers/contains.h"
 #include "base/metrics/histogram_macros.h"
 #include "base/metrics/user_metrics.h"
 #include "base/optional.h"
diff --git a/ash/app_list/model/search/search_result.h b/ash/app_list/model/search/search_result.h
index 7c66ca83..1939da1 100644
--- a/ash/app_list/model/search/search_result.h
+++ b/ash/app_list/model/search/search_result.h
@@ -132,11 +132,6 @@
     metadata_->position_priority = position_priority;
   }
 
-  int result_subtype() const { return metadata_->result_subtype; }
-  void set_result_subtype(int result_subtype) {
-    metadata_->result_subtype = result_subtype;
-  }
-
   const Actions& actions() const { return metadata_->actions; }
   void SetActions(const Actions& sets);
 
diff --git a/ash/app_list/views/search_result_view.cc b/ash/app_list/views/search_result_view.cc
index d91ee5e..7167a01 100644
--- a/ash/app_list/views/search_result_view.cc
+++ b/ash/app_list/views/search_result_view.cc
@@ -79,6 +79,22 @@
   }
 };
 
+int OmniboxDisplayTypeToDimension(const SearchResultOmniboxDisplayType type) {
+  const auto& config = SharedAppListConfig::instance();
+  switch (type) {
+    case SearchResultOmniboxDisplayType::kAnswer:
+    case SearchResultOmniboxDisplayType::kCalculatorAnswer:
+      return config.search_list_answer_icon_dimension();
+    case SearchResultOmniboxDisplayType::kRichImage:
+      return config.search_list_image_icon_dimension();
+    case SearchResultOmniboxDisplayType::kFavicon:
+      return config.search_list_favicon_dimension();
+    case SearchResultOmniboxDisplayType::kDefault:
+    default:
+      return config.search_list_icon_dimension();
+  }
+}
+
 }  // namespace
 
 // static
@@ -406,42 +422,23 @@
   // clearing it out. It should work correctly as long as the SearchResult does
   // not forget to SetIcon when it's ready.
   if (result() && !result()->icon().isNull()) {
-    // TODO(crbug.com/1201151): These nested if/elses can be flattened into one
-    // switch statement pending decisions on rich entity icons.
+    gfx::ImageSkia image = result()->icon();
+
+    // Calculate the image dimensions. Images could be rectangular, and we
+    // should preserve the aspect ratio.
+    const int dimension =
+        OmniboxDisplayTypeToDimension(result()->omnibox_type());
+    const int max = std::max(image.width(), image.height());
+    const bool is_square = image.width() == image.height();
+    const int width = is_square ? dimension : dimension * image.width() / max;
+    const int height = is_square ? dimension : dimension * image.height() / max;
+
     if (IsRichImage()) {
-      gfx::ImageSkia image = result()->icon();
-
-      // Images could be rectangular, and we should preserve the aspect ratio.
-      const int dimension =
-          SharedAppListConfig::instance().search_list_image_icon_dimension();
-      int width = image.width();
-      int height = image.height();
-      if (width != height) {
-        const int max = std::max(width, height);
-        width = dimension * width / max;
-        height = dimension * height / max;
-        SetIconImage(image, image_icon_, gfx::Size(width, height));
-      } else {
-        SetIconImage(image, image_icon_, gfx::Size(dimension, dimension));
-      }
-
+      SetIconImage(image, image_icon_, gfx::Size(width, height));
       icon_->SetVisible(false);
       image_icon_->SetVisible(true);
     } else {
-      int dimension;
-      if (IsAnswer()) {
-        dimension =
-            SharedAppListConfig::instance().search_list_answer_icon_dimension();
-      } else if (result()->omnibox_type() ==
-                 SearchResultOmniboxDisplayType::kFavicon) {
-        dimension =
-            SharedAppListConfig::instance().search_list_favicon_dimension();
-      } else {
-        dimension =
-            SharedAppListConfig::instance().search_list_icon_dimension();
-      }
-
-      SetIconImage(result()->icon(), icon_, gfx::Size(dimension, dimension));
+      SetIconImage(image, icon_, gfx::Size(width, height));
       icon_->SetVisible(true);
       image_icon_->SetVisible(false);
     }
@@ -566,13 +563,6 @@
   source->RequestFocus();
 }
 
-bool SearchResultView::IsAnswer() const {
-  return app_list_features::IsOmniboxRichEntitiesEnabled() && result() &&
-         (result()->omnibox_type() == SearchResultOmniboxDisplayType::kAnswer ||
-          result()->omnibox_type() ==
-              SearchResultOmniboxDisplayType::kCalculatorAnswer);
-}
-
 bool SearchResultView::IsRichImage() const {
   return app_list_features::IsOmniboxRichEntitiesEnabled() && result() &&
          result()->omnibox_type() == SearchResultOmniboxDisplayType::kRichImage;
diff --git a/ash/app_list/views/search_result_view.h b/ash/app_list/views/search_result_view.h
index a5655bd..fddaabc 100644
--- a/ash/app_list/views/search_result_view.h
+++ b/ash/app_list/views/search_result_view.h
@@ -106,8 +106,7 @@
   // Invoked when the context menu closes.
   void OnMenuClosed();
 
-  // Whether this result is one of the rich entity types.
-  bool IsAnswer() const;
+  // Whether this result has a rich image icon.
   bool IsRichImage() const;
 
   // Parent list view. Owned by views hierarchy.
diff --git a/ash/clipboard/views/clipboard_history_bitmap_item_view.cc b/ash/clipboard/views/clipboard_history_bitmap_item_view.cc
index 8e8296c..df18376 100644
--- a/ash/clipboard/views/clipboard_history_bitmap_item_view.cc
+++ b/ash/clipboard/views/clipboard_history_bitmap_item_view.cc
@@ -11,6 +11,7 @@
 #include "ash/clipboard/views/clipboard_history_view_constants.h"
 #include "ash/style/ash_color_provider.h"
 #include "ash/style/scoped_light_mode_as_default.h"
+#include "base/containers/contains.h"
 #include "base/time/time.h"
 #include "third_party/skia/include/core/SkBitmap.h"
 #include "ui/base/l10n/l10n_util.h"
diff --git a/ash/content/common/resources/BUILD.gn b/ash/content/common/resources/BUILD.gn
index 0cd4971..fdf3417 100644
--- a/ash/content/common/resources/BUILD.gn
+++ b/ash/content/common/resources/BUILD.gn
@@ -4,15 +4,24 @@
 
 import("//third_party/closure_compiler/compile_js.gni")
 import("//tools/grit/grit_rule.gni")
+import("//tools/grit/preprocess_if_expr.gni")
 import("//tools/grit/repack.gni")
+import("//tools/polymer/html_to_js.gni")
 import("//ui/webui/resources/tools/generate_grd.gni")
 
+preprocessed_dir = "preprocessed"
+preprocessed_gen_manifest = "preprocessed_gen_manifest.json"
+
+polymer_element_files = [ "navigation_selector.js" ]
+
 generate_grd("build_grdp") {
   input_files = [
     "fake_observables.js",
     "fake_method_resolver.js",
   ]
   input_files_base_dir = rebase_path(".", "//")
+  deps = [ ":preprocess_generated" ]
+  manifest_files = [ "$target_gen_dir/$preprocessed_gen_manifest" ]
   grd_prefix = "ash_common"
   out_grd = "$target_gen_dir/${grd_prefix}_resources.grdp"
   resource_path_prefix = "ash/common"
@@ -23,6 +32,7 @@
   deps = [
     ":fake_method_resolver",
     ":fake_observables",
+    ":navigation_selector",
   ]
 }
 
@@ -33,3 +43,21 @@
 js_library("fake_method_resolver") {
   deps = [ "//ui/webui/resources/js:cr.m" ]
 }
+
+js_library("navigation_selector") {
+  deps = [
+    "//third_party/polymer/v3_0/components-chromium/polymer:polymer_bundled",
+  ]
+}
+
+preprocess_if_expr("preprocess_generated") {
+  deps = [ ":web_components" ]
+  in_folder = target_gen_dir
+  out_folder = "$target_gen_dir/$preprocessed_dir"
+  out_manifest = "$target_gen_dir/$preprocessed_gen_manifest"
+  in_files = polymer_element_files
+}
+
+html_to_js("web_components") {
+  js_files = polymer_element_files
+}
diff --git a/ash/content/common/resources/navigation_selector.html b/ash/content/common/resources/navigation_selector.html
new file mode 100644
index 0000000..409562a
--- /dev/null
+++ b/ash/content/common/resources/navigation_selector.html
@@ -0,0 +1,27 @@
+<style include="cr-shared-style"></style>
+
+<div id="navigationSelectorComponent">
+  <div id="navigationSelectorMenu">
+    <template id="menuItems" is="dom-repeat" items="{{menuItems}}">
+      <template is="dom-if" if="[[!isCollapsible_(item)]]">
+        <div class="navigation-item" on-click="onSelected_">
+          [[item.selectorItem.name]]
+        </div>
+      </template>
+      <template is="dom-if" if="[[isCollapsible_(item)]]">
+        <cr-expand-button class="expand-button" on-click="onExpandClicked_">
+          [[item.selectorItem.name]]
+        </cr-expand-button>
+        <iron-collapse opened="[[item.properties.isExpanded]]">
+          <template class="collapsed-list" is="dom-repeat"
+              items="{{item.properties.subMenuItems}}">
+            <div class="navigation-item nested-item"
+                on-click="onNestedSelected_">
+              [[item.name]]
+            </div>
+          </template>
+        </iron-collapse>
+      </template>
+    </template>
+  </div>
+</div>
\ No newline at end of file
diff --git a/ash/content/common/resources/navigation_selector.js b/ash/content/common/resources/navigation_selector.js
new file mode 100644
index 0000000..258424d3
--- /dev/null
+++ b/ash/content/common/resources/navigation_selector.js
@@ -0,0 +1,113 @@
+// 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 {html, PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
+
+import 'chrome://resources/cr_elements/cr_expand_button/cr_expand_button.m.js';
+import 'chrome://resources/cr_elements/shared_style_css.m.js';
+import 'chrome://resources/polymer/v3_0/iron-collapse/iron-collapse.js';
+
+/**
+ * @typedef {{
+ *   name: string,
+ *   pageIs: string,
+ * }}
+ */
+export let SelectorItem;
+
+/**
+ * @typedef {{
+ *   isCollapsible: boolean,
+ *   isExpanded: boolean,
+ *   subMenuItems: ?Array<?SelectorItem>
+ * }}
+ */
+export let SelectorProperties;
+
+/**
+ * @typedef {{
+ *   selectorItem: !SelectorItem,
+ *   properties: !SelectorProperties,
+ * }}
+ */
+export let MenuSelectorItem;
+
+/**
+ * @fileoverview
+ * 'navigation-selector' is a side bar navigation element. To populate the
+ * navigation-selector you must provide the element with an array of
+ * MenuSelectorItem's.
+ */
+export class NavigationSelectorElement extends PolymerElement {
+  static get is() {
+    return 'navigation-selector';
+  }
+
+  static get template() {
+    return html`{__html_template__}`;
+  }
+
+  static get properties() {
+    return {
+      /**
+       * @type {?SelectorItem}
+       * Notifies parent elements of the the selected item.
+       */
+      selectedItem: {
+        type: Object,
+        value: null,
+        notify: true,
+      },
+
+      /**
+       * @type {!Array<!MenuSelectorItem>}
+       */
+      menuItems: {
+        type: Array,
+        value: () => [],
+      }
+    }
+  }
+
+  /**
+   * @param {Event} e
+   * @private
+   */
+  onSelected_(e) {
+    this.selectedItem = e.model.item.selectorItem;
+  }
+
+  /**
+   * @param {Event} e
+   * @private
+   */
+  onNestedSelected_(e) {
+    this.selectedItem = e.model.item;
+  }
+
+  /**
+   * @param {Event} e
+   * @private
+   */
+  onExpandClicked_(e) {
+    const selectedMenuItem = e.model.item;
+    const foundIndex = this.menuItems.findIndex((element) => {
+      return element.selectorItem.name === selectedMenuItem.selectorItem.name;
+    });
+
+    this.menuItems[foundIndex].properties.isExpanded =
+        !this.menuItems[foundIndex].properties.isExpanded;
+    this.notifyPath(`menuItems.${foundIndex}.properties.isExpanded`);
+  }
+
+  /**
+   * @param {!MenuSelectorItem} item
+   * @private
+   */
+  isCollapsible_(item) {
+    return item.properties.isCollapsible;
+  }
+}
+
+customElements.define(NavigationSelectorElement.is, NavigationSelectorElement);
\ No newline at end of file
diff --git a/ash/display/cros_display_config.cc b/ash/display/cros_display_config.cc
index 0d990e9..2b9f5a4 100644
--- a/ash/display/cros_display_config.cc
+++ b/ash/display/cros_display_config.cc
@@ -22,6 +22,7 @@
 #include "ash/touch/ash_touch_transform_controller.h"
 #include "ash/wm/tablet_mode/tablet_mode_controller.h"
 #include "base/bind.h"
+#include "base/containers/contains.h"
 #include "base/optional.h"
 #include "base/strings/string_number_conversions.h"
 #include "mojo/public/cpp/bindings/associated_remote.h"
diff --git a/ash/display/display_manager_unittest.cc b/ash/display/display_manager_unittest.cc
index 155c1b9..96efaf3 100644
--- a/ash/display/display_manager_unittest.cc
+++ b/ash/display/display_manager_unittest.cc
@@ -4535,6 +4535,13 @@
               display::Screen::GetScreen()->GetPrimaryDisplay().size());
     EXPECT_EQ(expected, Shell::GetPrimaryRootWindow()->bounds().size());
   }
+  UpdateDisplay(base::StringPrintf("2160x1440*%s", display::kDsfStr_1_8));
+  {
+    gfx::Size expected(1200, 800);
+    EXPECT_EQ(expected,
+              display::Screen::GetScreen()->GetPrimaryDisplay().size());
+    EXPECT_EQ(expected, Shell::GetPrimaryRootWindow()->bounds().size());
+  }
 }
 
 TEST_F(DisplayManagerTest, PanelOrientation) {
diff --git a/ash/ime/ime_controller_impl.cc b/ash/ime/ime_controller_impl.cc
index b46df87f..1cfb421 100644
--- a/ash/ime/ime_controller_impl.cc
+++ b/ash/ime/ime_controller_impl.cc
@@ -12,6 +12,7 @@
 #include "ash/shell.h"
 #include "ash/system/tray/system_tray_notifier.h"
 #include "base/callback_helpers.h"
+#include "base/containers/contains.h"
 #include "base/metrics/histogram_macros.h"
 #include "ui/base/accelerators/accelerator.h"
 #include "ui/base/ime/chromeos/extension_ime_util.h"
diff --git a/ash/login/ui/lock_screen_media_controls_view.cc b/ash/login/ui/lock_screen_media_controls_view.cc
index 7accc4a..07f6873 100644
--- a/ash/login/ui/lock_screen_media_controls_view.cc
+++ b/ash/login/ui/lock_screen_media_controls_view.cc
@@ -15,6 +15,7 @@
 #include "ash/strings/grit/ash_strings.h"
 #include "ash/style/ash_color_provider.h"
 #include "base/bind.h"
+#include "base/containers/contains.h"
 #include "base/metrics/histogram_functions.h"
 #include "base/power_monitor/power_monitor.h"
 #include "base/threading/thread_task_runner_handle.h"
diff --git a/ash/public/cpp/app_list/app_list_types.h b/ash/public/cpp/app_list/app_list_types.h
index ac5b818..c4a392e 100644
--- a/ash/public/cpp/app_list/app_list_types.h
+++ b/ash/public/cpp/app_list/app_list_types.h
@@ -288,13 +288,6 @@
   // The type of this result.
   AppListSearchResultType result_type = AppListSearchResultType::kUnknown;
 
-  // The subtype of this result. Derived search result classes can use this to
-  // represent their own subtypes. Currently, OmniboxResult sets this to
-  // indicate this is a history result, previous query, etc. If a result is an
-  // Answer, OmniboxResult will set this to be the answer type. A value of -1
-  // indicates no subtype has been set.
-  int result_subtype = -1;
-
   // A search result type used for metrics.
   ash::SearchResultType metrics_type = ash::SEARCH_RESULT_TYPE_BOUNDARY;
 
diff --git a/ash/public/cpp/holding_space/holding_space_model.cc b/ash/public/cpp/holding_space/holding_space_model.cc
index a078bfb3..4a46e4e 100644
--- a/ash/public/cpp/holding_space/holding_space_model.cc
+++ b/ash/public/cpp/holding_space/holding_space_model.cc
@@ -8,6 +8,7 @@
 
 #include "ash/public/cpp/holding_space/holding_space_model_observer.h"
 #include "base/check.h"
+#include "base/containers/contains.h"
 
 namespace ash {
 
diff --git a/ash/system/holding_space/holding_space_item_view_delegate.cc b/ash/system/holding_space/holding_space_item_view_delegate.cc
index 1b78df0b..175d913 100644
--- a/ash/system/holding_space/holding_space_item_view_delegate.cc
+++ b/ash/system/holding_space/holding_space_item_view_delegate.cc
@@ -17,6 +17,7 @@
 #include "ash/system/holding_space/holding_space_tray_bubble.h"
 #include "base/bind.h"
 #include "base/callback_helpers.h"
+#include "base/containers/contains.h"
 #include "net/base/mime_util.h"
 #include "ui/accessibility/ax_action_data.h"
 #include "ui/accessibility/ax_enums.mojom.h"
diff --git a/ash/system/holding_space/holding_space_item_views_section.cc b/ash/system/holding_space/holding_space_item_views_section.cc
index ffccd38..8024d5e4 100644
--- a/ash/system/holding_space/holding_space_item_views_section.cc
+++ b/ash/system/holding_space/holding_space_item_views_section.cc
@@ -12,6 +12,7 @@
 #include "ash/system/holding_space/holding_space_util.h"
 #include "base/auto_reset.h"
 #include "base/callback_helpers.h"
+#include "base/containers/contains.h"
 #include "ui/compositor/callback_layer_animation_observer.h"
 #include "ui/compositor/layer.h"
 #include "ui/compositor/layer_animation_observer.h"
diff --git a/ash/system/holding_space/holding_space_tray.cc b/ash/system/holding_space/holding_space_tray.cc
index 282d4cd..2e89345 100644
--- a/ash/system/holding_space/holding_space_tray.cc
+++ b/ash/system/holding_space/holding_space_tray.cc
@@ -27,6 +27,7 @@
 #include "base/callback_helpers.h"
 #include "base/check.h"
 #include "base/containers/adapters.h"
+#include "base/containers/contains.h"
 #include "base/pickle.h"
 #include "components/prefs/pref_change_registrar.h"
 #include "ui/aura/client/drag_drop_client.h"
diff --git a/ash/system/holding_space/holding_space_tray_icon.cc b/ash/system/holding_space/holding_space_tray_icon.cc
index 97e5f09..896682ee 100644
--- a/ash/system/holding_space/holding_space_tray_icon.cc
+++ b/ash/system/holding_space/holding_space_tray_icon.cc
@@ -17,6 +17,7 @@
 #include "ash/system/tray/tray_constants.h"
 #include "base/barrier_closure.h"
 #include "base/containers/adapters.h"
+#include "base/containers/contains.h"
 #include "base/containers/unique_ptr_adapters.h"
 #include "base/i18n/rtl.h"
 #include "base/stl_util.h"
diff --git a/ash/system/ime_menu/ime_menu_tray_unittest.cc b/ash/system/ime_menu/ime_menu_tray_unittest.cc
index b0976dce..c253477 100644
--- a/ash/system/ime_menu/ime_menu_tray_unittest.cc
+++ b/ash/system/ime_menu/ime_menu_tray_unittest.cc
@@ -13,6 +13,7 @@
 #include "ash/system/status_area_widget.h"
 #include "ash/system/status_area_widget_test_helper.h"
 #include "ash/test/ash_test_base.h"
+#include "base/containers/contains.h"
 #include "base/run_loop.h"
 #include "base/strings/utf_string_conversions.h"
 #include "ui/accessibility/ax_enums.mojom.h"
diff --git a/ash/system/media/unified_media_controls_view.cc b/ash/system/media/unified_media_controls_view.cc
index a083c735..f878db6 100644
--- a/ash/system/media/unified_media_controls_view.cc
+++ b/ash/system/media/unified_media_controls_view.cc
@@ -10,6 +10,7 @@
 #include "ash/system/media/unified_media_controls_controller.h"
 #include "ash/system/tray/tray_constants.h"
 #include "ash/system/tray/tray_popup_utils.h"
+#include "base/containers/contains.h"
 #include "components/media_message_center/media_notification_util.h"
 #include "components/vector_icons/vector_icons.h"
 #include "ui/base/l10n/l10n_util.h"
diff --git a/ash/system/message_center/session_state_notification_blocker.cc b/ash/system/message_center/session_state_notification_blocker.cc
index 1456c86..0041ce6 100644
--- a/ash/system/message_center/session_state_notification_blocker.cc
+++ b/ash/system/message_center/session_state_notification_blocker.cc
@@ -8,6 +8,7 @@
 #include "ash/shell.h"
 #include "ash/system/message_center/ash_message_center_lock_screen_controller.h"
 #include "ash/system/power/battery_notification.h"
+#include "base/containers/contains.h"
 #include "ui/message_center/message_center.h"
 #include "ui/message_center/public/cpp/notification.h"
 #include "ui/message_center/public/cpp/notification_types.h"
diff --git a/ash/system/network/tray_network_state_model.cc b/ash/system/network/tray_network_state_model.cc
index cc71a93..7ea4006 100644
--- a/ash/system/network/tray_network_state_model.cc
+++ b/ash/system/network/tray_network_state_model.cc
@@ -11,6 +11,7 @@
 #include "ash/system/network/vpn_list.h"
 #include "base/bind.h"
 #include "base/callback_helpers.h"
+#include "base/containers/contains.h"
 #include "base/location.h"
 #include "chromeos/services/network_config/public/cpp/cros_network_config_util.h"
 #include "chromeos/services/network_config/public/mojom/cros_network_config.mojom.h"
diff --git a/ash/wm/desks/desk_mini_view_animations.cc b/ash/wm/desks/desk_mini_view_animations.cc
index d1c5230c..89cb2bc4 100644
--- a/ash/wm/desks/desk_mini_view_animations.cc
+++ b/ash/wm/desks/desk_mini_view_animations.cc
@@ -13,6 +13,7 @@
 #include "ash/wm/desks/zero_state_button.h"
 #include "ash/wm/overview/overview_controller.h"
 #include "ash/wm/overview/overview_session.h"
+#include "base/containers/contains.h"
 #include "ui/compositor/layer.h"
 #include "ui/compositor/layer_animation_observer.h"
 #include "ui/compositor/layer_animator.h"
diff --git a/ash/wm/desks/desk_preview_view.cc b/ash/wm/desks/desk_preview_view.cc
index b32dd01..f6764f5 100644
--- a/ash/wm/desks/desk_preview_view.cc
+++ b/ash/wm/desks/desk_preview_view.cc
@@ -21,6 +21,7 @@
 #include "ash/wm/workspace/backdrop_controller.h"
 #include "ash/wm/workspace/workspace_layout_manager.h"
 #include "ash/wm/workspace_controller.h"
+#include "base/containers/contains.h"
 #include "base/containers/flat_map.h"
 #include "base/containers/flat_set.h"
 #include "base/numerics/ranges.h"
diff --git a/ash/wm/full_restore/full_restore_controller.cc b/ash/wm/full_restore/full_restore_controller.cc
index 3d60e5e..bc7d72b 100644
--- a/ash/wm/full_restore/full_restore_controller.cc
+++ b/ash/wm/full_restore/full_restore_controller.cc
@@ -17,6 +17,7 @@
 #include "ash/wm/wm_event.h"
 #include "base/auto_reset.h"
 #include "base/check_op.h"
+#include "base/containers/contains.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "components/full_restore/full_restore_utils.h"
 #include "components/prefs/pref_service.h"
diff --git a/ash/wm/full_restore/full_restore_controller_unittest.cc b/ash/wm/full_restore/full_restore_controller_unittest.cc
index 227839b..245e712 100644
--- a/ash/wm/full_restore/full_restore_controller_unittest.cc
+++ b/ash/wm/full_restore/full_restore_controller_unittest.cc
@@ -17,6 +17,7 @@
 #include "ash/wm/splitview/split_view_controller.h"
 #include "ash/wm/tablet_mode/tablet_mode_controller_test_api.h"
 #include "ash/wm/window_state.h"
+#include "base/containers/contains.h"
 #include "base/containers/flat_map.h"
 #include "base/scoped_observation.h"
 #include "base/test/scoped_feature_list.h"
diff --git a/ash/wm/gestures/back_gesture/back_gesture_event_handler.cc b/ash/wm/gestures/back_gesture/back_gesture_event_handler.cc
index 2c8df3b..742fcbf 100644
--- a/ash/wm/gestures/back_gesture/back_gesture_event_handler.cc
+++ b/ash/wm/gestures/back_gesture/back_gesture_event_handler.cc
@@ -24,6 +24,7 @@
 #include "ash/wm/window_state.h"
 #include "ash/wm/window_util.h"
 #include "ash/wm/wm_event.h"
+#include "base/containers/contains.h"
 #include "base/i18n/rtl.h"
 #include "base/metrics/user_metrics.h"
 #include "chromeos/ui/base/window_properties.h"
diff --git a/ash/wm/overview/overview_item_view.cc b/ash/wm/overview/overview_item_view.cc
index f316217b..8202f7c 100644
--- a/ash/wm/overview/overview_item_view.cc
+++ b/ash/wm/overview/overview_item_view.cc
@@ -15,6 +15,7 @@
 #include "ash/wm/overview/overview_item.h"
 #include "ash/wm/window_preview_view.h"
 #include "ash/wm/wm_highlight_item_border.h"
+#include "base/containers/contains.h"
 #include "ui/accessibility/ax_enums.mojom.h"
 #include "ui/accessibility/ax_node_data.h"
 #include "ui/aura/window.h"
diff --git a/ash/wm/overview/scoped_overview_hide_windows.cc b/ash/wm/overview/scoped_overview_hide_windows.cc
index 50b496c..580c5e0 100644
--- a/ash/wm/overview/scoped_overview_hide_windows.cc
+++ b/ash/wm/overview/scoped_overview_hide_windows.cc
@@ -4,6 +4,7 @@
 
 #include "ash/wm/overview/scoped_overview_hide_windows.h"
 
+#include "base/containers/contains.h"
 #include "base/notreached.h"
 #include "ui/aura/window.h"
 
diff --git a/ash/wm/session_state_animator_impl.cc b/ash/wm/session_state_animator_impl.cc
index a8a1bb6a..c77e180 100644
--- a/ash/wm/session_state_animator_impl.cc
+++ b/ash/wm/session_state_animator_impl.cc
@@ -14,6 +14,7 @@
 #include "ash/wm/desks/desks_util.h"
 #include "ash/wm/window_animations.h"
 #include "base/barrier_closure.h"
+#include "base/containers/contains.h"
 #include "ui/aura/client/aura_constants.h"
 #include "ui/compositor/layer_animation_observer.h"
 #include "ui/compositor/layer_animation_sequence.h"
diff --git a/ash/wm/tablet_mode/tablet_mode_controller.cc b/ash/wm/tablet_mode/tablet_mode_controller.cc
index 081bdce8..9135bbc5 100644
--- a/ash/wm/tablet_mode/tablet_mode_controller.cc
+++ b/ash/wm/tablet_mode/tablet_mode_controller.cc
@@ -30,6 +30,7 @@
 #include "base/bind.h"
 #include "base/callback.h"
 #include "base/command_line.h"
+#include "base/containers/contains.h"
 #include "base/location.h"
 #include "base/metrics/histogram.h"
 #include "base/metrics/histogram_macros.h"
diff --git a/ash/wm/window_cycle/window_cycle_controller_unittest.cc b/ash/wm/window_cycle/window_cycle_controller_unittest.cc
index 41e61fc..0537d8b 100644
--- a/ash/wm/window_cycle/window_cycle_controller_unittest.cc
+++ b/ash/wm/window_cycle/window_cycle_controller_unittest.cc
@@ -46,6 +46,7 @@
 #include "ash/wm/window_state.h"
 #include "ash/wm/window_util.h"
 #include "ash/wm/wm_event.h"
+#include "base/containers/contains.h"
 #include "base/run_loop.h"
 #include "base/test/metrics/histogram_tester.h"
 #include "base/test/scoped_feature_list.h"
diff --git a/ash/wm/window_util.cc b/ash/wm/window_util.cc
index 6695a42..a1bac11 100644
--- a/ash/wm/window_util.cc
+++ b/ash/wm/window_util.cc
@@ -27,6 +27,7 @@
 #include "ash/wm/window_positioning_utils.h"
 #include "ash/wm/window_state.h"
 #include "ash/wm/wm_event.h"
+#include "base/containers/contains.h"
 #include "chromeos/ui/base/chromeos_ui_constants.h"
 #include "chromeos/ui/frame/interior_resize_handler_targeter.h"
 #include "ui/aura/client/aura_constants.h"
diff --git a/ash/wm/window_util_unittest.cc b/ash/wm/window_util_unittest.cc
index f2d940a..3ad032b 100644
--- a/ash/wm/window_util_unittest.cc
+++ b/ash/wm/window_util_unittest.cc
@@ -8,6 +8,7 @@
 #include "ash/wm/window_positioning_utils.h"
 #include "ash/wm/window_state.h"
 #include "ash/wm/wm_event.h"
+#include "base/containers/contains.h"
 #include "ui/aura/test/test_windows.h"
 #include "ui/aura/window.h"
 #include "ui/display/screen.h"
diff --git a/ash/wm/workspace/workspace_window_resizer.cc b/ash/wm/workspace/workspace_window_resizer.cc
index 81a3829..9fad79b 100644
--- a/ash/wm/workspace/workspace_window_resizer.cc
+++ b/ash/wm/workspace/workspace_window_resizer.cc
@@ -31,6 +31,7 @@
 #include "ash/wm/window_util.h"
 #include "ash/wm/wm_event.h"
 #include "ash/wm/workspace/phantom_window_controller.h"
+#include "base/containers/contains.h"
 #include "base/metrics/user_metrics.h"
 #include "chromeos/ui/base/window_properties.h"
 #include "ui/aura/client/aura_constants.h"
diff --git a/build/fuchsia/linux.sdk.sha1 b/build/fuchsia/linux.sdk.sha1
index 1ceef9c..a3577ad7 100644
--- a/build/fuchsia/linux.sdk.sha1
+++ b/build/fuchsia/linux.sdk.sha1
@@ -1 +1 @@
-4.20210510.2.1
+4.20210510.3.1
diff --git a/build/fuchsia/mac.sdk.sha1 b/build/fuchsia/mac.sdk.sha1
index 1ceef9c..a3577ad7 100644
--- a/build/fuchsia/mac.sdk.sha1
+++ b/build/fuchsia/mac.sdk.sha1
@@ -1 +1 @@
-4.20210510.2.1
+4.20210510.3.1
diff --git a/chrome/VERSION b/chrome/VERSION
index 7a170d41..0788bf23 100644
--- a/chrome/VERSION
+++ b/chrome/VERSION
@@ -1,4 +1,4 @@
 MAJOR=92
 MINOR=0
-BUILD=4504
+BUILD=4505
 PATCH=0
diff --git a/chrome/android/chrome_test_java_sources.gni b/chrome/android/chrome_test_java_sources.gni
index bd83739e..e3d1c5e 100644
--- a/chrome/android/chrome_test_java_sources.gni
+++ b/chrome/android/chrome_test_java_sources.gni
@@ -184,7 +184,7 @@
   "javatests/src/org/chromium/chrome/browser/download/DownloadForegroundServiceManagerTest.java",
   "javatests/src/org/chromium/chrome/browser/download/DownloadForegroundServiceTest.java",
   "javatests/src/org/chromium/chrome/browser/download/DownloadInfoBarControllerTest.java",
-  "javatests/src/org/chromium/chrome/browser/download/DownloadLocationChangeTest.java",
+  "javatests/src/org/chromium/chrome/browser/download/DownloadLocationChangeEnd2EndTest.java",
   "javatests/src/org/chromium/chrome/browser/download/DownloadManagerServiceTest.java",
   "javatests/src/org/chromium/chrome/browser/download/DownloadNotificationServiceTest.java",
   "javatests/src/org/chromium/chrome/browser/download/DownloadTest.java",
@@ -195,6 +195,7 @@
   "javatests/src/org/chromium/chrome/browser/download/SystemDownloadNotifierTest.java",
   "javatests/src/org/chromium/chrome/browser/download/TestDownloadDirectoryProvider.java",
   "javatests/src/org/chromium/chrome/browser/download/dialogs/DownloadLaterDialogTest.java",
+  "javatests/src/org/chromium/chrome/browser/download/dialogs/DownloadLocationDialogTest.java",
   "javatests/src/org/chromium/chrome/browser/explore_sites/ExploreSitesBridgeExperimentalTest.java",
   "javatests/src/org/chromium/chrome/browser/explore_sites/ExploreSitesCategoryCardViewDenseTitleBottomTest.java",
   "javatests/src/org/chromium/chrome/browser/explore_sites/ExploreSitesCategoryCardViewDenseTitleRightTest.java",
diff --git a/chrome/android/features/autofill_assistant/javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantCollectUserDataIntegrationTest.java b/chrome/android/features/autofill_assistant/javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantCollectUserDataIntegrationTest.java
index 775643c..84663de 100644
--- a/chrome/android/features/autofill_assistant/javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantCollectUserDataIntegrationTest.java
+++ b/chrome/android/features/autofill_assistant/javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantCollectUserDataIntegrationTest.java
@@ -92,6 +92,8 @@
 import org.chromium.chrome.browser.autofill_assistant.proto.TextInputSectionProto;
 import org.chromium.chrome.browser.autofill_assistant.proto.UseCreditCardProto;
 import org.chromium.chrome.browser.autofill_assistant.proto.UserFormSectionProto;
+import org.chromium.chrome.browser.autofill_assistant.proto.ValueExpression;
+import org.chromium.chrome.browser.autofill_assistant.proto.ValueExpression.Chunk;
 import org.chromium.chrome.browser.autofill_assistant.proto.ValueProto;
 import org.chromium.chrome.browser.customtabs.CustomTabActivityTestRule;
 import org.chromium.chrome.browser.customtabs.CustomTabsTestUtils;
@@ -161,7 +163,8 @@
         RequiredFieldProto fallbackTextField =
                 RequiredFieldProto.newBuilder()
                         .setForced(true) // Make sure we do actual work.
-                        .setValueExpression("${57}")
+                        .setValueExpression(ValueExpression.newBuilder().addChunk(
+                                Chunk.newBuilder().setKey(57)))
                         .setElement(SelectorProto.newBuilder().addFilters(
                                 SelectorProto.Filter.newBuilder().setCssSelector(
                                         "#fallback_entry")))
@@ -170,7 +173,8 @@
         RequiredFieldProto fallbackDropdownField =
                 RequiredFieldProto.newBuilder()
                         .setForced(true) // Make sure we do actual work.
-                        .setValueExpression("${-2}")
+                        .setValueExpression(ValueExpression.newBuilder().addChunk(
+                                Chunk.newBuilder().setKey(-2)))
                         .setElement(SelectorProto.newBuilder().addFilters(
                                 SelectorProto.Filter.newBuilder().setCssSelector(
                                         "#fallback_dropdown")))
@@ -178,7 +182,8 @@
                         .build();
         RequiredFieldProto fallbackJsDropdownField =
                 RequiredFieldProto.newBuilder()
-                        .setValueExpression("${55}")
+                        .setValueExpression(ValueExpression.newBuilder().addChunk(
+                                Chunk.newBuilder().setKey(55)))
                         .setElement(SelectorProto.newBuilder().addFilters(
                                 SelectorProto.Filter.newBuilder().setCssSelector(
                                         "#js_dropdown_value")))
@@ -264,7 +269,9 @@
         RequiredFieldProto fallbackTextField =
                 RequiredFieldProto.newBuilder()
                         .setForced(true) // Make sure we fail here while trying to fill the field.
-                        .setValueExpression("${-99}") // Use non-existent key to force an error.
+                        .setValueExpression(
+                                ValueExpression.newBuilder().addChunk(Chunk.newBuilder().setKey(
+                                        -99))) // Use non-existent key to force an error.
                         .setElement(SelectorProto.newBuilder().addFilters(
                                 SelectorProto.Filter.newBuilder().setCssSelector(
                                         "#fallback_entry")))
diff --git a/chrome/android/features/autofill_assistant/javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantPersonalDataManagerTest.java b/chrome/android/features/autofill_assistant/javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantPersonalDataManagerTest.java
index be2ed63..6e394a0 100644
--- a/chrome/android/features/autofill_assistant/javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantPersonalDataManagerTest.java
+++ b/chrome/android/features/autofill_assistant/javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantPersonalDataManagerTest.java
@@ -71,6 +71,8 @@
 import org.chromium.chrome.browser.autofill_assistant.proto.SupportedScriptProto;
 import org.chromium.chrome.browser.autofill_assistant.proto.SupportedScriptProto.PresentationProto;
 import org.chromium.chrome.browser.autofill_assistant.proto.UseAddressProto;
+import org.chromium.chrome.browser.autofill_assistant.proto.ValueExpression;
+import org.chromium.chrome.browser.autofill_assistant.proto.ValueExpression.Chunk;
 import org.chromium.chrome.browser.customtabs.CustomTabActivityTestRule;
 import org.chromium.chrome.browser.customtabs.CustomTabsTestUtils;
 import org.chromium.chrome.browser.flags.ChromeSwitches;
@@ -145,20 +147,30 @@
                                                         "#profile_name")))
                                         .addRequiredFields(
                                                 RequiredFieldProto.newBuilder()
-                                                        .setValueExpression("7")
+                                                        .setValueExpression(
+                                                                ValueExpression.newBuilder()
+                                                                        .addChunk(
+                                                                                Chunk.newBuilder()
+                                                                                        .setKey(7)))
                                                         .setElement(SelectorProto.newBuilder().addFilters(
                                                                 SelectorProto.Filter.newBuilder()
                                                                         .setCssSelector(
-                                                                                "#profile_name"))))
+                                                                                "#profile_name")))
+                                                        .setForced(true))
                                         .addRequiredFields(
                                                 RequiredFieldProto.newBuilder()
-                                                        .setValueExpression("9")
+                                                        .setValueExpression(
+                                                                ValueExpression.newBuilder()
+                                                                        .addChunk(
+                                                                                Chunk.newBuilder()
+                                                                                        .setKey(9)))
                                                         .setElement(
                                                                 SelectorProto.newBuilder().addFilters(
                                                                         SelectorProto.Filter
                                                                                 .newBuilder()
                                                                                 .setCssSelector(
-                                                                                        "#email")))))
+                                                                                        "#email")))
+                                                        .setForced(true)))
                         .build());
         list.add((ActionProto) ActionProto.newBuilder()
                          .setPrompt(PromptProto.newBuilder().setMessage("Prompt").addChoices(
diff --git a/chrome/android/features/autofill_assistant/public/java/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantFacade.java b/chrome/android/features/autofill_assistant/public/java/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantFacade.java
index 8abe515..9df732c 100644
--- a/chrome/android/features/autofill_assistant/public/java/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantFacade.java
+++ b/chrome/android/features/autofill_assistant/public/java/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantFacade.java
@@ -167,15 +167,15 @@
         }
 
         // The tab is not yet available. We need to register as listener and wait for it.
-        activity.getActivityTabProvider().addObserverAndTrigger(
-                new ActivityTabProvider.HintlessActivityTabObserver() {
-                    @Override
-                    public void onActivityTabChanged(Tab tab) {
-                        if (tab == null) return;
-                        activity.getActivityTabProvider().removeObserver(this);
-                        callback.onResult(tab);
-                    }
-                });
+        activity.getActivityTabProvider().addObserver(new Callback<Tab>() {
+            @Override
+            public void onResult(Tab tab) {
+                if (tab == null) return;
+                activity.getActivityTabProvider().removeObserver(this);
+                assert tab.getWebContents() != null;
+                callback.onResult(tab);
+            }
+        });
     }
 
     public static boolean isAutofillAssistantEnabled(Intent intent) {
diff --git a/chrome/android/features/keyboard_accessory/junit/src/org/chromium/chrome/browser/keyboard_accessory/ManualFillingControllerTest.java b/chrome/android/features/keyboard_accessory/junit/src/org/chromium/chrome/browser/keyboard_accessory/ManualFillingControllerTest.java
index 89502c4..a599e47 100644
--- a/chrome/android/features/keyboard_accessory/junit/src/org/chromium/chrome/browser/keyboard_accessory/ManualFillingControllerTest.java
+++ b/chrome/android/features/keyboard_accessory/junit/src/org/chromium/chrome/browser/keyboard_accessory/ManualFillingControllerTest.java
@@ -139,6 +139,7 @@
     private static class MockActivityTabProvider extends ActivityTabProvider {
         public Tab mTab;
 
+        @Override
         public void set(Tab tab) {
             mTab = tab;
         }
diff --git a/chrome/android/features/start_surface/internal/javatests/src/org/chromium/chrome/features/start_surface/StartSurfaceTest.java b/chrome/android/features/start_surface/internal/javatests/src/org/chromium/chrome/features/start_surface/StartSurfaceTest.java
index 069cf9c9..b849384 100644
--- a/chrome/android/features/start_surface/internal/javatests/src/org/chromium/chrome/features/start_surface/StartSurfaceTest.java
+++ b/chrome/android/features/start_surface/internal/javatests/src/org/chromium/chrome/features/start_surface/StartSurfaceTest.java
@@ -141,7 +141,7 @@
     private static final String BASE_PARAMS =
             "force-fieldtrial-params=Study.Group:start_surface_variation";
 
-    private static final long MAX_TIMEOUT_MS = 30000L;
+    private static final long MAX_TIMEOUT_MS = 40000L;
 
     @Rule
     public ChromeTabbedActivityTestRule mActivityTestRule = new ChromeTabbedActivityTestRule();
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ActivityTabProvider.java b/chrome/android/java/src/org/chromium/chrome/browser/ActivityTabProvider.java
index 1b87a6c..2ea7330 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ActivityTabProvider.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ActivityTabProvider.java
@@ -7,9 +7,8 @@
 import androidx.annotation.CallSuper;
 import androidx.annotation.VisibleForTesting;
 
-import org.chromium.base.ObserverList;
-import org.chromium.base.ObserverList.RewindableIterator;
-import org.chromium.base.supplier.Supplier;
+import org.chromium.base.Callback;
+import org.chromium.base.supplier.ObservableSupplierImpl;
 import org.chromium.chrome.browser.layouts.LayoutStateProvider;
 import org.chromium.chrome.browser.layouts.LayoutStateProvider.LayoutStateObserver;
 import org.chromium.chrome.browser.layouts.LayoutType;
@@ -24,37 +23,7 @@
 /**
  * A class that provides the current {@link Tab} for various states of the browser's activity.
  */
-public class ActivityTabProvider implements Supplier<Tab> {
-    /** An interface to track the visible tab for the activity. */
-    public interface ActivityTabObserver {
-        /**
-         * A notification that the activity's tab has changed. This will be triggered whenever a
-         * different tab is selected by the active {@link TabModel} and when that tab is
-         * interactive (i.e. not in a tab switching mode). When switching to toolbar swipe or tab
-         * switcher, this method will be called with {@code null} to indicate that there is no
-         * single activity tab (observers may or may not choose to ignore this event).
-         * @param tab The {@link Tab} that became visible or null if not in {@link StaticLayout}.
-         * @param hint Whether the change event is a hint that a tab change is likely. If true, the
-         *             provided tab may still be frozen and is not yet selected.
-         */
-        void onActivityTabChanged(Tab tab, boolean hint);
-    }
-
-    /** An {@link ActivityTabObserver} that can be used to explicitly watch non-hint events. */
-    public abstract static class HintlessActivityTabObserver implements ActivityTabObserver {
-        @Override
-        public final void onActivityTabChanged(Tab tab, boolean hint) {
-            // Only pass the event through if it isn't a hint.
-            if (!hint) onActivityTabChanged(tab);
-        }
-
-        /**
-         * A notification that the {@link Tab} in the {@link StaticLayout} has changed.
-         * @param tab The activity's tab.
-         */
-        public abstract void onActivityTabChanged(Tab tab);
-    }
-
+public class ActivityTabProvider extends ObservableSupplierImpl<Tab> {
     /**
      * A utility class for observing the activity tab via {@link TabObserver}. When the activity
      * tab changes, the observer is switched to that tab.
@@ -64,7 +33,7 @@
         private final ActivityTabProvider mTabProvider;
 
         /** An observer to watch for a changing activity tab and move this tab observer. */
-        private final ActivityTabObserver mActivityTabObserver;
+        private final Callback<Tab> mActivityTabObserver;
 
         /** The current activity tab. */
         private Tab mTab;
@@ -87,16 +56,15 @@
          */
         public ActivityTabTabObserver(ActivityTabProvider tabProvider, boolean shouldTrigger) {
             mTabProvider = tabProvider;
-            mActivityTabObserver = (tab, hint) -> {
+            mActivityTabObserver = (tab) -> {
                 updateObservedTab(tab);
-                onObservingDifferentTab(tab, hint);
+                onObservingDifferentTab(tab, /*hint=*/false);
             };
-            if (shouldTrigger) {
-                mTabProvider.addObserverAndTrigger(mActivityTabObserver);
-            } else {
-                mTabProvider.addObserver(mActivityTabObserver);
-            }
-            updateObservedTab(mTabProvider.get());
+
+            addObserverToTabProvider();
+            if (shouldTrigger) onObservingDifferentTab(tabProvider.get(), /*hint=*/false);
+
+            updateObservedTabToCurrent();
         }
 
         /**
@@ -128,22 +96,25 @@
                 mTab.removeObserver(this);
                 mTab = null;
             }
+            removeObserverFromTabProvider();
+        }
+
+        @VisibleForTesting
+        protected void updateObservedTabToCurrent() {
+            updateObservedTab(mTabProvider.get());
+        }
+
+        @VisibleForTesting
+        protected void addObserverToTabProvider() {
+            mTabProvider.addObserver(mActivityTabObserver);
+        }
+
+        @VisibleForTesting
+        protected void removeObserverFromTabProvider() {
             mTabProvider.removeObserver(mActivityTabObserver);
         }
     }
 
-    /** The list of observers to send events to. */
-    private final ObserverList<ActivityTabObserver> mObservers = new ObserverList<>();
-
-    /**
-     * A single rewindable iterator bound to {@link #mObservers} to prevent constant allocation of
-     * new iterators.
-     */
-    private final RewindableIterator<ActivityTabObserver> mRewindableIterator;
-
-    /** The {@link Tab} that is considered to be the activity's tab. */
-    private Tab mActivityTab;
-
     /** A handle to the {@link LayoutStateProvider} to get the active layout. */
     private LayoutStateProvider mLayoutStateProvider;
 
@@ -159,24 +130,15 @@
     /** An observer for watching tab model switching event. */
     private TabModelSelectorObserver mTabModelSelectorObserver;
 
-    /** The last tab ID that was hinted. This is reset when the activity tab actually changes. */
-    private int mLastHintedTabId;
-
     /**
      * Default constructor.
      */
     public ActivityTabProvider() {
-        mRewindableIterator = mObservers.rewindableIterator();
         mLayoutStateObserver = new LayoutStateObserver() {
             @Override
             public void onTabSelectionHinted(int tabId) {
-                if (mTabModelSelector == null || mLastHintedTabId == tabId) return;
-                Tab tab = mTabModelSelector.getTabById(tabId);
-                mLastHintedTabId = tabId;
-                mRewindableIterator.rewind();
-                while (mRewindableIterator.hasNext()) {
-                    mRewindableIterator.next().onActivityTabChanged(tab, true);
-                }
+                if (mTabModelSelector == null) return;
+                set(mTabModelSelector.getTabById(tabId));
             }
 
             @Override
@@ -195,14 +157,6 @@
     }
 
     /**
-     * @return The activity's current tab.
-     */
-    @Override
-    public Tab get() {
-        return mActivityTab;
-    }
-
-    /**
      * @param selector A {@link TabModelSelector} for watching for changes in tabs.
      */
     public void setTabModelSelector(TabModelSelector selector) {
@@ -256,50 +210,11 @@
             return;
         }
 
-        if (mActivityTab == tab) return;
-        mActivityTab = tab;
-        mLastHintedTabId = Tab.INVALID_TAB_ID;
-
-        mRewindableIterator.rewind();
-        while (mRewindableIterator.hasNext()) {
-            mRewindableIterator.next().onActivityTabChanged(tab, false);
-        }
-    }
-
-    /**
-     * Add an observer but do not immediately trigger the event. This should only be used in
-     * extremely specific cases where the observer would trigger an event from the constructor of
-     * the implementing class (see {@link ActivityTabTabObserver}).
-     * @param observer The observer to be added.
-     *
-     * TODO(fgorski): Find a different way to mock this in tests for {@link LoadProgressMediator}.
-     */
-    @VisibleForTesting
-    @Deprecated
-    public void addObserver(ActivityTabObserver observer) {
-        mObservers.addObserver(observer);
-    }
-
-    /**
-     * @param observer The {@link ActivityTabObserver} to add to the activity. This will trigger the
-     *                 {@link ActivityTabObserver#onActivityTabChanged(Tab, boolean)} event to be
-     *                 called on the added observer, providing access to the current tab.
-     */
-    public void addObserverAndTrigger(ActivityTabObserver observer) {
-        mObservers.addObserver(observer);
-        observer.onActivityTabChanged(mActivityTab, false);
-    }
-
-    /**
-     * @param observer The {@link ActivityTabObserver} to remove from the activity.
-     */
-    public void removeObserver(ActivityTabObserver observer) {
-        mObservers.removeObserver(observer);
+        set(tab);
     }
 
     /** Clean up and detach any observers this object created. */
     public void destroy() {
-        mObservers.clear();
         if (mLayoutStateProvider != null) mLayoutStateProvider.removeObserver(mLayoutStateObserver);
         mLayoutStateProvider = null;
         if (mTabModelObserver != null) mTabModelObserver.destroy();
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/contextmenu/ChromeContextMenuPopulator.java b/chrome/android/java/src/org/chromium/chrome/browser/contextmenu/ChromeContextMenuPopulator.java
index 062202c..3503c57 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/contextmenu/ChromeContextMenuPopulator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/contextmenu/ChromeContextMenuPopulator.java
@@ -90,7 +90,6 @@
     private final Supplier<ShareDelegate> mShareDelegateSupplier;
     private final ExternalAuthUtils mExternalAuthUtils;
     private final ContextMenuParams mParams;
-    private boolean mIsLensIntentInProgress;
     private @Nullable UkmRecorder.Bridge mUkmRecorderBridge;
     private ContextMenuNativeDelegate mNativeDelegate;
     private static final String LENS_SEARCH_MENU_ITEM_KEY = "searchWithGoogleLensMenuItem";
@@ -772,11 +771,6 @@
                     TrackerFactory.getTrackerForProfile(Profile.getLastUsedRegularProfile());
             if (tracker.isInitialized()) tracker.dismissed(FeatureConstants.EPHEMERAL_TAB_FEATURE);
         }
-
-        if (!mIsLensIntentInProgress) {
-            // TODO(crbug/1158604): Remove leftover Lens dependencies.
-            LensUtils.terminateLensConnectionsIfNecessary(mItemDelegate.isIncognito());
-        }
     }
 
     private WindowAndroid getWindow() {
@@ -853,7 +847,6 @@
      */
     protected void searchWithGoogleLens(
             @LensEntryPoint int lensEntryPoint, boolean requiresConfirmation) {
-        mIsLensIntentInProgress = true;
         mNativeDelegate.retrieveImageForShare(ContextMenuImageFormat.PNG, (Uri imageUri) -> {
             ShareHelper.shareImageWithGoogleLens(getWindow(), imageUri, mItemDelegate.isIncognito(),
                     mParams.getSrcUrl(), mParams.getTitleText(), mParams.getPageUrl(),
@@ -869,13 +862,13 @@
             // TODO(crbug.com/783819): Migrate LensChipDelegate to GURL.
             return new LensChipDelegate(mParams.getPageUrl().getSpec(), mParams.getTitleText(),
                     mParams.getSrcUrl().getSpec(), getPageTitle(), isIncognito(),
-                    mItemDelegate.getWebContents(), mNativeDelegate, getOnLensChipClickedCallback(),
-                    getOnLensChipShownCallback());
+                    mItemDelegate.getWebContents(), mNativeDelegate, getOnChipClickedCallback(),
+                    getOnChipShownCallback());
         }
         return null;
     }
 
-    private Callback<Integer> getOnLensChipShownCallback() {
+    private Callback<Integer> getOnChipShownCallback() {
         return (Integer result) -> {
             int chipType = result.intValue();
             switch (chipType) {
@@ -893,7 +886,7 @@
         };
     }
 
-    private Callback<Integer> getOnLensChipClickedCallback() {
+    private Callback<Integer> getOnChipClickedCallback() {
         return (Integer result) -> {
             int chipType = result.intValue();
             switch (chipType) {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/contextmenu/ContextMenuChipController.java b/chrome/android/java/src/org/chromium/chrome/browser/contextmenu/ContextMenuChipController.java
index 3969ed3..f15a8ce3 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/contextmenu/ContextMenuChipController.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/contextmenu/ContextMenuChipController.java
@@ -34,6 +34,7 @@
     private AnchoredPopupWindow mPopupWindow;
     private Context mContext;
     private ChipRenderParams mChipRenderParams;
+    private final Runnable mDismissContextMenuCallback;
 
     @VisibleForTesting
     @IntDef({ChipEvent.SHOWN, ChipEvent.CLICKED, ChipEvent.DISMISSED})
@@ -50,9 +51,11 @@
                 "ContextMenu.LensChip.Event", chipEvent, ChipEvent.NUM_ENTRIES);
     }
 
-    ContextMenuChipController(Context context, View anchorView) {
+    ContextMenuChipController(
+            Context context, View anchorView, final Runnable dismissContextMenuCallback) {
         mContext = context;
         mAnchorView = anchorView;
+        mDismissContextMenuCallback = dismissContextMenuCallback;
     }
 
     /**
@@ -110,8 +113,10 @@
     public void onClick(View v) {
         if (v == mChipView) {
             recordChipEvent(ChipEvent.CLICKED);
+            // The onClick callback may result in a cross-app switch so dismiss the menu before
+            // executing that logic. Also note that dismissing the menu will also dismiss the chip.
+            mDismissContextMenuCallback.run();
             mChipRenderParams.onClickCallback.run();
-            dismissLensChipIfShowing();
         }
     }
 
@@ -119,7 +124,7 @@
      * Dismiss the lens chip. Needed for cases where a user dismisses
      * the context menu without closing the chip manually.
      */
-    void dismissLensChipIfShowing() {
+    void dismissChipIfShowing() {
         if (mPopupWindow != null && mPopupWindow.isShowing()) {
             mPopupWindow.dismiss();
         }
@@ -171,7 +176,7 @@
         if (!chipRenderParams.isRemoveIconHidden) {
             mChipView.addRemoveIcon();
             mChipView.setRemoveIconClickListener(v -> {
-                dismissLensChipIfShowing();
+                dismissChipIfShowing();
                 recordChipEvent(ChipEvent.DISMISSED);
             });
         }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/contextmenu/ContextMenuCoordinator.java b/chrome/android/java/src/org/chromium/chrome/browser/contextmenu/ContextMenuCoordinator.java
index 5dec1f6..7f6cde4 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/contextmenu/ContextMenuCoordinator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/contextmenu/ContextMenuCoordinator.java
@@ -115,9 +115,11 @@
                 R.layout.context_menu_fullscreen_container, null);
 
         // Only display a chip if an image was selected and the menu isn't a popup.
-        if (params.isImage() && chipDelegate != null && !isPopup) {
+        if (params.isImage() && chipDelegate != null && chipDelegate.isChipSupported()
+                && !isPopup) {
             View chipAnchorView = layout.findViewById(R.id.context_menu_chip_anchor_point);
-            mChipController = new ContextMenuChipController(activity, chipAnchorView);
+            mChipController =
+                    new ContextMenuChipController(activity, chipAnchorView, () -> dismiss());
             chipDelegate.getChipRenderParams((chipRenderParams) -> {
                 if (chipDelegate.isValidChipRenderParams(chipRenderParams) && mDialog.isShowing()) {
                     mChipController.showChip(chipRenderParams);
@@ -301,7 +303,7 @@
             mWebContentsObserver.destroy();
         }
         if (mChipController != null) {
-            mChipController.dismissLensChipIfShowing();
+            mChipController.dismissChipIfShowing();
         }
         mDialog.dismiss();
     }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/contextmenu/ContextMenuHelper.java b/chrome/android/java/src/org/chromium/chrome/browser/contextmenu/ContextMenuHelper.java
index cf18edb..78554d2 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/contextmenu/ContextMenuHelper.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/contextmenu/ContextMenuHelper.java
@@ -167,8 +167,6 @@
         mCurrentContextMenu = menuCoordinator;
         mChipDelegate = mCurrentPopulator.getChipDelegate();
 
-        // TODO(crbug/1158604): Remove leftover Lens dependencies.
-        LensUtils.startLensConnectionIfNecessary(mIsIncognito);
         if (mChipDelegate != null) {
             menuCoordinator.displayMenuWithChip(mWindow, mWebContents, mCurrentContextMenuParams,
                     items, mCallback, mOnMenuShown, mOnMenuClosed, mChipDelegate);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/contextmenu/LensChipDelegate.java b/chrome/android/java/src/org/chromium/chrome/browser/contextmenu/LensChipDelegate.java
index 0f00320..5833b2c 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/contextmenu/LensChipDelegate.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/contextmenu/LensChipDelegate.java
@@ -16,6 +16,7 @@
  * The class to handle Lens chip data and actions.
  */
 public class LensChipDelegate implements ChipDelegate {
+    private boolean mIsChipSupported;
     private LensQueryParams mLensQueryParams;
     private LensController mLensController;
     private ContextMenuNativeDelegate mNativeDelegate;
@@ -26,7 +27,8 @@
             boolean isIncognito, WebContents webContents, ContextMenuNativeDelegate nativeDelegate,
             Callback<Integer> onChipClickedCallback, Callback<Integer> onChipShownCallback) {
         mLensController = LensController.getInstance();
-        if (!mLensController.isQueryEnabled()) {
+        mIsChipSupported = mLensController.isQueryEnabled();
+        if (!mIsChipSupported) {
             return;
         }
         mLensQueryParams =
@@ -43,6 +45,11 @@
     }
 
     @Override
+    public boolean isChipSupported() {
+        return mIsChipSupported;
+    }
+
+    @Override
     public void getChipRenderParams(Callback<ChipRenderParams> chipParamsCallback) {
         if (mLensQueryParams == null) {
             chipParamsCallback.onResult(null);
@@ -75,9 +82,8 @@
 
     @Override
     public void onMenuClosed() {
-        if (mLensController.isQueryEnabled()) {
-            mLensController.terminateClassification();
-        }
+        // Lens controller will not react if a classification was not in progress.
+        mLensController.terminateClassification();
     }
 
     @Override
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/content/CustomTabActivityTabController.java b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/content/CustomTabActivityTabController.java
index ec29f1e..62638b3 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/content/CustomTabActivityTabController.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/content/CustomTabActivityTabController.java
@@ -20,7 +20,6 @@
 import org.chromium.base.metrics.RecordHistogram;
 import org.chromium.base.supplier.Supplier;
 import org.chromium.chrome.browser.ActivityTabProvider;
-import org.chromium.chrome.browser.ActivityTabProvider.HintlessActivityTabObserver;
 import org.chromium.chrome.browser.IntentHandler;
 import org.chromium.chrome.browser.ServiceTabLauncher;
 import org.chromium.chrome.browser.WarmupManager;
@@ -115,14 +114,6 @@
     private final CustomTabsSessionToken mSession;
     private final Intent mIntent;
 
-    @Nullable
-    private HintlessActivityTabObserver mTabSwapObserver = new HintlessActivityTabObserver() {
-        @Override
-        public void onActivityTabChanged(@Nullable Tab tab) {
-            mTabProvider.swapTab(tab);
-        }
-    };
-
     @Inject
     public CustomTabActivityTabController(ChromeActivity<?> activity,
             Lazy<CustomTabDelegateFactory> customTabDelegateFactory,
@@ -370,7 +361,7 @@
         } // else we've already set the initial tab.
 
         // Listen to tab swapping and closing.
-        mActivityTabProvider.addObserverAndTrigger(mTabSwapObserver);
+        mActivityTabProvider.addObserver(mTabProvider::swapTab);
     }
 
     @Nullable
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/media/PictureInPictureController.java b/chrome/android/java/src/org/chromium/chrome/browser/media/PictureInPictureController.java
index 814df3b..c8bbaf57 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/media/PictureInPictureController.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/media/PictureInPictureController.java
@@ -16,6 +16,7 @@
 
 import androidx.annotation.Nullable;
 
+import org.chromium.base.Callback;
 import org.chromium.base.Log;
 import org.chromium.base.MathUtils;
 import org.chromium.base.annotations.VerifiesOnO;
@@ -193,7 +194,7 @@
         });
 
         TabObserver tabObserver = new DismissActivityOnTabEventObserver(mActivity);
-        ActivityTabProvider.ActivityTabObserver activityTabObserver =
+        Callback<Tab> activityTabObserver =
                 new DismissActivityOnTabChangeObserver(mActivity, activityTab);
         WebContentsObserver webContentsObserver =
                 new DismissActivityOnWebContentsObserver(mActivity);
@@ -207,7 +208,7 @@
         activityTab.addObserver(tabObserver);
         webContents.addObserver(webContentsObserver);
         mFullscreenManager.addObserver(fullscreenListener);
-        mActivityTabProvider.addObserverAndTrigger(activityTabObserver);
+        mActivityTabProvider.addObserver(activityTabObserver);
 
         mOnLeavePipCallbacks.add(() -> {
             activityTab.removeObserver(tabObserver);
@@ -324,8 +325,7 @@
     }
 
     /** A class to dismiss the Activity when the tab changes. */
-    private class DismissActivityOnTabChangeObserver
-            extends ActivityTabProvider.HintlessActivityTabObserver {
+    private class DismissActivityOnTabChangeObserver implements Callback<Tab> {
         private final Activity mActivity;
         private final Tab mCurrentTab;
 
@@ -335,7 +335,7 @@
         }
 
         @Override
-        public void onActivityTabChanged(Tab tab) {
+        public void onResult(Tab tab) {
             if (mCurrentTab == tab) return;
             dismissActivity(mActivity, METRICS_END_REASON_NEW_TAB);
         }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/LocationBarMediator.java b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/LocationBarMediator.java
index e0dc957..fac9eac 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/LocationBarMediator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/LocationBarMediator.java
@@ -67,7 +67,6 @@
 import org.chromium.ui.util.ColorUtils;
 
 import java.util.ArrayList;
-import java.util.HashMap;
 import java.util.List;
 
 /**
@@ -467,10 +466,10 @@
                     headers.append(prevHeader);
                     headers.append("\r\n");
                 }
-                loadUrlParams.setExtraHeaders(new HashMap<String, String>() {
-                    { put("Content-Type", postDataType); }
-                });
-                headers.append(loadUrlParams.getExtraHttpRequestHeadersString());
+
+                headers.append("Content-Type: ");
+                headers.append(postDataType);
+
                 loadUrlParams.setVerbatimHeaders(headers.toString());
             }
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ui/BottomSheetManager.java b/chrome/android/java/src/org/chromium/chrome/browser/ui/BottomSheetManager.java
index e521f2e..1e9ea6e 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ui/BottomSheetManager.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ui/BottomSheetManager.java
@@ -12,8 +12,6 @@
 import org.chromium.base.supplier.OneshotSupplier;
 import org.chromium.base.supplier.Supplier;
 import org.chromium.chrome.browser.ActivityTabProvider;
-import org.chromium.chrome.browser.ActivityTabProvider.ActivityTabObserver;
-import org.chromium.chrome.browser.ActivityTabProvider.HintlessActivityTabObserver;
 import org.chromium.chrome.browser.browser_controls.BrowserControlsVisibilityManager;
 import org.chromium.chrome.browser.compositor.bottombar.OverlayPanel;
 import org.chromium.chrome.browser.compositor.bottombar.OverlayPanelManager;
@@ -56,9 +54,6 @@
     /** A listener for browser controls offset changes. */
     private final BrowserControlsVisibilityManager.Observer mBrowserControlsObserver;
 
-    /** An observer for the tab provider. */
-    private final ActivityTabObserver mActivityTabObserver;
-
     /** A tab observer that is only attached to the active tab. */
     private final TabObserver mTabObserver;
 
@@ -165,30 +160,8 @@
             }
         };
 
-        mActivityTabObserver = new HintlessActivityTabObserver() {
-            @Override
-            public void onActivityTabChanged(Tab tab) {
-                // Temporarily suppress the sheet if entering a state where there is no activity
-                // tab and the Start surface homepage isn't showing.
-                updateSuppressionForTabSwitcher(tab,
-                        mStartSurfaceSupplier.get() == null ? null
-                                                            : mStartSurfaceSupplier.get()
-                                                                      .getController()
-                                                                      .getStartSurfaceState());
-
-                if (tab == null) return;
-
-                // If refocusing the same tab, simply unsuppress the sheet.
-                if (mLastActivityTab == tab) return;
-
-                // Move the observer to the new activity tab and clear the sheet.
-                if (mLastActivityTab != null) mLastActivityTab.removeObserver(mTabObserver);
-                mLastActivityTab = tab;
-                mLastActivityTab.addObserver(mTabObserver);
-                controller.clearRequestsAndHide();
-            }
-        };
-        mTabProvider.addObserverAndTrigger(mActivityTabObserver);
+        mTabProvider.addObserver(this::setActivityTab);
+        setActivityTab(mTabProvider.get());
 
         mVrModeObserver = new VrModeObserver() {
             /** A token held while this object is suppressing the bottom sheet. */
@@ -232,6 +205,26 @@
         mOmniboxFocusStateSupplier.addObserver(mOmniboxFocusObserver);
     }
 
+    private void setActivityTab(Tab tab) {
+        // Temporarily suppress the sheet if entering a state where there is no activity
+        // tab and the Start surface homepage isn't showing.
+        updateSuppressionForTabSwitcher(tab,
+                mStartSurfaceSupplier.get() == null
+                        ? null
+                        : mStartSurfaceSupplier.get().getController().getStartSurfaceState());
+
+        if (tab == null) return;
+
+        // If refocusing the same tab, simply unsuppress the sheet.
+        if (mLastActivityTab == tab) return;
+
+        // Move the observer to the new activity tab and clear the sheet.
+        if (mLastActivityTab != null) mLastActivityTab.removeObserver(mTabObserver);
+        mLastActivityTab = tab;
+        mLastActivityTab.addObserver(mTabObserver);
+        mSheetController.clearRequestsAndHide();
+    }
+
     /**
      * Called by both {@link StateObserver} and {@link HintlessActivityTabObserver} to update the
      * suppression of the bottom sheet for Tab switcher.
@@ -398,7 +391,6 @@
     public void onDestroy() {
         mCallbackController.destroy();
         if (mLastActivityTab != null) mLastActivityTab.removeObserver(mTabObserver);
-        mTabProvider.removeObserver(mActivityTabObserver);
         mSheetController.removeObserver(this);
         mBrowserControlsVisibilityManager.removeObserver(mBrowserControlsObserver);
         mOmniboxFocusStateSupplier.removeObserver(mOmniboxFocusObserver);
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/ActivityTabProviderTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/ActivityTabProviderTest.java
index d35cdcc..2fb8a2a 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/ActivityTabProviderTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/ActivityTabProviderTest.java
@@ -8,9 +8,7 @@
 import static org.junit.Assert.assertNotEquals;
 
 import android.support.test.InstrumentationRegistry;
-
-import androidx.test.filters.LargeTest;
-import androidx.test.filters.SmallTest;
+import android.support.test.filters.SmallTest;
 
 import org.junit.Before;
 import org.junit.Rule;
@@ -45,22 +43,32 @@
 public class ActivityTabProviderTest {
     /** A test observer that provides access to the tab being observed. */
     private static class TestActivityTabTabObserver extends ActivityTabTabObserver {
-        /** Callback helper for notification that the observer is watching a different tab. */
-        private CallbackHelper mObserverMoveHelper;
-
         /** The tab currently being observed. */
         private Tab mObservedTab;
 
         public TestActivityTabTabObserver(ActivityTabProvider provider) {
             super(provider);
-            mObserverMoveHelper = new CallbackHelper();
-            mObservedTab = provider.get();
+            TestThreadUtils.runOnUiThreadBlockingNoException(() -> mObservedTab = provider.get());
         }
 
         @Override
         public void onObservingDifferentTab(Tab tab, boolean hint) {
             mObservedTab = tab;
-            mObserverMoveHelper.notifyCalled();
+        }
+
+        @Override
+        protected void updateObservedTabToCurrent() {
+            TestThreadUtils.runOnUiThreadBlocking(super::updateObservedTabToCurrent);
+        }
+
+        @Override
+        protected void addObserverToTabProvider() {
+            TestThreadUtils.runOnUiThreadBlocking(super::addObserverToTabProvider);
+        }
+
+        @Override
+        protected void removeObserverFromTabProvider() {
+            TestThreadUtils.runOnUiThreadBlocking(super::removeObserverFromTabProvider);
         }
     }
 
@@ -71,25 +79,21 @@
     private ActivityTabProvider mProvider;
     private Tab mActivityTab;
     private CallbackHelper mActivityTabChangedHelper = new CallbackHelper();
-    private CallbackHelper mActivityTabChangedHintHelper = new CallbackHelper();
-    private int mLastValidTabId;
 
     @Before
     public void setUp() throws Exception {
         mActivityTestRule.startMainActivityOnBlankPage();
-        mActivity = mActivityTestRule.getActivity();
-        mProvider = mActivity.getActivityTabProvider();
-        mProvider.addObserverAndTrigger((tab, hint) -> {
-            if (hint) {
-                mActivityTabChangedHintHelper.notifyCalled();
-            } else {
+        TestThreadUtils.runOnUiThreadBlocking(() -> {
+            mActivity = mActivityTestRule.getActivity();
+            mProvider = mActivity.getActivityTabProvider();
+            mProvider.addObserver(tab -> {
                 mActivityTab = tab;
-                mLastValidTabId = mActivityTab == null ? mLastValidTabId : mActivityTab.getId();
                 mActivityTabChangedHelper.notifyCalled();
-            }
+            });
         });
-        assertEquals("Setup should have only triggered the event once.",
-                mActivityTabChangedHelper.getCallCount(), 1);
+        mActivityTabChangedHelper.waitForCallback(0);
+        assertEquals("Setup should have only triggered the event once.", 1,
+                mActivityTabChangedHelper.getCallCount());
     }
 
     /**
@@ -108,7 +112,8 @@
     @Feature({"ActivityTabObserver"})
     public void testTriggerOnAddObserver() throws TimeoutException {
         CallbackHelper helper = new CallbackHelper();
-        mProvider.addObserverAndTrigger((tab, hint) -> helper.notifyCalled());
+        TestThreadUtils.runOnUiThreadBlocking(
+                () -> { mProvider.addObserver(tab -> helper.notifyCalled()); });
         helper.waitForCallback(0);
 
         assertEquals("Only the added observer should have been triggered.",
@@ -142,27 +147,6 @@
                 mActivityTab);
     }
 
-    /** Test that the hint event triggers when exiting the tab switcher. */
-    @Test
-    @LargeTest
-    @Feature({"ActivityTabObserver"})
-    @Restriction(UiRestriction.RESTRICTION_TYPE_PHONE)
-    public void testTriggerHintWithTabSwitcher() throws TimeoutException {
-        assertEquals("The hint should not yet have triggered.", 0,
-                mActivityTabChangedHintHelper.getCallCount());
-
-        setTabSwitcherModeAndWait(true);
-
-        assertEquals("The hint should not yet have triggered.", 0,
-                mActivityTabChangedHintHelper.getCallCount());
-
-        setTabSwitcherModeAndWait(false);
-        mActivityTabChangedHintHelper.waitForCallback(0);
-
-        assertEquals("The hint should have triggerd once.", 1,
-                mActivityTabChangedHintHelper.getCallCount());
-    }
-
     /**
      * Test that onActivityTabChanged is triggered when switching to a new tab without switching
      * layouts.
@@ -245,7 +229,7 @@
     @Test
     @SmallTest
     @Feature({"ActivityTabObserver"})
-    public void testActivityTabTabObserver() {
+    public void testActivityTabTabObserver() throws TimeoutException {
         Tab startingTab = getModelSelectedTab();
 
         TestActivityTabTabObserver tabObserver = new TestActivityTabTabObserver(mProvider);
@@ -272,10 +256,9 @@
             if (inSwitcher) {
                 mActivity.getLayoutManager().showOverview(true);
             } else {
-                mActivity.getLayoutManager().hideOverviewWithNextTab(true, mLastValidTabId);
+                mActivity.getLayoutManager().hideOverview(true);
             }
         });
-
         LayoutTestUtils.waitForLayout(mActivity.getLayoutManager(), LayoutType.TAB_SWITCHER);
     }
 }
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/ChromeActionModeHandlerTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/ChromeActionModeHandlerTest.java
index c14d5858..cd124ee 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/ChromeActionModeHandlerTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/ChromeActionModeHandlerTest.java
@@ -54,13 +54,13 @@
         assertActionModeIsReady();
 
         LoadUrlParams urlParams = new LoadUrlParams(ContentUrlConstants.ABOUT_BLANK_DISPLAY_URL);
-        Tab tab = mActivityTestRule.getActivity().getActivityTabProvider().get();
-
         // Assert that a new tab has an action mode callback set as expected.
         // clang-format off
-        TestThreadUtils.runOnUiThreadBlockingNoException(
-                () -> mActivityTestRule.getActivity().getTabModelSelector().openNewTab(
-                        urlParams, TabLaunchType.FROM_LONGPRESS_FOREGROUND, tab, true));
+        TestThreadUtils.runOnUiThreadBlockingNoException(() -> {
+            Tab tab = mActivityTestRule.getActivity().getActivityTabProvider().get();
+            return mActivityTestRule.getActivity().getTabModelSelector().openNewTab(
+                        urlParams, TabLaunchType.FROM_LONGPRESS_FOREGROUND, tab, true);
+        });
         // clang-format on
         assertActionModeIsReady();
         testServer.stopAndDestroyServer();
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/contextmenu/ContextMenuChipControllerTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/contextmenu/ContextMenuChipControllerTest.java
index 8669a3b..d0a991a 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/contextmenu/ContextMenuChipControllerTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/contextmenu/ContextMenuChipControllerTest.java
@@ -9,6 +9,9 @@
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
 
 import android.view.View;
 
@@ -17,6 +20,7 @@
 import org.junit.BeforeClass;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
 import org.chromium.chrome.R;
@@ -46,9 +50,11 @@
     // 8 (text start padding)
     private static final int EXPECTED_CHIP_NO_END_BUTTON_WIDTH_DP = 290;
 
-    private final Runnable mEmptyChipClickCallbackForTesting = () -> {
-        return;
-    };
+    @Mock
+    private Runnable mMockChipClickRunnable;
+
+    @Mock
+    private Runnable mMockDismissRunnable;
 
     private float mMeasuredDeviceDensity;
     private View mAnchorView;
@@ -71,30 +77,10 @@
 
     @Test
     @SmallTest
-    public void testChipShownWhenCallbackReturnsChipRenderParams() {
-        ContextMenuChipController chipController =
-                new ContextMenuChipController(getActivity(), mAnchorView);
-        TestThreadUtils.runOnUiThreadBlocking(() -> {
-            ChipRenderParams chipRenderParams = new ChipRenderParams();
-            chipRenderParams.titleResourceId = R.string.contextmenu_shop_image_with_google_lens;
-            chipRenderParams.iconResourceId = R.drawable.lens_icon;
-            chipRenderParams.onClickCallback = mEmptyChipClickCallbackForTesting;
-            chipController.showChip(chipRenderParams);
-        });
-
-        assertNotNull("Anchor view was not initialized.", mAnchorView);
-        assertNotNull("Popup window was not initialized.",
-                chipController.getCurrentPopupWindowForTesting());
-        assertTrue("Popup window not showing.",
-                chipController.getCurrentPopupWindowForTesting().isShowing());
-    }
-
-    @Test
-    @SmallTest
     public void testDismissChipWhenNotShownBeforeClassificationReturned() {
         ContextMenuChipController chipController =
-                new ContextMenuChipController(getActivity(), mAnchorView);
-        TestThreadUtils.runOnUiThreadBlocking(() -> { chipController.dismissLensChipIfShowing(); });
+                new ContextMenuChipController(getActivity(), mAnchorView, mMockDismissRunnable);
+        TestThreadUtils.runOnUiThreadBlocking(() -> { chipController.dismissChipIfShowing(); });
 
         assertNotNull("Anchor view was not initialized.", mAnchorView);
         assertNull("Popup window was initialized unexpectedly.",
@@ -105,18 +91,20 @@
     @SmallTest
     public void testDismissChipWhenShown() {
         ContextMenuChipController chipController =
-                new ContextMenuChipController(getActivity(), mAnchorView);
+                new ContextMenuChipController(getActivity(), mAnchorView, mMockDismissRunnable);
         TestThreadUtils.runOnUiThreadBlocking(() -> {
             ChipRenderParams chipRenderParams = new ChipRenderParams();
             chipRenderParams.titleResourceId = R.string.contextmenu_shop_image_with_google_lens;
             chipRenderParams.iconResourceId = R.drawable.lens_icon;
-            chipRenderParams.onClickCallback = mEmptyChipClickCallbackForTesting;
+            chipRenderParams.onClickCallback = mMockChipClickRunnable;
             chipController.showChip(chipRenderParams);
-            chipController.dismissLensChipIfShowing();
+            chipController.dismissChipIfShowing();
         });
 
+        verify(mMockDismissRunnable, never()).run();
+        verify(mMockChipClickRunnable, never()).run();
         assertNotNull("Anchor view was not initialized.", mAnchorView);
-        assertNotNull("Popup window was initialized unexpectedly.",
+        assertNotNull("Popup window was not initialized.",
                 chipController.getCurrentPopupWindowForTesting());
         assertFalse("Popup window showing unexpectedly.",
                 chipController.getCurrentPopupWindowForTesting().isShowing());
@@ -124,9 +112,32 @@
 
     @Test
     @SmallTest
+    public void testClickChipWhenShown() {
+        ContextMenuChipController chipController =
+                new ContextMenuChipController(getActivity(), mAnchorView, mMockDismissRunnable);
+        TestThreadUtils.runOnUiThreadBlocking(() -> {
+            ChipRenderParams chipRenderParams = new ChipRenderParams();
+            chipRenderParams.titleResourceId = R.string.contextmenu_shop_image_with_google_lens;
+            chipRenderParams.iconResourceId = R.drawable.lens_icon;
+            chipRenderParams.onClickCallback = mMockChipClickRunnable;
+            chipController.showChip(chipRenderParams);
+            chipController.clickChipForTesting();
+        });
+
+        verify(mMockDismissRunnable, times(1)).run();
+        verify(mMockChipClickRunnable, times(1)).run();
+        assertNotNull("Anchor view was not initialized.", mAnchorView);
+        assertNotNull("Popup window was not initialized.",
+                chipController.getCurrentPopupWindowForTesting());
+        assertTrue("Dismiss was mocked so the popup window should still be showing.",
+                chipController.getCurrentPopupWindowForTesting().isShowing());
+    }
+
+    @Test
+    @SmallTest
     public void testExpectedVerticalPxNeededForChip() {
         ContextMenuChipController chipController =
-                new ContextMenuChipController(getActivity(), mAnchorView);
+                new ContextMenuChipController(getActivity(), mAnchorView, mMockDismissRunnable);
         assertEquals("Vertical px is not matching the expectation",
                 (int) (EXPECTED_VERTICAL_DP * mMeasuredDeviceDensity),
                 chipController.getVerticalPxNeededForChip());
@@ -136,7 +147,7 @@
     @SmallTest
     public void testExpectedChipTextMaxWidthPx() {
         ContextMenuChipController chipController =
-                new ContextMenuChipController(getActivity(), mAnchorView);
+                new ContextMenuChipController(getActivity(), mAnchorView, mMockDismissRunnable);
         assertEquals("Chip width px is not matching the expectation",
                 (int) (EXPECTED_CHIP_WIDTH_DP * mMeasuredDeviceDensity),
                 chipController.getChipTextMaxWidthPx(false));
@@ -146,7 +157,7 @@
     @SmallTest
     public void testExpectedChipTextMaxWidthPx_EndButtonHidden() {
         ContextMenuChipController chipController =
-                new ContextMenuChipController(getActivity(), mAnchorView);
+                new ContextMenuChipController(getActivity(), mAnchorView, mMockDismissRunnable);
         assertEquals("Chip width px is not matching the expectation",
                 (int) (EXPECTED_CHIP_NO_END_BUTTON_WIDTH_DP * mMeasuredDeviceDensity),
                 chipController.getChipTextMaxWidthPx(true));
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/contextmenu/ContextMenuTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/contextmenu/ContextMenuTest.java
index d9d54cc9..6e612f2 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/contextmenu/ContextMenuTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/contextmenu/ContextMenuTest.java
@@ -108,6 +108,11 @@
     // Test chip delegate that always returns valid chip render params.
     private static final ChipDelegate FAKE_CHIP_DELEGATE = new ChipDelegate() {
         @Override
+        public boolean isChipSupported() {
+            return true;
+        }
+
+        @Override
         public void getChipRenderParams(Callback<ChipRenderParams> callback) {
             // Do nothing.
         }
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/download/DownloadLocationChangeTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/download/DownloadLocationChangeEnd2EndTest.java
similarity index 97%
rename from chrome/android/javatests/src/org/chromium/chrome/browser/download/DownloadLocationChangeTest.java
rename to chrome/android/javatests/src/org/chromium/chrome/browser/download/DownloadLocationChangeEnd2EndTest.java
index 3f44982..cd0e2f2 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/download/DownloadLocationChangeTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/download/DownloadLocationChangeEnd2EndTest.java
@@ -43,11 +43,11 @@
 import java.util.ArrayList;
 
 /**
- * Test to verify download location change feature behaviors.
+ * Test to verify download end to end flow with download location dialog.
  */
 @RunWith(ChromeJUnit4ClassRunner.class)
 @CommandLineFlags.Add({ChromeSwitches.DISABLE_FIRST_RUN_EXPERIENCE})
-public class DownloadLocationChangeTest implements CustomMainActivityStart {
+public class DownloadLocationChangeEnd2EndTest implements CustomMainActivityStart {
     @Rule
     public DownloadTestRule mDownloadTestRule = new DownloadTestRule(this);
 
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/download/dialogs/DownloadLocationDialogTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/download/dialogs/DownloadLocationDialogTest.java
new file mode 100644
index 0000000..05e80a5
--- /dev/null
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/download/dialogs/DownloadLocationDialogTest.java
@@ -0,0 +1,181 @@
+// 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.download.dialogs;
+
+import static androidx.test.espresso.Espresso.onView;
+import static androidx.test.espresso.assertion.ViewAssertions.matches;
+import static androidx.test.espresso.matcher.ViewMatchers.isChecked;
+import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed;
+import static androidx.test.espresso.matcher.ViewMatchers.isNotChecked;
+import static androidx.test.espresso.matcher.ViewMatchers.withId;
+import static androidx.test.espresso.matcher.ViewMatchers.withText;
+
+import static org.mockito.AdditionalMatchers.not;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.when;
+
+import androidx.annotation.StringRes;
+import androidx.test.filters.MediumTest;
+
+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.StrictModeContext;
+import org.chromium.base.test.util.CommandLineFlags;
+import org.chromium.base.test.util.JniMocker;
+import org.chromium.chrome.browser.download.DirectoryOption;
+import org.chromium.chrome.browser.download.DownloadDialogBridge;
+import org.chromium.chrome.browser.download.DownloadDialogBridgeJni;
+import org.chromium.chrome.browser.download.DownloadDirectoryProvider;
+import org.chromium.chrome.browser.download.DownloadLocationDialogType;
+import org.chromium.chrome.browser.download.DownloadPromptStatus;
+import org.chromium.chrome.browser.download.R;
+import org.chromium.chrome.browser.download.TestDownloadDirectoryProvider;
+import org.chromium.chrome.browser.flags.ChromeFeatureList;
+import org.chromium.chrome.browser.flags.ChromeSwitches;
+import org.chromium.chrome.browser.preferences.Pref;
+import org.chromium.chrome.browser.profiles.Profile;
+import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
+import org.chromium.components.browser_ui.modaldialog.AppModalPresenter;
+import org.chromium.components.browser_ui.util.DownloadUtils;
+import org.chromium.components.prefs.PrefService;
+import org.chromium.components.user_prefs.UserPrefs;
+import org.chromium.components.user_prefs.UserPrefsJni;
+import org.chromium.content_public.browser.test.util.TestThreadUtils;
+import org.chromium.ui.modaldialog.ModalDialogManager;
+import org.chromium.ui.test.util.DummyUiActivityTestCase;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Test focus on verifying UI elements in the download location dialog.
+ */
+// TODO(xingliu): Implement more test cases to test every MVC properties.
+@RunWith(ChromeJUnit4ClassRunner.class)
+@CommandLineFlags.Add({ChromeSwitches.DISABLE_FIRST_RUN_EXPERIENCE})
+public class DownloadLocationDialogTest extends DummyUiActivityTestCase {
+    private static final long TOTAL_BYTES = 1024L;
+    private static final String SUGGESTED_PATH = "download.png";
+    private static final String PRIMARY_STORAGE_PATH = "/sdcard";
+    private static final String SECONDARY_STORAGE_PATH = "/android/Download";
+
+    @Rule
+    public JniMocker mJniMocker = new JniMocker();
+
+    @Mock
+    private DownloadLocationDialogController mController;
+    @Mock
+    private Profile mProfileMock;
+    @Mock
+    PrefService mPrefService;
+    @Mock
+    private UserPrefs.Natives mUserPrefsJniMock;
+    @Mock
+    private DownloadDialogBridge.Natives mDownloadDialogBridgeJniMock;
+
+    private AppModalPresenter mAppModalPresenter;
+    private ModalDialogManager mModalDialogManager;
+    private DownloadLocationDialogCoordinator mDialogCoordinator;
+
+    @Override
+    public void setUpTest() throws Exception {
+        super.setUpTest();
+        MockitoAnnotations.initMocks(this);
+        mJniMocker.mock(UserPrefsJni.TEST_HOOKS, mUserPrefsJniMock);
+        when(mUserPrefsJniMock.get(any())).thenReturn(mPrefService);
+        mJniMocker.mock(DownloadDialogBridgeJni.TEST_HOOKS, mDownloadDialogBridgeJniMock);
+        when(mDownloadDialogBridgeJniMock.getDownloadDefaultDirectory())
+                .thenReturn(PRIMARY_STORAGE_PATH);
+        Profile.setLastUsedProfileForTesting(mProfileMock);
+        mAppModalPresenter = new AppModalPresenter(getActivity());
+        mModalDialogManager =
+                new ModalDialogManager(mAppModalPresenter, ModalDialogManager.ModalDialogType.APP);
+        Map<String, Boolean> features = new HashMap<>();
+        features.put(ChromeFeatureList.SMART_SUGGESTION_FOR_LARGE_DOWNLOADS, false);
+        ChromeFeatureList.setTestFeatures(features);
+
+        setDownloadPromptStatus(DownloadPromptStatus.SHOW_INITIAL);
+        TestThreadUtils.runOnUiThreadBlocking(() -> {
+            // Create fake directory options.
+            ArrayList<DirectoryOption> dirs = new ArrayList<>();
+            try (StrictModeContext ignored = StrictModeContext.allowDiskReads()) {
+                dirs.add(buildDirectoryOption(DirectoryOption.DownloadLocationDirectoryType.DEFAULT,
+                        PRIMARY_STORAGE_PATH));
+                dirs.add(buildDirectoryOption(
+                        DirectoryOption.DownloadLocationDirectoryType.ADDITIONAL,
+                        SECONDARY_STORAGE_PATH));
+            }
+            DownloadDirectoryProvider.getInstance().setDirectoryProviderForTesting(
+                    new TestDownloadDirectoryProvider(dirs));
+        });
+
+        mDialogCoordinator = new DownloadLocationDialogCoordinator();
+        mDialogCoordinator.initialize(mController);
+    }
+
+    private DirectoryOption buildDirectoryOption(
+            @DirectoryOption.DownloadLocationDirectoryType int type, String directoryPath) {
+        return new DirectoryOption("Download", directoryPath, 1024000, 1024000, type);
+    }
+
+    private void setDownloadPromptStatus(@DownloadPromptStatus int promptStatus) {
+        when(mPrefService.getInteger(Pref.PROMPT_FOR_DOWNLOAD_ANDROID)).thenReturn(promptStatus);
+    }
+
+    private void showDialog(
+            long totalBytes, @DownloadLocationDialogType int dialogType, String suggestedPath) {
+        TestThreadUtils.runOnUiThreadBlocking(() -> {
+            mDialogCoordinator.showDialog(
+                    getActivity(), mModalDialogManager, totalBytes, dialogType, suggestedPath);
+        });
+    }
+
+    private void assertTitle(@StringRes int titleId) {
+        onView(withText(getActivity().getString(titleId))).check(matches(isDisplayed()));
+    }
+
+    private void assertSubtitle(String subtitle) {
+        onView(withText(subtitle)).check(matches(isDisplayed()));
+    }
+
+    /**
+     * Verifies the state of the "don't show again" checkbox.
+     * @param checked The expected state of the checkbox. If null, the checkbox is expected to be
+     *         hidden.
+     */
+    private void assertDontShowAgainCheckbox(Boolean checked) {
+        if (checked == null) {
+            onView(withId(R.id.show_again_checkbox)).check(matches(not(isDisplayed())));
+        } else if (checked) {
+            onView(withId(R.id.show_again_checkbox)).check(matches(isChecked()));
+        } else {
+            onView(withId(R.id.show_again_checkbox)).check(matches(isNotChecked()));
+        }
+    }
+
+    @Test
+    @MediumTest
+    public void testDefaultLocationDialog() throws Exception {
+        showDialog(TOTAL_BYTES, DownloadLocationDialogType.DEFAULT, SUGGESTED_PATH);
+        assertTitle(R.string.download_location_dialog_title);
+        assertSubtitle(DownloadUtils.getStringForBytes(getActivity(), TOTAL_BYTES));
+        assertDontShowAgainCheckbox(true);
+    }
+
+    @Test
+    @MediumTest
+    public void testDefaultLocationDialogUnchecked() throws Exception {
+        setDownloadPromptStatus(DownloadPromptStatus.SHOW_PREFERENCE);
+        showDialog(TOTAL_BYTES, DownloadLocationDialogType.DEFAULT, SUGGESTED_PATH);
+        assertTitle(R.string.download_location_dialog_title);
+        assertSubtitle(DownloadUtils.getStringForBytes(getActivity(), TOTAL_BYTES));
+        assertDontShowAgainCheckbox(false);
+    }
+}
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/gesturenav/NavigationHandlerTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/gesturenav/NavigationHandlerTest.java
index 712aa00f..054ce6468 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/gesturenav/NavigationHandlerTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/gesturenav/NavigationHandlerTest.java
@@ -91,7 +91,8 @@
     }
 
     private Tab currentTab() {
-        return mActivityTestRule.getActivity().getActivityTabProvider().get();
+        return TestThreadUtils.runOnUiThreadBlockingNoException(
+                () -> mActivityTestRule.getActivity().getActivityTabProvider().get());
     }
 
     private void loadNewTabPage() {
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/signin/SyncConsentFragmentTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/signin/SyncConsentFragmentTest.java
index 1c358c7..717c3e3 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/signin/SyncConsentFragmentTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/signin/SyncConsentFragmentTest.java
@@ -46,7 +46,6 @@
 import org.chromium.base.test.util.CommandLineFlags;
 import org.chromium.base.test.util.CriteriaHelper;
 import org.chromium.base.test.util.Feature;
-import org.chromium.base.test.util.FlakyTest;
 import org.chromium.base.test.util.Matchers;
 import org.chromium.base.test.util.MetricsUtils.HistogramDelta;
 import org.chromium.chrome.R;
@@ -231,7 +230,6 @@
     @Test
     @LargeTest
     @Feature("RenderTest")
-    @FlakyTest(message = "https://crbug.com/1197578")
     public void testSigninFREFragmentWithAdultAccount() throws IOException {
         HistogramDelta countHistogram =
                 new HistogramDelta("Signin.AndroidDeviceAccountsNumberWhenEnteringFRE", 1);
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/widget/bottomsheet/BottomSheetControllerTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/widget/bottomsheet/BottomSheetControllerTest.java
index 683afff..453cf80 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/widget/bottomsheet/BottomSheetControllerTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/widget/bottomsheet/BottomSheetControllerTest.java
@@ -125,6 +125,12 @@
         });
     }
 
+    /** @return The height of the container view. */
+    private int getContainerHeight() {
+        return TestThreadUtils.runOnUiThreadBlockingNoException(
+                () -> mActivity.getActivityTabProvider().get().getView().getHeight());
+    }
+
     @Test
     @SmallTest
     @Feature({"BottomSheetController"})
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/customtabs/content/CustomTabActivityContentTestEnvironment.java b/chrome/android/junit/src/org/chromium/chrome/browser/customtabs/content/CustomTabActivityContentTestEnvironment.java
index 4e654c6..274dca3a 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/customtabs/content/CustomTabActivityContentTestEnvironment.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/customtabs/content/CustomTabActivityContentTestEnvironment.java
@@ -8,7 +8,6 @@
 import static org.mockito.ArgumentMatchers.anyBoolean;
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.doNothing;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.when;
 
@@ -25,9 +24,9 @@
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
+import org.chromium.base.Callback;
 import org.chromium.base.UserDataHost;
 import org.chromium.chrome.browser.ActivityTabProvider;
-import org.chromium.chrome.browser.ActivityTabProvider.ActivityTabObserver;
 import org.chromium.chrome.browser.IntentHandler;
 import org.chromium.chrome.browser.WarmupManager;
 import org.chromium.chrome.browser.WebContentsFactory;
@@ -108,7 +107,7 @@
     public final CustomTabActivityTabProvider tabProvider = new CustomTabActivityTabProvider();
 
     @Captor
-    public ArgumentCaptor<ActivityTabObserver> activityTabObserverCaptor;
+    public ArgumentCaptor<Callback<Tab>> activityTabObserverCaptor;
 
     // Captures the WebContents with which tabFromFactory is initialized
     @Captor public ArgumentCaptor<WebContents> webContentsCaptor;
@@ -143,9 +142,7 @@
 
         when(startupTabPreloader.takeTabIfMatchingOrDestroy(any(), anyInt())).thenReturn(null);
         when(reparentingTaskProvider.get(any())).thenReturn(reparentingTask);
-        doNothing()
-                .when(activityTabProvider)
-                .addObserverAndTrigger(activityTabObserverCaptor.capture());
+        when(activityTabProvider.addObserver(activityTabObserverCaptor.capture())).thenReturn(null);
     }
 
     @Override
@@ -200,8 +197,8 @@
 
     public void changeTab(Tab newTab) {
         when(activityTabProvider.get()).thenReturn(newTab);
-        for (ActivityTabObserver observer : activityTabObserverCaptor.getAllValues()) {
-            observer.onActivityTabChanged(newTab, false);
+        for (Callback<Tab> observer : activityTabObserverCaptor.getAllValues()) {
+            observer.onResult(newTab);
         }
     }
 
diff --git a/chrome/app/generated_resources.grd b/chrome/app/generated_resources.grd
index f91f4be..eedfc46 100644
--- a/chrome/app/generated_resources.grd
+++ b/chrome/app/generated_resources.grd
@@ -5717,6 +5717,12 @@
       <message name="IDS_NTP_MODULES_DRIVE_SENTENCE2" desc="Variant of the name of the Drive module in sentence case shown in various UIs.">
         Drive files
       </message>
+      <message name="IDS_NTP_MODULES_DRIVE_FILES_SENTENCE" desc="Name of the files in the Drive module in sentence case shown in various UIs.">
+        Files
+      </message>
+      <message name="IDS_NTP_MODULES_DRIVE_FILES_LOWER" desc="Name of the files in the Drive module in lowercase shown in various UIs.">
+        these files
+      </message>
       <message name="IDS_NTP_MODULES_DUMMY_LOWER" translateable="false" desc="Name of the dummy module in lowercase shown in various UIs.">
         this module
       </message>
diff --git a/chrome/app/generated_resources_grd/IDS_NTP_MODULES_DRIVE_FILES_LOWER.png.sha1 b/chrome/app/generated_resources_grd/IDS_NTP_MODULES_DRIVE_FILES_LOWER.png.sha1
new file mode 100644
index 0000000..c8b5b587
--- /dev/null
+++ b/chrome/app/generated_resources_grd/IDS_NTP_MODULES_DRIVE_FILES_LOWER.png.sha1
@@ -0,0 +1 @@
+6b3cfb1f6f6448e8db2bd7017c908c7c439dc319
\ No newline at end of file
diff --git a/chrome/app/generated_resources_grd/IDS_NTP_MODULES_DRIVE_FILES_SENTENCE.png.sha1 b/chrome/app/generated_resources_grd/IDS_NTP_MODULES_DRIVE_FILES_SENTENCE.png.sha1
new file mode 100644
index 0000000..cb13376
--- /dev/null
+++ b/chrome/app/generated_resources_grd/IDS_NTP_MODULES_DRIVE_FILES_SENTENCE.png.sha1
@@ -0,0 +1 @@
+55929ddb9acdca93232d2bb5c38c19297e2a62a9
\ No newline at end of file
diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn
index 4173e34..6ffb7af 100644
--- a/chrome/browser/BUILD.gn
+++ b/chrome/browser/BUILD.gn
@@ -1482,6 +1482,8 @@
     "resource_coordinator/utils.h",
     "resources_util.cc",
     "resources_util.h",
+    "safe_browsing/metrics/safe_browsing_metrics_provider.cc",
+    "safe_browsing/metrics/safe_browsing_metrics_provider.h",
     "search/search.cc",
     "search/search.h",
     "search/suggestions/suggestions_service_factory.cc",
diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc
index ad74e3d..615cc783 100644
--- a/chrome/browser/about_flags.cc
+++ b/chrome/browser/about_flags.cc
@@ -1496,6 +1496,13 @@
      "t3329139" /* variation_id */},
 };
 
+const FeatureEntry::FeatureParam kNtpDriveModuleFakeData[] = {
+    {ntp_features::kNtpDriveModuleDataParam, "fake"}};
+const FeatureEntry::FeatureVariation kNtpDriveModuleVariations[] = {
+    {"- Fake Data", kNtpDriveModuleFakeData,
+     base::size(kNtpDriveModuleFakeData), nullptr},
+};
+
 const FeatureEntry::FeatureParam kNtpRepeatableQueriesInsertPositionStart[] = {
     {ntp_features::kNtpRepeatableQueriesInsertPositionParam, "start"}};
 const FeatureEntry::FeatureParam kNtpRepeatableQueriesInsertPositionEnd[] = {
@@ -4650,7 +4657,9 @@
 
     {"ntp-drive-module", flag_descriptions::kNtpDriveModuleName,
      flag_descriptions::kNtpDriveModuleDescription, kOsDesktop,
-     FEATURE_VALUE_TYPE(ntp_features::kNtpDriveModule)},
+     FEATURE_WITH_PARAMS_VALUE_TYPE(ntp_features::kNtpDriveModule,
+                                    kNtpDriveModuleVariations,
+                                    "DesktopNtpModules")},
 
     {"ntp-recipe-tasks-module", flag_descriptions::kNtpRecipeTasksModuleName,
      flag_descriptions::kNtpRecipeTasksModuleDescription, kOsDesktop,
@@ -6844,11 +6853,6 @@
                                     "SCTAuditingVariations")},
 #endif  // !defined(OS_ANDROID)
 
-    {"insert-key-toggle-mode", flag_descriptions::kInsertKeyToggleModeName,
-     flag_descriptions::kInsertKeyToggleModeDescription,
-     kOsWin | kOsLinux | kOsCrOS,
-     FEATURE_VALUE_TYPE(blink::features::kInsertKeyToggleMode)},
-
 #if defined(OS_ANDROID)
     {"enable-autofill-password-account-indicator-footer",
      flag_descriptions::
diff --git a/chrome/browser/apps/app_service/app_platform_metrics_service_unittest.cc b/chrome/browser/apps/app_service/app_platform_metrics_service_unittest.cc
index 24877a9a..fcbdcd4d 100644
--- a/chrome/browser/apps/app_service/app_platform_metrics_service_unittest.cc
+++ b/chrome/browser/apps/app_service/app_platform_metrics_service_unittest.cc
@@ -181,8 +181,8 @@
   void ModifyInstance(const std::string& app_id,
                       aura::Window* window,
                       apps::InstanceState state) {
-    std::unique_ptr<apps::Instance> instance =
-        std::make_unique<apps::Instance>(app_id, window);
+    std::unique_ptr<apps::Instance> instance = std::make_unique<apps::Instance>(
+        app_id, std::make_unique<apps::Instance::InstanceKey>(window));
     instance->UpdateState(state, base::Time::Now());
 
     std::vector<std::unique_ptr<apps::Instance>> deltas;
diff --git a/chrome/browser/apps/app_service/publishers/extension_apps_chromeos.cc b/chrome/browser/apps/app_service/publishers/extension_apps_chromeos.cc
index bbf458d..aa058b9 100644
--- a/chrome/browser/apps/app_service/publishers/extension_apps_chromeos.cc
+++ b/chrome/browser/apps/app_service/publishers/extension_apps_chromeos.cc
@@ -754,8 +754,9 @@
     window = app_window_to_aura_window_[app_window];
   }
   std::vector<std::unique_ptr<apps::Instance>> deltas;
-  auto instance =
-      std::make_unique<apps::Instance>(app_window->extension_id(), window);
+  auto instance = std::make_unique<apps::Instance>(
+      app_window->extension_id(),
+      std::make_unique<apps::Instance::InstanceKey>(window));
   instance->SetLaunchId(GetLaunchId(app_window));
   instance->UpdateState(new_state, base::Time::Now());
   instance->SetBrowserContext(app_window->browser_context());
diff --git a/chrome/browser/apps/guest_view/web_view_browsertest.cc b/chrome/browser/apps/guest_view/web_view_browsertest.cc
index a58c0e8..c12faf35 100644
--- a/chrome/browser/apps/guest_view/web_view_browsertest.cc
+++ b/chrome/browser/apps/guest_view/web_view_browsertest.cc
@@ -1788,10 +1788,10 @@
 }
 
 // Creates a guest in a unattached state, then confirms that calling
-// |RenderFrameHost::ForEachFrame| on the embedder will include the guest's
-// frame.
+// |RenderFrameHost::ForEachRenderFrameHost| on the embedder will include the
+// guest's frame.
 IN_PROC_BROWSER_TEST_F(WebViewNewWindowTest,
-                       NewWindow_UnattachedVisitedByForEachFrame) {
+                       NewWindow_UnattachedVisitedByForEachRenderFrameHost) {
   TestHelper("testNewWindowDeferredAttachmentIndefinitely",
              "web_view/newwindow", NEEDS_TEST_SERVER);
   // The test creates two guests, one of which is created but left in an
@@ -1821,7 +1821,7 @@
       other_guest->GetMainFrame();
 
   EXPECT_THAT(
-      content::CollectAllFrames(embedder_main_frame),
+      content::CollectAllRenderFrameHosts(embedder_main_frame),
       testing::UnorderedElementsAre(embedder_main_frame, other_guest_main_frame,
                                     unattached_guest_main_frame));
 }
diff --git a/chrome/browser/apps/platform_apps/shortcut_manager.cc b/chrome/browser/apps/platform_apps/shortcut_manager.cc
index 768fd74..891cb72 100644
--- a/chrome/browser/apps/platform_apps/shortcut_manager.cc
+++ b/chrome/browser/apps/platform_apps/shortcut_manager.cc
@@ -97,7 +97,7 @@
              content::BrowserThread::UI) ||
          content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
 
-  extension_registry_observer_.Add(
+  extension_registry_observation_.Observe(
       extensions::ExtensionRegistry::Get(profile_));
   // Wait for extensions to be ready before running
   // UpdateShortcutsForAllAppsIfNeeded.
@@ -109,7 +109,7 @@
   ProfileManager* profile_manager = g_browser_process->profile_manager();
   // profile_manager might be NULL in testing environments.
   if (profile_manager) {
-    profile_storage_observer_.Add(
+    profile_storage_observation_.Observe(
         &profile_manager->GetProfileAttributesStorage());
   }
 }
diff --git a/chrome/browser/apps/platform_apps/shortcut_manager.h b/chrome/browser/apps/platform_apps/shortcut_manager.h
index 3154496..d683d9a 100644
--- a/chrome/browser/apps/platform_apps/shortcut_manager.h
+++ b/chrome/browser/apps/platform_apps/shortcut_manager.h
@@ -8,7 +8,7 @@
 #include <string>
 
 #include "base/memory/weak_ptr.h"
-#include "base/scoped_observer.h"
+#include "base/scoped_observation.h"
 #include "chrome/browser/profiles/profile_attributes_storage.h"
 #include "components/keyed_service/core/keyed_service.h"
 #include "extensions/browser/extension_registry.h"
@@ -62,11 +62,12 @@
   void DeleteApplicationShortcuts(const extensions::Extension* extension);
 
   Profile* profile_;
-  ScopedObserver<ProfileAttributesStorage, ProfileAttributesStorage::Observer>
-      profile_storage_observer_{this};
-  ScopedObserver<extensions::ExtensionRegistry,
-                 extensions::ExtensionRegistryObserver>
-      extension_registry_observer_{this};
+  base::ScopedObservation<ProfileAttributesStorage,
+                          ProfileAttributesStorage::Observer>
+      profile_storage_observation_{this};
+  base::ScopedObservation<extensions::ExtensionRegistry,
+                          extensions::ExtensionRegistryObserver>
+      extension_registry_observation_{this};
 
   base::WeakPtrFactory<AppShortcutManager> weak_ptr_factory_{this};
 };
diff --git a/chrome/browser/ash/borealis/borealis_window_manager_test_helper.cc b/chrome/browser/ash/borealis/borealis_window_manager_test_helper.cc
index a48a5c7..033913a 100644
--- a/chrome/browser/ash/borealis/borealis_window_manager_test_helper.cc
+++ b/chrome/browser/ash/borealis/borealis_window_manager_test_helper.cc
@@ -12,14 +12,16 @@
 ScopedTestWindow::ScopedTestWindow(std::unique_ptr<aura::Window> window,
                                    BorealisWindowManager* manager)
     : window_(std::move(window)), manager_(manager) {
-  apps::Instance instance(manager_->GetShelfAppId(window_.get()),
-                          window_.get());
+  apps::Instance instance(
+      manager_->GetShelfAppId(window_.get()),
+      std::make_unique<apps::Instance::InstanceKey>(window_.get()));
   manager_->OnInstanceUpdate(apps::InstanceUpdate(nullptr, &instance));
 }
 
 ScopedTestWindow::~ScopedTestWindow() {
-  apps::Instance instance(manager_->GetShelfAppId(window_.get()),
-                          window_.get());
+  apps::Instance instance(
+      manager_->GetShelfAppId(window_.get()),
+      std::make_unique<apps::Instance::InstanceKey>(window_.get()));
   std::unique_ptr<apps::Instance> delta = instance.Clone();
   delta->UpdateState(apps::InstanceState::kDestroyed, base::Time::Now());
   manager_->OnInstanceUpdate(apps::InstanceUpdate(&instance, delta.get()));
diff --git a/chrome/browser/ash/child_accounts/family_user_app_metrics_unittest.cc b/chrome/browser/ash/child_accounts/family_user_app_metrics_unittest.cc
index dcca3b9..e21741a0b 100644
--- a/chrome/browser/ash/child_accounts/family_user_app_metrics_unittest.cc
+++ b/chrome/browser/ash/child_accounts/family_user_app_metrics_unittest.cc
@@ -196,8 +196,9 @@
             ->InstanceRegistry();
     window_ = std::make_unique<aura::Window>(nullptr);
     window_->Init(ui::LAYER_NOT_DRAWN);
-    instances.push_back(
-        std::make_unique<apps::Instance>(/*app_id=*/"a", window_.get()));
+    instances.push_back(std::make_unique<apps::Instance>(
+        /*app_id=*/"a",
+        std::make_unique<apps::Instance::InstanceKey>(window_.get())));
     instance_registry.OnInstances(instances);
   }
 
diff --git a/chrome/browser/ash/child_accounts/family_user_chrome_activity_metrics_unittest.cc b/chrome/browser/ash/child_accounts/family_user_chrome_activity_metrics_unittest.cc
index 3823413..452b163 100644
--- a/chrome/browser/ash/child_accounts/family_user_chrome_activity_metrics_unittest.cc
+++ b/chrome/browser/ash/child_accounts/family_user_chrome_activity_metrics_unittest.cc
@@ -108,7 +108,8 @@
 
   void PushChromeAppInstance(aura::Window* window, apps::InstanceState state) {
     std::unique_ptr<apps::Instance> instance = std::make_unique<apps::Instance>(
-        app_time::GetChromeAppId().app_id(), window);
+        app_time::GetChromeAppId().app_id(),
+        std::make_unique<apps::Instance::InstanceKey>(window));
     instance->UpdateState(state, base::Time::Now());
 
     std::vector<std::unique_ptr<apps::Instance>> deltas;
diff --git a/chrome/browser/ash/file_system_provider/OWNERS b/chrome/browser/ash/file_system_provider/OWNERS
new file mode 100644
index 0000000..4dce310
--- /dev/null
+++ b/chrome/browser/ash/file_system_provider/OWNERS
@@ -0,0 +1 @@
+yamaguchi@chromium.org
diff --git a/chrome/browser/chromeos/file_system_provider/fileapi/backend_delegate.cc b/chrome/browser/ash/file_system_provider/fileapi/backend_delegate.cc
similarity index 83%
rename from chrome/browser/chromeos/file_system_provider/fileapi/backend_delegate.cc
rename to chrome/browser/ash/file_system_provider/fileapi/backend_delegate.cc
index a01b75a..eca8cdb 100644
--- a/chrome/browser/chromeos/file_system_provider/fileapi/backend_delegate.cc
+++ b/chrome/browser/ash/file_system_provider/fileapi/backend_delegate.cc
@@ -2,16 +2,16 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/chromeos/file_system_provider/fileapi/backend_delegate.h"
+#include "chrome/browser/ash/file_system_provider/fileapi/backend_delegate.h"
 
 #include <memory>
 
-#include "chrome/browser/chromeos/file_system_provider/fileapi/buffering_file_stream_reader.h"
-#include "chrome/browser/chromeos/file_system_provider/fileapi/buffering_file_stream_writer.h"
-#include "chrome/browser/chromeos/file_system_provider/fileapi/file_stream_reader.h"
-#include "chrome/browser/chromeos/file_system_provider/fileapi/file_stream_writer.h"
-#include "chrome/browser/chromeos/file_system_provider/fileapi/provider_async_file_util.h"
-#include "chrome/browser/chromeos/file_system_provider/fileapi/watcher_manager.h"
+#include "chrome/browser/ash/file_system_provider/fileapi/buffering_file_stream_reader.h"
+#include "chrome/browser/ash/file_system_provider/fileapi/buffering_file_stream_writer.h"
+#include "chrome/browser/ash/file_system_provider/fileapi/file_stream_reader.h"
+#include "chrome/browser/ash/file_system_provider/fileapi/file_stream_writer.h"
+#include "chrome/browser/ash/file_system_provider/fileapi/provider_async_file_util.h"
+#include "chrome/browser/ash/file_system_provider/fileapi/watcher_manager.h"
 #include "content/public/browser/browser_thread.h"
 #include "storage/browser/file_system/file_stream_reader.h"
 #include "storage/browser/file_system/file_stream_writer.h"
diff --git a/chrome/browser/chromeos/file_system_provider/fileapi/backend_delegate.h b/chrome/browser/ash/file_system_provider/fileapi/backend_delegate.h
similarity index 88%
rename from chrome/browser/chromeos/file_system_provider/fileapi/backend_delegate.h
rename to chrome/browser/ash/file_system_provider/fileapi/backend_delegate.h
index d472a13..ed63528 100644
--- a/chrome/browser/chromeos/file_system_provider/fileapi/backend_delegate.h
+++ b/chrome/browser/ash/file_system_provider/fileapi/backend_delegate.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CHROME_BROWSER_CHROMEOS_FILE_SYSTEM_PROVIDER_FILEAPI_BACKEND_DELEGATE_H_
-#define CHROME_BROWSER_CHROMEOS_FILE_SYSTEM_PROVIDER_FILEAPI_BACKEND_DELEGATE_H_
+#ifndef CHROME_BROWSER_ASH_FILE_SYSTEM_PROVIDER_FILEAPI_BACKEND_DELEGATE_H_
+#define CHROME_BROWSER_ASH_FILE_SYSTEM_PROVIDER_FILEAPI_BACKEND_DELEGATE_H_
 
 #include <stdint.h>
 
@@ -60,4 +60,4 @@
 }  // namespace file_system_provider
 }  // namespace chromeos
 
-#endif  // CHROME_BROWSER_CHROMEOS_FILE_SYSTEM_PROVIDER_FILEAPI_BACKEND_DELEGATE_H_
+#endif  // CHROME_BROWSER_ASH_FILE_SYSTEM_PROVIDER_FILEAPI_BACKEND_DELEGATE_H_
diff --git a/chrome/browser/chromeos/file_system_provider/fileapi/buffering_file_stream_reader.cc b/chrome/browser/ash/file_system_provider/fileapi/buffering_file_stream_reader.cc
similarity index 97%
rename from chrome/browser/chromeos/file_system_provider/fileapi/buffering_file_stream_reader.cc
rename to chrome/browser/ash/file_system_provider/fileapi/buffering_file_stream_reader.cc
index 2ec52e9..a7e3cf4 100644
--- a/chrome/browser/chromeos/file_system_provider/fileapi/buffering_file_stream_reader.cc
+++ b/chrome/browser/ash/file_system_provider/fileapi/buffering_file_stream_reader.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/chromeos/file_system_provider/fileapi/buffering_file_stream_reader.h"
+#include "chrome/browser/ash/file_system_provider/fileapi/buffering_file_stream_reader.h"
 
 #include <algorithm>
 #include <utility>
diff --git a/chrome/browser/chromeos/file_system_provider/fileapi/buffering_file_stream_reader.h b/chrome/browser/ash/file_system_provider/fileapi/buffering_file_stream_reader.h
similarity index 90%
rename from chrome/browser/chromeos/file_system_provider/fileapi/buffering_file_stream_reader.h
rename to chrome/browser/ash/file_system_provider/fileapi/buffering_file_stream_reader.h
index 1ae4ebb..589e169 100644
--- a/chrome/browser/chromeos/file_system_provider/fileapi/buffering_file_stream_reader.h
+++ b/chrome/browser/ash/file_system_provider/fileapi/buffering_file_stream_reader.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CHROME_BROWSER_CHROMEOS_FILE_SYSTEM_PROVIDER_FILEAPI_BUFFERING_FILE_STREAM_READER_H_
-#define CHROME_BROWSER_CHROMEOS_FILE_SYSTEM_PROVIDER_FILEAPI_BUFFERING_FILE_STREAM_READER_H_
+#ifndef CHROME_BROWSER_ASH_FILE_SYSTEM_PROVIDER_FILEAPI_BUFFERING_FILE_STREAM_READER_H_
+#define CHROME_BROWSER_ASH_FILE_SYSTEM_PROVIDER_FILEAPI_BUFFERING_FILE_STREAM_READER_H_
 
 #include <stdint.h>
 
@@ -78,4 +78,4 @@
 }  // namespace file_system_provider
 }  // namespace chromeos
 
-#endif  // CHROME_BROWSER_CHROMEOS_FILE_SYSTEM_PROVIDER_FILEAPI_BUFFERING_FILE_STREAM_READER_H_
+#endif  // CHROME_BROWSER_ASH_FILE_SYSTEM_PROVIDER_FILEAPI_BUFFERING_FILE_STREAM_READER_H_
diff --git a/chrome/browser/chromeos/file_system_provider/fileapi/buffering_file_stream_reader_unittest.cc b/chrome/browser/ash/file_system_provider/fileapi/buffering_file_stream_reader_unittest.cc
similarity index 99%
rename from chrome/browser/chromeos/file_system_provider/fileapi/buffering_file_stream_reader_unittest.cc
rename to chrome/browser/ash/file_system_provider/fileapi/buffering_file_stream_reader_unittest.cc
index be5cc76..de583c4 100644
--- a/chrome/browser/chromeos/file_system_provider/fileapi/buffering_file_stream_reader_unittest.cc
+++ b/chrome/browser/ash/file_system_provider/fileapi/buffering_file_stream_reader_unittest.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/chromeos/file_system_provider/fileapi/buffering_file_stream_reader.h"
+#include "chrome/browser/ash/file_system_provider/fileapi/buffering_file_stream_reader.h"
 
 #include <stdint.h>
 
diff --git a/chrome/browser/chromeos/file_system_provider/fileapi/buffering_file_stream_writer.cc b/chrome/browser/ash/file_system_provider/fileapi/buffering_file_stream_writer.cc
similarity index 98%
rename from chrome/browser/chromeos/file_system_provider/fileapi/buffering_file_stream_writer.cc
rename to chrome/browser/ash/file_system_provider/fileapi/buffering_file_stream_writer.cc
index 1f3cf7f..88d99bc 100644
--- a/chrome/browser/chromeos/file_system_provider/fileapi/buffering_file_stream_writer.cc
+++ b/chrome/browser/ash/file_system_provider/fileapi/buffering_file_stream_writer.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/chromeos/file_system_provider/fileapi/buffering_file_stream_writer.h"
+#include "chrome/browser/ash/file_system_provider/fileapi/buffering_file_stream_writer.h"
 
 #include <algorithm>
 #include <utility>
diff --git a/chrome/browser/chromeos/file_system_provider/fileapi/buffering_file_stream_writer.h b/chrome/browser/ash/file_system_provider/fileapi/buffering_file_stream_writer.h
similarity index 92%
rename from chrome/browser/chromeos/file_system_provider/fileapi/buffering_file_stream_writer.h
rename to chrome/browser/ash/file_system_provider/fileapi/buffering_file_stream_writer.h
index c23ec5f..e98d9ec 100644
--- a/chrome/browser/chromeos/file_system_provider/fileapi/buffering_file_stream_writer.h
+++ b/chrome/browser/ash/file_system_provider/fileapi/buffering_file_stream_writer.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CHROME_BROWSER_CHROMEOS_FILE_SYSTEM_PROVIDER_FILEAPI_BUFFERING_FILE_STREAM_WRITER_H_
-#define CHROME_BROWSER_CHROMEOS_FILE_SYSTEM_PROVIDER_FILEAPI_BUFFERING_FILE_STREAM_WRITER_H_
+#ifndef CHROME_BROWSER_ASH_FILE_SYSTEM_PROVIDER_FILEAPI_BUFFERING_FILE_STREAM_WRITER_H_
+#define CHROME_BROWSER_ASH_FILE_SYSTEM_PROVIDER_FILEAPI_BUFFERING_FILE_STREAM_WRITER_H_
 
 #include <memory>
 
@@ -96,4 +96,4 @@
 }  // namespace file_system_provider
 }  // namespace chromeos
 
-#endif  // CHROME_BROWSER_CHROMEOS_FILE_SYSTEM_PROVIDER_FILEAPI_BUFFERING_FILE_STREAM_WRITER_H_
+#endif  // CHROME_BROWSER_ASH_FILE_SYSTEM_PROVIDER_FILEAPI_BUFFERING_FILE_STREAM_WRITER_H_
diff --git a/chrome/browser/chromeos/file_system_provider/fileapi/buffering_file_stream_writer_unittest.cc b/chrome/browser/ash/file_system_provider/fileapi/buffering_file_stream_writer_unittest.cc
similarity index 99%
rename from chrome/browser/chromeos/file_system_provider/fileapi/buffering_file_stream_writer_unittest.cc
rename to chrome/browser/ash/file_system_provider/fileapi/buffering_file_stream_writer_unittest.cc
index 0513226..c167238d 100644
--- a/chrome/browser/chromeos/file_system_provider/fileapi/buffering_file_stream_writer_unittest.cc
+++ b/chrome/browser/ash/file_system_provider/fileapi/buffering_file_stream_writer_unittest.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/chromeos/file_system_provider/fileapi/buffering_file_stream_writer.h"
+#include "chrome/browser/ash/file_system_provider/fileapi/buffering_file_stream_writer.h"
 
 #include <memory>
 #include <string>
diff --git a/chrome/browser/chromeos/file_system_provider/fileapi/file_stream_reader.cc b/chrome/browser/ash/file_system_provider/fileapi/file_stream_reader.cc
similarity index 98%
rename from chrome/browser/chromeos/file_system_provider/fileapi/file_stream_reader.cc
rename to chrome/browser/ash/file_system_provider/fileapi/file_stream_reader.cc
index 6e05eb2..52f39c1 100644
--- a/chrome/browser/chromeos/file_system_provider/fileapi/file_stream_reader.cc
+++ b/chrome/browser/ash/file_system_provider/fileapi/file_stream_reader.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/chromeos/file_system_provider/fileapi/file_stream_reader.h"
+#include "chrome/browser/ash/file_system_provider/fileapi/file_stream_reader.h"
 
 #include <memory>
 #include <utility>
@@ -13,8 +13,8 @@
 #include "base/memory/ptr_util.h"
 #include "base/memory/ref_counted.h"
 #include "base/trace_event/trace_event.h"
+#include "chrome/browser/ash/file_system_provider/fileapi/provider_async_file_util.h"
 #include "chrome/browser/chromeos/file_system_provider/abort_callback.h"
-#include "chrome/browser/chromeos/file_system_provider/fileapi/provider_async_file_util.h"
 #include "chrome/browser/chromeos/file_system_provider/mount_path_util.h"
 #include "chrome/browser/chromeos/file_system_provider/provided_file_system_interface.h"
 #include "chrome/browser/chromeos/file_system_provider/scoped_file_opener.h"
diff --git a/chrome/browser/chromeos/file_system_provider/fileapi/file_stream_reader.h b/chrome/browser/ash/file_system_provider/fileapi/file_stream_reader.h
similarity index 94%
rename from chrome/browser/chromeos/file_system_provider/fileapi/file_stream_reader.h
rename to chrome/browser/ash/file_system_provider/fileapi/file_stream_reader.h
index f632f5b..9eb3464 100644
--- a/chrome/browser/chromeos/file_system_provider/fileapi/file_stream_reader.h
+++ b/chrome/browser/ash/file_system_provider/fileapi/file_stream_reader.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CHROME_BROWSER_CHROMEOS_FILE_SYSTEM_PROVIDER_FILEAPI_FILE_STREAM_READER_H_
-#define CHROME_BROWSER_CHROMEOS_FILE_SYSTEM_PROVIDER_FILEAPI_FILE_STREAM_READER_H_
+#ifndef CHROME_BROWSER_ASH_FILE_SYSTEM_PROVIDER_FILEAPI_FILE_STREAM_READER_H_
+#define CHROME_BROWSER_ASH_FILE_SYSTEM_PROVIDER_FILEAPI_FILE_STREAM_READER_H_
 
 #include <stdint.h>
 
@@ -115,4 +115,4 @@
 }  // namespace file_system_provider
 }  // namespace chromeos
 
-#endif  // CHROME_BROWSER_CHROMEOS_FILE_SYSTEM_PROVIDER_FILEAPI_FILE_STREAM_READER_H_
+#endif  // CHROME_BROWSER_ASH_FILE_SYSTEM_PROVIDER_FILEAPI_FILE_STREAM_READER_H_
diff --git a/chrome/browser/chromeos/file_system_provider/fileapi/file_stream_reader_unittest.cc b/chrome/browser/ash/file_system_provider/fileapi/file_stream_reader_unittest.cc
similarity index 99%
rename from chrome/browser/chromeos/file_system_provider/fileapi/file_stream_reader_unittest.cc
rename to chrome/browser/ash/file_system_provider/fileapi/file_stream_reader_unittest.cc
index 275033e..eb27cd5d 100644
--- a/chrome/browser/chromeos/file_system_provider/fileapi/file_stream_reader_unittest.cc
+++ b/chrome/browser/ash/file_system_provider/fileapi/file_stream_reader_unittest.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/chromeos/file_system_provider/fileapi/file_stream_reader.h"
+#include "chrome/browser/ash/file_system_provider/fileapi/file_stream_reader.h"
 
 #include <stddef.h>
 #include <stdint.h>
diff --git a/chrome/browser/chromeos/file_system_provider/fileapi/file_stream_writer.cc b/chrome/browser/ash/file_system_provider/fileapi/file_stream_writer.cc
similarity index 98%
rename from chrome/browser/chromeos/file_system_provider/fileapi/file_stream_writer.cc
rename to chrome/browser/ash/file_system_provider/fileapi/file_stream_writer.cc
index 1672866..37bbdf1 100644
--- a/chrome/browser/chromeos/file_system_provider/fileapi/file_stream_writer.cc
+++ b/chrome/browser/ash/file_system_provider/fileapi/file_stream_writer.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/chromeos/file_system_provider/fileapi/file_stream_writer.h"
+#include "chrome/browser/ash/file_system_provider/fileapi/file_stream_writer.h"
 
 #include <memory>
 #include <utility>
@@ -12,8 +12,8 @@
 #include "base/memory/ref_counted.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "base/trace_event/trace_event.h"
+#include "chrome/browser/ash/file_system_provider/fileapi/provider_async_file_util.h"
 #include "chrome/browser/chromeos/file_system_provider/abort_callback.h"
-#include "chrome/browser/chromeos/file_system_provider/fileapi/provider_async_file_util.h"
 #include "chrome/browser/chromeos/file_system_provider/mount_path_util.h"
 #include "chrome/browser/chromeos/file_system_provider/provided_file_system_interface.h"
 #include "chrome/browser/chromeos/file_system_provider/scoped_file_opener.h"
diff --git a/chrome/browser/chromeos/file_system_provider/fileapi/file_stream_writer.h b/chrome/browser/ash/file_system_provider/fileapi/file_stream_writer.h
similarity index 91%
rename from chrome/browser/chromeos/file_system_provider/fileapi/file_stream_writer.h
rename to chrome/browser/ash/file_system_provider/fileapi/file_stream_writer.h
index 5bbf4e5..b4c47b1 100644
--- a/chrome/browser/chromeos/file_system_provider/fileapi/file_stream_writer.h
+++ b/chrome/browser/ash/file_system_provider/fileapi/file_stream_writer.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CHROME_BROWSER_CHROMEOS_FILE_SYSTEM_PROVIDER_FILEAPI_FILE_STREAM_WRITER_H_
-#define CHROME_BROWSER_CHROMEOS_FILE_SYSTEM_PROVIDER_FILEAPI_FILE_STREAM_WRITER_H_
+#ifndef CHROME_BROWSER_ASH_FILE_SYSTEM_PROVIDER_FILEAPI_FILE_STREAM_WRITER_H_
+#define CHROME_BROWSER_ASH_FILE_SYSTEM_PROVIDER_FILEAPI_FILE_STREAM_WRITER_H_
 
 #include <stdint.h>
 
@@ -86,4 +86,4 @@
 }  // namespace file_system_provider
 }  // namespace chromeos
 
-#endif  // CHROME_BROWSER_CHROMEOS_FILE_SYSTEM_PROVIDER_FILEAPI_FILE_STREAM_WRITER_H_
+#endif  // CHROME_BROWSER_ASH_FILE_SYSTEM_PROVIDER_FILEAPI_FILE_STREAM_WRITER_H_
diff --git a/chrome/browser/chromeos/file_system_provider/fileapi/file_stream_writer_unittest.cc b/chrome/browser/ash/file_system_provider/fileapi/file_stream_writer_unittest.cc
similarity index 98%
rename from chrome/browser/chromeos/file_system_provider/fileapi/file_stream_writer_unittest.cc
rename to chrome/browser/ash/file_system_provider/fileapi/file_stream_writer_unittest.cc
index 90303189..15bbc0e 100644
--- a/chrome/browser/chromeos/file_system_provider/fileapi/file_stream_writer_unittest.cc
+++ b/chrome/browser/ash/file_system_provider/fileapi/file_stream_writer_unittest.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/chromeos/file_system_provider/fileapi/file_stream_writer.h"
+#include "chrome/browser/ash/file_system_provider/fileapi/file_stream_writer.h"
 
 #include <stddef.h>
 #include <stdint.h>
diff --git a/chrome/browser/chromeos/file_system_provider/fileapi/provider_async_file_util.cc b/chrome/browser/ash/file_system_provider/fileapi/provider_async_file_util.cc
similarity index 99%
rename from chrome/browser/chromeos/file_system_provider/fileapi/provider_async_file_util.cc
rename to chrome/browser/ash/file_system_provider/fileapi/provider_async_file_util.cc
index 17b667a..0113870 100644
--- a/chrome/browser/chromeos/file_system_provider/fileapi/provider_async_file_util.cc
+++ b/chrome/browser/ash/file_system_provider/fileapi/provider_async_file_util.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/chromeos/file_system_provider/fileapi/provider_async_file_util.h"
+#include "chrome/browser/ash/file_system_provider/fileapi/provider_async_file_util.h"
 
 #include <algorithm>
 #include <utility>
diff --git a/chrome/browser/chromeos/file_system_provider/fileapi/provider_async_file_util.h b/chrome/browser/ash/file_system_provider/fileapi/provider_async_file_util.h
similarity index 93%
rename from chrome/browser/chromeos/file_system_provider/fileapi/provider_async_file_util.h
rename to chrome/browser/ash/file_system_provider/fileapi/provider_async_file_util.h
index 8a325558..325d1a1 100644
--- a/chrome/browser/chromeos/file_system_provider/fileapi/provider_async_file_util.h
+++ b/chrome/browser/ash/file_system_provider/fileapi/provider_async_file_util.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CHROME_BROWSER_CHROMEOS_FILE_SYSTEM_PROVIDER_FILEAPI_PROVIDER_ASYNC_FILE_UTIL_H_
-#define CHROME_BROWSER_CHROMEOS_FILE_SYSTEM_PROVIDER_FILEAPI_PROVIDER_ASYNC_FILE_UTIL_H_
+#ifndef CHROME_BROWSER_ASH_FILE_SYSTEM_PROVIDER_FILEAPI_PROVIDER_ASYNC_FILE_UTIL_H_
+#define CHROME_BROWSER_ASH_FILE_SYSTEM_PROVIDER_FILEAPI_PROVIDER_ASYNC_FILE_UTIL_H_
 
 #include <stdint.h>
 
@@ -109,4 +109,4 @@
 }  // namespace file_system_provider
 }  // namespace chromeos
 
-#endif  // CHROME_BROWSER_CHROMEOS_FILE_SYSTEM_PROVIDER_FILEAPI_PROVIDER_ASYNC_FILE_UTIL_H_
+#endif  // CHROME_BROWSER_ASH_FILE_SYSTEM_PROVIDER_FILEAPI_PROVIDER_ASYNC_FILE_UTIL_H_
diff --git a/chrome/browser/chromeos/file_system_provider/fileapi/provider_async_file_util_unittest.cc b/chrome/browser/ash/file_system_provider/fileapi/provider_async_file_util_unittest.cc
similarity index 99%
rename from chrome/browser/chromeos/file_system_provider/fileapi/provider_async_file_util_unittest.cc
rename to chrome/browser/ash/file_system_provider/fileapi/provider_async_file_util_unittest.cc
index f69807a..8d75fde8 100644
--- a/chrome/browser/chromeos/file_system_provider/fileapi/provider_async_file_util_unittest.cc
+++ b/chrome/browser/ash/file_system_provider/fileapi/provider_async_file_util_unittest.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/chromeos/file_system_provider/fileapi/provider_async_file_util.h"
+#include "chrome/browser/ash/file_system_provider/fileapi/provider_async_file_util.h"
 
 #include <stdint.h>
 
diff --git a/chrome/browser/chromeos/file_system_provider/fileapi/watcher_manager.cc b/chrome/browser/ash/file_system_provider/fileapi/watcher_manager.cc
similarity index 97%
rename from chrome/browser/chromeos/file_system_provider/fileapi/watcher_manager.cc
rename to chrome/browser/ash/file_system_provider/fileapi/watcher_manager.cc
index 4a2a966..e50c796 100644
--- a/chrome/browser/chromeos/file_system_provider/fileapi/watcher_manager.cc
+++ b/chrome/browser/ash/file_system_provider/fileapi/watcher_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 "chrome/browser/chromeos/file_system_provider/fileapi/watcher_manager.h"
+#include "chrome/browser/ash/file_system_provider/fileapi/watcher_manager.h"
 
 #include "base/bind.h"
 #include "base/files/file.h"
diff --git a/chrome/browser/chromeos/file_system_provider/fileapi/watcher_manager.h b/chrome/browser/ash/file_system_provider/fileapi/watcher_manager.h
similarity index 79%
rename from chrome/browser/chromeos/file_system_provider/fileapi/watcher_manager.h
rename to chrome/browser/ash/file_system_provider/fileapi/watcher_manager.h
index c6442969..9cd56a9 100644
--- a/chrome/browser/chromeos/file_system_provider/fileapi/watcher_manager.h
+++ b/chrome/browser/ash/file_system_provider/fileapi/watcher_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 CHROME_BROWSER_CHROMEOS_FILE_SYSTEM_PROVIDER_FILEAPI_WATCHER_MANAGER_H_
-#define CHROME_BROWSER_CHROMEOS_FILE_SYSTEM_PROVIDER_FILEAPI_WATCHER_MANAGER_H_
+#ifndef CHROME_BROWSER_ASH_FILE_SYSTEM_PROVIDER_FILEAPI_WATCHER_MANAGER_H_
+#define CHROME_BROWSER_ASH_FILE_SYSTEM_PROVIDER_FILEAPI_WATCHER_MANAGER_H_
 
 #include "storage/browser/file_system/watcher_manager.h"
 
@@ -33,4 +33,4 @@
 }  // namespace file_system_provider
 }  // namespace chromeos
 
-#endif  // CHROME_BROWSER_CHROMEOS_FILE_SYSTEM_PROVIDER_FILEAPI_WATCHER_MANAGER_H_
+#endif  // CHROME_BROWSER_ASH_FILE_SYSTEM_PROVIDER_FILEAPI_WATCHER_MANAGER_H_
diff --git a/chrome/browser/chromeos/file_system_provider/operations/abort.cc b/chrome/browser/ash/file_system_provider/operations/abort.cc
similarity index 95%
rename from chrome/browser/chromeos/file_system_provider/operations/abort.cc
rename to chrome/browser/ash/file_system_provider/operations/abort.cc
index cac9c4d8..c58f54b 100644
--- a/chrome/browser/chromeos/file_system_provider/operations/abort.cc
+++ b/chrome/browser/ash/file_system_provider/operations/abort.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/chromeos/file_system_provider/operations/abort.h"
+#include "chrome/browser/ash/file_system_provider/operations/abort.h"
 
 #include <string>
 
diff --git a/chrome/browser/chromeos/file_system_provider/operations/abort.h b/chrome/browser/ash/file_system_provider/operations/abort.h
similarity index 83%
rename from chrome/browser/chromeos/file_system_provider/operations/abort.h
rename to chrome/browser/ash/file_system_provider/operations/abort.h
index bc80a029..5b216dac 100644
--- a/chrome/browser/chromeos/file_system_provider/operations/abort.h
+++ b/chrome/browser/ash/file_system_provider/operations/abort.h
@@ -2,14 +2,14 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CHROME_BROWSER_CHROMEOS_FILE_SYSTEM_PROVIDER_OPERATIONS_ABORT_H_
-#define CHROME_BROWSER_CHROMEOS_FILE_SYSTEM_PROVIDER_OPERATIONS_ABORT_H_
+#ifndef CHROME_BROWSER_ASH_FILE_SYSTEM_PROVIDER_OPERATIONS_ABORT_H_
+#define CHROME_BROWSER_ASH_FILE_SYSTEM_PROVIDER_OPERATIONS_ABORT_H_
 
 #include <memory>
 
 #include "base/files/file.h"
 #include "base/macros.h"
-#include "chrome/browser/chromeos/file_system_provider/operations/operation.h"
+#include "chrome/browser/ash/file_system_provider/operations/operation.h"
 #include "chrome/browser/chromeos/file_system_provider/provided_file_system_info.h"
 #include "chrome/browser/chromeos/file_system_provider/provided_file_system_interface.h"
 #include "chrome/browser/chromeos/file_system_provider/request_value.h"
@@ -52,4 +52,4 @@
 }  // namespace file_system_provider
 }  // namespace chromeos
 
-#endif  // CHROME_BROWSER_CHROMEOS_FILE_SYSTEM_PROVIDER_OPERATIONS_ABORT_H_
+#endif  // CHROME_BROWSER_ASH_FILE_SYSTEM_PROVIDER_OPERATIONS_ABORT_H_
diff --git a/chrome/browser/chromeos/file_system_provider/operations/abort_unittest.cc b/chrome/browser/ash/file_system_provider/operations/abort_unittest.cc
similarity index 96%
rename from chrome/browser/chromeos/file_system_provider/operations/abort_unittest.cc
rename to chrome/browser/ash/file_system_provider/operations/abort_unittest.cc
index 8316d72..4d7fc017 100644
--- a/chrome/browser/chromeos/file_system_provider/operations/abort_unittest.cc
+++ b/chrome/browser/ash/file_system_provider/operations/abort_unittest.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/chromeos/file_system_provider/operations/abort.h"
+#include "chrome/browser/ash/file_system_provider/operations/abort.h"
 
 #include <memory>
 #include <string>
@@ -11,8 +11,8 @@
 #include "base/bind.h"
 #include "base/files/file.h"
 #include "base/files/file_path.h"
+#include "chrome/browser/ash/file_system_provider/operations/test_util.h"
 #include "chrome/browser/chromeos/file_system_provider/icon_set.h"
-#include "chrome/browser/chromeos/file_system_provider/operations/test_util.h"
 #include "chrome/browser/chromeos/file_system_provider/provided_file_system_interface.h"
 #include "chrome/common/extensions/api/file_system_provider.h"
 #include "chrome/common/extensions/api/file_system_provider_capabilities/file_system_provider_capabilities_handler.h"
diff --git a/chrome/browser/chromeos/file_system_provider/operations/add_watcher.cc b/chrome/browser/ash/file_system_provider/operations/add_watcher.cc
similarity index 95%
rename from chrome/browser/chromeos/file_system_provider/operations/add_watcher.cc
rename to chrome/browser/ash/file_system_provider/operations/add_watcher.cc
index 802664b5..e4b6727 100644
--- a/chrome/browser/chromeos/file_system_provider/operations/add_watcher.cc
+++ b/chrome/browser/ash/file_system_provider/operations/add_watcher.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/chromeos/file_system_provider/operations/add_watcher.h"
+#include "chrome/browser/ash/file_system_provider/operations/add_watcher.h"
 
 #include <string>
 
diff --git a/chrome/browser/chromeos/file_system_provider/operations/add_watcher.h b/chrome/browser/ash/file_system_provider/operations/add_watcher.h
similarity index 84%
rename from chrome/browser/chromeos/file_system_provider/operations/add_watcher.h
rename to chrome/browser/ash/file_system_provider/operations/add_watcher.h
index ae2f8cb..d625bd1 100644
--- a/chrome/browser/chromeos/file_system_provider/operations/add_watcher.h
+++ b/chrome/browser/ash/file_system_provider/operations/add_watcher.h
@@ -2,14 +2,14 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CHROME_BROWSER_CHROMEOS_FILE_SYSTEM_PROVIDER_OPERATIONS_ADD_WATCHER_H_
-#define CHROME_BROWSER_CHROMEOS_FILE_SYSTEM_PROVIDER_OPERATIONS_ADD_WATCHER_H_
+#ifndef CHROME_BROWSER_ASH_FILE_SYSTEM_PROVIDER_OPERATIONS_ADD_WATCHER_H_
+#define CHROME_BROWSER_ASH_FILE_SYSTEM_PROVIDER_OPERATIONS_ADD_WATCHER_H_
 
 #include <memory>
 
 #include "base/files/file.h"
 #include "base/macros.h"
-#include "chrome/browser/chromeos/file_system_provider/operations/operation.h"
+#include "chrome/browser/ash/file_system_provider/operations/operation.h"
 #include "chrome/browser/chromeos/file_system_provider/provided_file_system_info.h"
 #include "chrome/browser/chromeos/file_system_provider/provided_file_system_interface.h"
 #include "chrome/browser/chromeos/file_system_provider/request_value.h"
@@ -60,4 +60,4 @@
 }  // namespace file_system_provider
 }  // namespace chromeos
 
-#endif  // CHROME_BROWSER_CHROMEOS_FILE_SYSTEM_PROVIDER_OPERATIONS_ADD_WATCHER_H_
+#endif  // CHROME_BROWSER_ASH_FILE_SYSTEM_PROVIDER_OPERATIONS_ADD_WATCHER_H_
diff --git a/chrome/browser/chromeos/file_system_provider/operations/add_watcher_unittest.cc b/chrome/browser/ash/file_system_provider/operations/add_watcher_unittest.cc
similarity index 96%
rename from chrome/browser/chromeos/file_system_provider/operations/add_watcher_unittest.cc
rename to chrome/browser/ash/file_system_provider/operations/add_watcher_unittest.cc
index 78b16ee..74619f2 100644
--- a/chrome/browser/chromeos/file_system_provider/operations/add_watcher_unittest.cc
+++ b/chrome/browser/ash/file_system_provider/operations/add_watcher_unittest.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/chromeos/file_system_provider/operations/add_watcher.h"
+#include "chrome/browser/ash/file_system_provider/operations/add_watcher.h"
 
 #include <memory>
 #include <string>
@@ -11,8 +11,8 @@
 #include "base/bind.h"
 #include "base/files/file.h"
 #include "base/files/file_path.h"
+#include "chrome/browser/ash/file_system_provider/operations/test_util.h"
 #include "chrome/browser/chromeos/file_system_provider/icon_set.h"
-#include "chrome/browser/chromeos/file_system_provider/operations/test_util.h"
 #include "chrome/browser/chromeos/file_system_provider/provided_file_system_interface.h"
 #include "chrome/common/extensions/api/file_system_provider.h"
 #include "chrome/common/extensions/api/file_system_provider_capabilities/file_system_provider_capabilities_handler.h"
diff --git a/chrome/browser/chromeos/file_system_provider/operations/close_file.cc b/chrome/browser/ash/file_system_provider/operations/close_file.cc
similarity index 95%
rename from chrome/browser/chromeos/file_system_provider/operations/close_file.cc
rename to chrome/browser/ash/file_system_provider/operations/close_file.cc
index 9be7944..48335b2 100644
--- a/chrome/browser/chromeos/file_system_provider/operations/close_file.cc
+++ b/chrome/browser/ash/file_system_provider/operations/close_file.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/chromeos/file_system_provider/operations/close_file.h"
+#include "chrome/browser/ash/file_system_provider/operations/close_file.h"
 
 #include <string>
 
diff --git a/chrome/browser/chromeos/file_system_provider/operations/close_file.h b/chrome/browser/ash/file_system_provider/operations/close_file.h
similarity index 84%
rename from chrome/browser/chromeos/file_system_provider/operations/close_file.h
rename to chrome/browser/ash/file_system_provider/operations/close_file.h
index 59a84f4..6f7288e 100644
--- a/chrome/browser/chromeos/file_system_provider/operations/close_file.h
+++ b/chrome/browser/ash/file_system_provider/operations/close_file.h
@@ -2,14 +2,14 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CHROME_BROWSER_CHROMEOS_FILE_SYSTEM_PROVIDER_OPERATIONS_CLOSE_FILE_H_
-#define CHROME_BROWSER_CHROMEOS_FILE_SYSTEM_PROVIDER_OPERATIONS_CLOSE_FILE_H_
+#ifndef CHROME_BROWSER_ASH_FILE_SYSTEM_PROVIDER_OPERATIONS_CLOSE_FILE_H_
+#define CHROME_BROWSER_ASH_FILE_SYSTEM_PROVIDER_OPERATIONS_CLOSE_FILE_H_
 
 #include <memory>
 
 #include "base/files/file.h"
 #include "base/macros.h"
-#include "chrome/browser/chromeos/file_system_provider/operations/operation.h"
+#include "chrome/browser/ash/file_system_provider/operations/operation.h"
 #include "chrome/browser/chromeos/file_system_provider/provided_file_system_info.h"
 #include "chrome/browser/chromeos/file_system_provider/provided_file_system_interface.h"
 #include "chrome/browser/chromeos/file_system_provider/request_value.h"
@@ -54,4 +54,4 @@
 }  // namespace file_system_provider
 }  // namespace chromeos
 
-#endif  // CHROME_BROWSER_CHROMEOS_FILE_SYSTEM_PROVIDER_OPERATIONS_CLOSE_FILE_H_
+#endif  // CHROME_BROWSER_ASH_FILE_SYSTEM_PROVIDER_OPERATIONS_CLOSE_FILE_H_
diff --git a/chrome/browser/chromeos/file_system_provider/operations/close_file_unittest.cc b/chrome/browser/ash/file_system_provider/operations/close_file_unittest.cc
similarity index 96%
rename from chrome/browser/chromeos/file_system_provider/operations/close_file_unittest.cc
rename to chrome/browser/ash/file_system_provider/operations/close_file_unittest.cc
index 02c9b68..9104eb9 100644
--- a/chrome/browser/chromeos/file_system_provider/operations/close_file_unittest.cc
+++ b/chrome/browser/ash/file_system_provider/operations/close_file_unittest.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/chromeos/file_system_provider/operations/close_file.h"
+#include "chrome/browser/ash/file_system_provider/operations/close_file.h"
 
 #include <memory>
 #include <string>
@@ -11,8 +11,8 @@
 #include "base/bind.h"
 #include "base/files/file.h"
 #include "base/files/file_path.h"
+#include "chrome/browser/ash/file_system_provider/operations/test_util.h"
 #include "chrome/browser/chromeos/file_system_provider/icon_set.h"
-#include "chrome/browser/chromeos/file_system_provider/operations/test_util.h"
 #include "chrome/browser/chromeos/file_system_provider/provided_file_system_interface.h"
 #include "chrome/common/extensions/api/file_system_provider.h"
 #include "chrome/common/extensions/api/file_system_provider_capabilities/file_system_provider_capabilities_handler.h"
diff --git a/chrome/browser/chromeos/file_system_provider/operations/configure.cc b/chrome/browser/ash/file_system_provider/operations/configure.cc
similarity index 95%
rename from chrome/browser/chromeos/file_system_provider/operations/configure.cc
rename to chrome/browser/ash/file_system_provider/operations/configure.cc
index eec08373..611dd045a 100644
--- a/chrome/browser/chromeos/file_system_provider/operations/configure.cc
+++ b/chrome/browser/ash/file_system_provider/operations/configure.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/chromeos/file_system_provider/operations/configure.h"
+#include "chrome/browser/ash/file_system_provider/operations/configure.h"
 
 #include "base/values.h"
 #include "chrome/common/extensions/api/file_system_provider.h"
diff --git a/chrome/browser/chromeos/file_system_provider/operations/configure.h b/chrome/browser/ash/file_system_provider/operations/configure.h
similarity index 81%
rename from chrome/browser/chromeos/file_system_provider/operations/configure.h
rename to chrome/browser/ash/file_system_provider/operations/configure.h
index 438fd71..481737f 100644
--- a/chrome/browser/chromeos/file_system_provider/operations/configure.h
+++ b/chrome/browser/ash/file_system_provider/operations/configure.h
@@ -2,14 +2,14 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CHROME_BROWSER_CHROMEOS_FILE_SYSTEM_PROVIDER_OPERATIONS_CONFIGURE_H_
-#define CHROME_BROWSER_CHROMEOS_FILE_SYSTEM_PROVIDER_OPERATIONS_CONFIGURE_H_
+#ifndef CHROME_BROWSER_ASH_FILE_SYSTEM_PROVIDER_OPERATIONS_CONFIGURE_H_
+#define CHROME_BROWSER_ASH_FILE_SYSTEM_PROVIDER_OPERATIONS_CONFIGURE_H_
 
 #include <memory>
 
 #include "base/files/file.h"
 #include "base/macros.h"
-#include "chrome/browser/chromeos/file_system_provider/operations/operation.h"
+#include "chrome/browser/ash/file_system_provider/operations/operation.h"
 #include "storage/browser/file_system/async_file_util.h"
 
 namespace extensions {
@@ -51,4 +51,4 @@
 }  // namespace file_system_provider
 }  // namespace chromeos
 
-#endif  // CHROME_BROWSER_CHROMEOS_FILE_SYSTEM_PROVIDER_OPERATIONS_CONFIGURE_H_
+#endif  // CHROME_BROWSER_ASH_FILE_SYSTEM_PROVIDER_OPERATIONS_CONFIGURE_H_
diff --git a/chrome/browser/chromeos/file_system_provider/operations/configure_unittest.cc b/chrome/browser/ash/file_system_provider/operations/configure_unittest.cc
similarity index 96%
rename from chrome/browser/chromeos/file_system_provider/operations/configure_unittest.cc
rename to chrome/browser/ash/file_system_provider/operations/configure_unittest.cc
index b8fdbca..fc43cd6 100644
--- a/chrome/browser/chromeos/file_system_provider/operations/configure_unittest.cc
+++ b/chrome/browser/ash/file_system_provider/operations/configure_unittest.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/chromeos/file_system_provider/operations/configure.h"
+#include "chrome/browser/ash/file_system_provider/operations/configure.h"
 
 #include <memory>
 #include <string>
@@ -11,8 +11,8 @@
 #include "base/bind.h"
 #include "base/files/file.h"
 #include "base/files/file_path.h"
+#include "chrome/browser/ash/file_system_provider/operations/test_util.h"
 #include "chrome/browser/chromeos/file_system_provider/icon_set.h"
-#include "chrome/browser/chromeos/file_system_provider/operations/test_util.h"
 #include "chrome/browser/chromeos/file_system_provider/provided_file_system_interface.h"
 #include "chrome/common/extensions/api/file_system_provider.h"
 #include "chrome/common/extensions/api/file_system_provider_capabilities/file_system_provider_capabilities_handler.h"
diff --git a/chrome/browser/chromeos/file_system_provider/operations/copy_entry.cc b/chrome/browser/ash/file_system_provider/operations/copy_entry.cc
similarity index 96%
rename from chrome/browser/chromeos/file_system_provider/operations/copy_entry.cc
rename to chrome/browser/ash/file_system_provider/operations/copy_entry.cc
index ebc5dbe..2d30381 100644
--- a/chrome/browser/chromeos/file_system_provider/operations/copy_entry.cc
+++ b/chrome/browser/ash/file_system_provider/operations/copy_entry.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/chromeos/file_system_provider/operations/copy_entry.h"
+#include "chrome/browser/ash/file_system_provider/operations/copy_entry.h"
 
 #include <string>
 
diff --git a/chrome/browser/chromeos/file_system_provider/operations/copy_entry.h b/chrome/browser/ash/file_system_provider/operations/copy_entry.h
similarity index 84%
rename from chrome/browser/chromeos/file_system_provider/operations/copy_entry.h
rename to chrome/browser/ash/file_system_provider/operations/copy_entry.h
index da93c79..bbc2f3e 100644
--- a/chrome/browser/chromeos/file_system_provider/operations/copy_entry.h
+++ b/chrome/browser/ash/file_system_provider/operations/copy_entry.h
@@ -2,14 +2,14 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CHROME_BROWSER_CHROMEOS_FILE_SYSTEM_PROVIDER_OPERATIONS_COPY_ENTRY_H_
-#define CHROME_BROWSER_CHROMEOS_FILE_SYSTEM_PROVIDER_OPERATIONS_COPY_ENTRY_H_
+#ifndef CHROME_BROWSER_ASH_FILE_SYSTEM_PROVIDER_OPERATIONS_COPY_ENTRY_H_
+#define CHROME_BROWSER_ASH_FILE_SYSTEM_PROVIDER_OPERATIONS_COPY_ENTRY_H_
 
 #include <memory>
 
 #include "base/files/file.h"
 #include "base/macros.h"
-#include "chrome/browser/chromeos/file_system_provider/operations/operation.h"
+#include "chrome/browser/ash/file_system_provider/operations/operation.h"
 #include "chrome/browser/chromeos/file_system_provider/provided_file_system_info.h"
 #include "chrome/browser/chromeos/file_system_provider/provided_file_system_interface.h"
 #include "chrome/browser/chromeos/file_system_provider/request_value.h"
@@ -58,4 +58,4 @@
 }  // namespace file_system_provider
 }  // namespace chromeos
 
-#endif  // CHROME_BROWSER_CHROMEOS_FILE_SYSTEM_PROVIDER_OPERATIONS_COPY_ENTRY_H_
+#endif  // CHROME_BROWSER_ASH_FILE_SYSTEM_PROVIDER_OPERATIONS_COPY_ENTRY_H_
diff --git a/chrome/browser/chromeos/file_system_provider/operations/copy_entry_unittest.cc b/chrome/browser/ash/file_system_provider/operations/copy_entry_unittest.cc
similarity index 97%
rename from chrome/browser/chromeos/file_system_provider/operations/copy_entry_unittest.cc
rename to chrome/browser/ash/file_system_provider/operations/copy_entry_unittest.cc
index 455b804..ef681df5 100644
--- a/chrome/browser/chromeos/file_system_provider/operations/copy_entry_unittest.cc
+++ b/chrome/browser/ash/file_system_provider/operations/copy_entry_unittest.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/chromeos/file_system_provider/operations/copy_entry.h"
+#include "chrome/browser/ash/file_system_provider/operations/copy_entry.h"
 
 #include <memory>
 #include <string>
@@ -11,8 +11,8 @@
 #include "base/bind.h"
 #include "base/files/file.h"
 #include "base/files/file_path.h"
+#include "chrome/browser/ash/file_system_provider/operations/test_util.h"
 #include "chrome/browser/chromeos/file_system_provider/icon_set.h"
-#include "chrome/browser/chromeos/file_system_provider/operations/test_util.h"
 #include "chrome/browser/chromeos/file_system_provider/provided_file_system_interface.h"
 #include "chrome/common/extensions/api/file_system_provider.h"
 #include "chrome/common/extensions/api/file_system_provider_capabilities/file_system_provider_capabilities_handler.h"
diff --git a/chrome/browser/chromeos/file_system_provider/operations/create_directory.cc b/chrome/browser/ash/file_system_provider/operations/create_directory.cc
similarity index 95%
rename from chrome/browser/chromeos/file_system_provider/operations/create_directory.cc
rename to chrome/browser/ash/file_system_provider/operations/create_directory.cc
index d2c2d51..fe82ccc 100644
--- a/chrome/browser/chromeos/file_system_provider/operations/create_directory.cc
+++ b/chrome/browser/ash/file_system_provider/operations/create_directory.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/chromeos/file_system_provider/operations/create_directory.h"
+#include "chrome/browser/ash/file_system_provider/operations/create_directory.h"
 
 #include <string>
 
diff --git a/chrome/browser/chromeos/file_system_provider/operations/create_directory.h b/chrome/browser/ash/file_system_provider/operations/create_directory.h
similarity index 84%
rename from chrome/browser/chromeos/file_system_provider/operations/create_directory.h
rename to chrome/browser/ash/file_system_provider/operations/create_directory.h
index dcef9d6..58e80e6 100644
--- a/chrome/browser/chromeos/file_system_provider/operations/create_directory.h
+++ b/chrome/browser/ash/file_system_provider/operations/create_directory.h
@@ -2,14 +2,14 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CHROME_BROWSER_CHROMEOS_FILE_SYSTEM_PROVIDER_OPERATIONS_CREATE_DIRECTORY_H_
-#define CHROME_BROWSER_CHROMEOS_FILE_SYSTEM_PROVIDER_OPERATIONS_CREATE_DIRECTORY_H_
+#ifndef CHROME_BROWSER_ASH_FILE_SYSTEM_PROVIDER_OPERATIONS_CREATE_DIRECTORY_H_
+#define CHROME_BROWSER_ASH_FILE_SYSTEM_PROVIDER_OPERATIONS_CREATE_DIRECTORY_H_
 
 #include <memory>
 
 #include "base/files/file.h"
 #include "base/macros.h"
-#include "chrome/browser/chromeos/file_system_provider/operations/operation.h"
+#include "chrome/browser/ash/file_system_provider/operations/operation.h"
 #include "chrome/browser/chromeos/file_system_provider/provided_file_system_info.h"
 #include "chrome/browser/chromeos/file_system_provider/request_value.h"
 #include "storage/browser/file_system/async_file_util.h"
@@ -59,4 +59,4 @@
 }  // namespace file_system_provider
 }  // namespace chromeos
 
-#endif  // CHROME_BROWSER_CHROMEOS_FILE_SYSTEM_PROVIDER_OPERATIONS_CREATE_DIRECTORY_H_
+#endif  // CHROME_BROWSER_ASH_FILE_SYSTEM_PROVIDER_OPERATIONS_CREATE_DIRECTORY_H_
diff --git a/chrome/browser/chromeos/file_system_provider/operations/create_directory_unittest.cc b/chrome/browser/ash/file_system_provider/operations/create_directory_unittest.cc
similarity index 97%
rename from chrome/browser/chromeos/file_system_provider/operations/create_directory_unittest.cc
rename to chrome/browser/ash/file_system_provider/operations/create_directory_unittest.cc
index 2d47e037..37fb0d38 100644
--- a/chrome/browser/chromeos/file_system_provider/operations/create_directory_unittest.cc
+++ b/chrome/browser/ash/file_system_provider/operations/create_directory_unittest.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/chromeos/file_system_provider/operations/create_directory.h"
+#include "chrome/browser/ash/file_system_provider/operations/create_directory.h"
 
 #include <stddef.h>
 
@@ -13,8 +13,8 @@
 #include "base/bind.h"
 #include "base/files/file.h"
 #include "base/files/file_path.h"
+#include "chrome/browser/ash/file_system_provider/operations/test_util.h"
 #include "chrome/browser/chromeos/file_system_provider/icon_set.h"
-#include "chrome/browser/chromeos/file_system_provider/operations/test_util.h"
 #include "chrome/browser/chromeos/file_system_provider/provided_file_system_interface.h"
 #include "chrome/common/extensions/api/file_system_provider.h"
 #include "chrome/common/extensions/api/file_system_provider_capabilities/file_system_provider_capabilities_handler.h"
diff --git a/chrome/browser/chromeos/file_system_provider/operations/create_file.cc b/chrome/browser/ash/file_system_provider/operations/create_file.cc
similarity index 95%
rename from chrome/browser/chromeos/file_system_provider/operations/create_file.cc
rename to chrome/browser/ash/file_system_provider/operations/create_file.cc
index d0cafdd..c125d9f 100644
--- a/chrome/browser/chromeos/file_system_provider/operations/create_file.cc
+++ b/chrome/browser/ash/file_system_provider/operations/create_file.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/chromeos/file_system_provider/operations/create_file.h"
+#include "chrome/browser/ash/file_system_provider/operations/create_file.h"
 
 #include <string>
 
diff --git a/chrome/browser/chromeos/file_system_provider/operations/create_file.h b/chrome/browser/ash/file_system_provider/operations/create_file.h
similarity index 84%
rename from chrome/browser/chromeos/file_system_provider/operations/create_file.h
rename to chrome/browser/ash/file_system_provider/operations/create_file.h
index f321c4f5..80530fa 100644
--- a/chrome/browser/chromeos/file_system_provider/operations/create_file.h
+++ b/chrome/browser/ash/file_system_provider/operations/create_file.h
@@ -2,14 +2,14 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CHROME_BROWSER_CHROMEOS_FILE_SYSTEM_PROVIDER_OPERATIONS_CREATE_FILE_H_
-#define CHROME_BROWSER_CHROMEOS_FILE_SYSTEM_PROVIDER_OPERATIONS_CREATE_FILE_H_
+#ifndef CHROME_BROWSER_ASH_FILE_SYSTEM_PROVIDER_OPERATIONS_CREATE_FILE_H_
+#define CHROME_BROWSER_ASH_FILE_SYSTEM_PROVIDER_OPERATIONS_CREATE_FILE_H_
 
 #include <memory>
 
 #include "base/files/file.h"
 #include "base/macros.h"
-#include "chrome/browser/chromeos/file_system_provider/operations/operation.h"
+#include "chrome/browser/ash/file_system_provider/operations/operation.h"
 #include "chrome/browser/chromeos/file_system_provider/provided_file_system_info.h"
 #include "chrome/browser/chromeos/file_system_provider/provided_file_system_interface.h"
 #include "chrome/browser/chromeos/file_system_provider/request_value.h"
@@ -57,4 +57,4 @@
 }  // namespace file_system_provider
 }  // namespace chromeos
 
-#endif  // CHROME_BROWSER_CHROMEOS_FILE_SYSTEM_PROVIDER_OPERATIONS_CREATE_FILE_H_
+#endif  // CHROME_BROWSER_ASH_FILE_SYSTEM_PROVIDER_OPERATIONS_CREATE_FILE_H_
diff --git a/chrome/browser/chromeos/file_system_provider/operations/create_file_unittest.cc b/chrome/browser/ash/file_system_provider/operations/create_file_unittest.cc
similarity index 97%
rename from chrome/browser/chromeos/file_system_provider/operations/create_file_unittest.cc
rename to chrome/browser/ash/file_system_provider/operations/create_file_unittest.cc
index 45712c7a..f6be164 100644
--- a/chrome/browser/chromeos/file_system_provider/operations/create_file_unittest.cc
+++ b/chrome/browser/ash/file_system_provider/operations/create_file_unittest.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/chromeos/file_system_provider/operations/create_file.h"
+#include "chrome/browser/ash/file_system_provider/operations/create_file.h"
 
 #include <memory>
 #include <string>
@@ -11,8 +11,8 @@
 #include "base/bind.h"
 #include "base/files/file.h"
 #include "base/files/file_path.h"
+#include "chrome/browser/ash/file_system_provider/operations/test_util.h"
 #include "chrome/browser/chromeos/file_system_provider/icon_set.h"
-#include "chrome/browser/chromeos/file_system_provider/operations/test_util.h"
 #include "chrome/browser/chromeos/file_system_provider/provided_file_system_interface.h"
 #include "chrome/common/extensions/api/file_system_provider.h"
 #include "chrome/common/extensions/api/file_system_provider_capabilities/file_system_provider_capabilities_handler.h"
diff --git a/chrome/browser/chromeos/file_system_provider/operations/delete_entry.cc b/chrome/browser/ash/file_system_provider/operations/delete_entry.cc
similarity index 96%
rename from chrome/browser/chromeos/file_system_provider/operations/delete_entry.cc
rename to chrome/browser/ash/file_system_provider/operations/delete_entry.cc
index b8e9cd2..c91c36c 100644
--- a/chrome/browser/chromeos/file_system_provider/operations/delete_entry.cc
+++ b/chrome/browser/ash/file_system_provider/operations/delete_entry.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/chromeos/file_system_provider/operations/delete_entry.h"
+#include "chrome/browser/ash/file_system_provider/operations/delete_entry.h"
 
 #include <string>
 
diff --git a/chrome/browser/chromeos/file_system_provider/operations/delete_entry.h b/chrome/browser/ash/file_system_provider/operations/delete_entry.h
similarity index 84%
rename from chrome/browser/chromeos/file_system_provider/operations/delete_entry.h
rename to chrome/browser/ash/file_system_provider/operations/delete_entry.h
index 9d585405..395b7b5 100644
--- a/chrome/browser/chromeos/file_system_provider/operations/delete_entry.h
+++ b/chrome/browser/ash/file_system_provider/operations/delete_entry.h
@@ -2,14 +2,14 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CHROME_BROWSER_CHROMEOS_FILE_SYSTEM_PROVIDER_OPERATIONS_DELETE_ENTRY_H_
-#define CHROME_BROWSER_CHROMEOS_FILE_SYSTEM_PROVIDER_OPERATIONS_DELETE_ENTRY_H_
+#ifndef CHROME_BROWSER_ASH_FILE_SYSTEM_PROVIDER_OPERATIONS_DELETE_ENTRY_H_
+#define CHROME_BROWSER_ASH_FILE_SYSTEM_PROVIDER_OPERATIONS_DELETE_ENTRY_H_
 
 #include <memory>
 
 #include "base/files/file.h"
 #include "base/macros.h"
-#include "chrome/browser/chromeos/file_system_provider/operations/operation.h"
+#include "chrome/browser/ash/file_system_provider/operations/operation.h"
 #include "chrome/browser/chromeos/file_system_provider/provided_file_system_info.h"
 #include "chrome/browser/chromeos/file_system_provider/request_value.h"
 #include "storage/browser/file_system/async_file_util.h"
@@ -58,4 +58,4 @@
 }  // namespace file_system_provider
 }  // namespace chromeos
 
-#endif  // CHROME_BROWSER_CHROMEOS_FILE_SYSTEM_PROVIDER_OPERATIONS_DELETE_ENTRY_H_
+#endif  // CHROME_BROWSER_ASH_FILE_SYSTEM_PROVIDER_OPERATIONS_DELETE_ENTRY_H_
diff --git a/chrome/browser/chromeos/file_system_provider/operations/delete_entry_unittest.cc b/chrome/browser/ash/file_system_provider/operations/delete_entry_unittest.cc
similarity index 97%
rename from chrome/browser/chromeos/file_system_provider/operations/delete_entry_unittest.cc
rename to chrome/browser/ash/file_system_provider/operations/delete_entry_unittest.cc
index 0bbba84..a629b41 100644
--- a/chrome/browser/chromeos/file_system_provider/operations/delete_entry_unittest.cc
+++ b/chrome/browser/ash/file_system_provider/operations/delete_entry_unittest.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/chromeos/file_system_provider/operations/delete_entry.h"
+#include "chrome/browser/ash/file_system_provider/operations/delete_entry.h"
 
 #include <memory>
 #include <string>
@@ -11,8 +11,8 @@
 #include "base/bind.h"
 #include "base/files/file.h"
 #include "base/files/file_path.h"
+#include "chrome/browser/ash/file_system_provider/operations/test_util.h"
 #include "chrome/browser/chromeos/file_system_provider/icon_set.h"
-#include "chrome/browser/chromeos/file_system_provider/operations/test_util.h"
 #include "chrome/browser/chromeos/file_system_provider/provided_file_system_interface.h"
 #include "chrome/common/extensions/api/file_system_provider.h"
 #include "chrome/common/extensions/api/file_system_provider_capabilities/file_system_provider_capabilities_handler.h"
diff --git a/chrome/browser/chromeos/file_system_provider/operations/execute_action.cc b/chrome/browser/ash/file_system_provider/operations/execute_action.cc
similarity index 96%
rename from chrome/browser/chromeos/file_system_provider/operations/execute_action.cc
rename to chrome/browser/ash/file_system_provider/operations/execute_action.cc
index 0b97332..3f0e39d6 100644
--- a/chrome/browser/chromeos/file_system_provider/operations/execute_action.cc
+++ b/chrome/browser/ash/file_system_provider/operations/execute_action.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/chromeos/file_system_provider/operations/execute_action.h"
+#include "chrome/browser/ash/file_system_provider/operations/execute_action.h"
 
 #include <algorithm>
 #include <string>
diff --git a/chrome/browser/chromeos/file_system_provider/operations/execute_action.h b/chrome/browser/ash/file_system_provider/operations/execute_action.h
similarity index 85%
rename from chrome/browser/chromeos/file_system_provider/operations/execute_action.h
rename to chrome/browser/ash/file_system_provider/operations/execute_action.h
index b06781bb..61c41c8 100644
--- a/chrome/browser/chromeos/file_system_provider/operations/execute_action.h
+++ b/chrome/browser/ash/file_system_provider/operations/execute_action.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CHROME_BROWSER_CHROMEOS_FILE_SYSTEM_PROVIDER_OPERATIONS_EXECUTE_ACTION_H_
-#define CHROME_BROWSER_CHROMEOS_FILE_SYSTEM_PROVIDER_OPERATIONS_EXECUTE_ACTION_H_
+#ifndef CHROME_BROWSER_ASH_FILE_SYSTEM_PROVIDER_OPERATIONS_EXECUTE_ACTION_H_
+#define CHROME_BROWSER_ASH_FILE_SYSTEM_PROVIDER_OPERATIONS_EXECUTE_ACTION_H_
 
 #include <memory>
 #include <string>
@@ -11,7 +11,7 @@
 
 #include "base/files/file.h"
 #include "base/macros.h"
-#include "chrome/browser/chromeos/file_system_provider/operations/operation.h"
+#include "chrome/browser/ash/file_system_provider/operations/operation.h"
 #include "chrome/browser/chromeos/file_system_provider/provided_file_system_info.h"
 #include "chrome/browser/chromeos/file_system_provider/provided_file_system_interface.h"
 #include "chrome/browser/chromeos/file_system_provider/request_value.h"
@@ -63,4 +63,4 @@
 }  // namespace file_system_provider
 }  // namespace chromeos
 
-#endif  // CHROME_BROWSER_CHROMEOS_FILE_SYSTEM_PROVIDER_OPERATIONS_EXECUTE_ACTION_H_
+#endif  // CHROME_BROWSER_ASH_FILE_SYSTEM_PROVIDER_OPERATIONS_EXECUTE_ACTION_H_
diff --git a/chrome/browser/chromeos/file_system_provider/operations/execute_action_unittest.cc b/chrome/browser/ash/file_system_provider/operations/execute_action_unittest.cc
similarity index 97%
rename from chrome/browser/chromeos/file_system_provider/operations/execute_action_unittest.cc
rename to chrome/browser/ash/file_system_provider/operations/execute_action_unittest.cc
index f8d08c8..8abacf1 100644
--- a/chrome/browser/chromeos/file_system_provider/operations/execute_action_unittest.cc
+++ b/chrome/browser/ash/file_system_provider/operations/execute_action_unittest.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/chromeos/file_system_provider/operations/execute_action.h"
+#include "chrome/browser/ash/file_system_provider/operations/execute_action.h"
 
 #include <memory>
 #include <string>
@@ -11,8 +11,8 @@
 #include "base/bind.h"
 #include "base/files/file.h"
 #include "base/files/file_path.h"
+#include "chrome/browser/ash/file_system_provider/operations/test_util.h"
 #include "chrome/browser/chromeos/file_system_provider/icon_set.h"
-#include "chrome/browser/chromeos/file_system_provider/operations/test_util.h"
 #include "chrome/browser/chromeos/file_system_provider/provided_file_system_interface.h"
 #include "chrome/common/extensions/api/file_system_provider.h"
 #include "chrome/common/extensions/api/file_system_provider_capabilities/file_system_provider_capabilities_handler.h"
diff --git a/chrome/browser/chromeos/file_system_provider/operations/get_actions.cc b/chrome/browser/ash/file_system_provider/operations/get_actions.cc
similarity index 96%
rename from chrome/browser/chromeos/file_system_provider/operations/get_actions.cc
rename to chrome/browser/ash/file_system_provider/operations/get_actions.cc
index a70866ce..b5ad0d5 100644
--- a/chrome/browser/chromeos/file_system_provider/operations/get_actions.cc
+++ b/chrome/browser/ash/file_system_provider/operations/get_actions.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/chromeos/file_system_provider/operations/get_actions.h"
+#include "chrome/browser/ash/file_system_provider/operations/get_actions.h"
 
 #include <algorithm>
 #include <string>
diff --git a/chrome/browser/chromeos/file_system_provider/operations/get_actions.h b/chrome/browser/ash/file_system_provider/operations/get_actions.h
similarity index 84%
rename from chrome/browser/chromeos/file_system_provider/operations/get_actions.h
rename to chrome/browser/ash/file_system_provider/operations/get_actions.h
index 76d921c5..224c98b 100644
--- a/chrome/browser/chromeos/file_system_provider/operations/get_actions.h
+++ b/chrome/browser/ash/file_system_provider/operations/get_actions.h
@@ -2,15 +2,15 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CHROME_BROWSER_CHROMEOS_FILE_SYSTEM_PROVIDER_OPERATIONS_GET_ACTIONS_H_
-#define CHROME_BROWSER_CHROMEOS_FILE_SYSTEM_PROVIDER_OPERATIONS_GET_ACTIONS_H_
+#ifndef CHROME_BROWSER_ASH_FILE_SYSTEM_PROVIDER_OPERATIONS_GET_ACTIONS_H_
+#define CHROME_BROWSER_ASH_FILE_SYSTEM_PROVIDER_OPERATIONS_GET_ACTIONS_H_
 
 #include <memory>
 #include <vector>
 
 #include "base/files/file.h"
 #include "base/macros.h"
-#include "chrome/browser/chromeos/file_system_provider/operations/operation.h"
+#include "chrome/browser/ash/file_system_provider/operations/operation.h"
 #include "chrome/browser/chromeos/file_system_provider/provided_file_system_info.h"
 #include "chrome/browser/chromeos/file_system_provider/provided_file_system_interface.h"
 #include "chrome/browser/chromeos/file_system_provider/request_value.h"
@@ -58,4 +58,4 @@
 }  // namespace file_system_provider
 }  // namespace chromeos
 
-#endif  // CHROME_BROWSER_CHROMEOS_FILE_SYSTEM_PROVIDER_OPERATIONS_GET_ACTIONS_H_
+#endif  // CHROME_BROWSER_ASH_FILE_SYSTEM_PROVIDER_OPERATIONS_GET_ACTIONS_H_
diff --git a/chrome/browser/chromeos/file_system_provider/operations/get_actions_unittest.cc b/chrome/browser/ash/file_system_provider/operations/get_actions_unittest.cc
similarity index 97%
rename from chrome/browser/chromeos/file_system_provider/operations/get_actions_unittest.cc
rename to chrome/browser/ash/file_system_provider/operations/get_actions_unittest.cc
index d4dd4e7..403e57c 100644
--- a/chrome/browser/chromeos/file_system_provider/operations/get_actions_unittest.cc
+++ b/chrome/browser/ash/file_system_provider/operations/get_actions_unittest.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/chromeos/file_system_provider/operations/get_actions.h"
+#include "chrome/browser/ash/file_system_provider/operations/get_actions.h"
 
 #include <memory>
 #include <string>
@@ -15,9 +15,9 @@
 #include "base/json/json_reader.h"
 #include "base/macros.h"
 #include "base/values.h"
+#include "chrome/browser/ash/file_system_provider/operations/get_metadata.h"
+#include "chrome/browser/ash/file_system_provider/operations/test_util.h"
 #include "chrome/browser/chromeos/file_system_provider/icon_set.h"
-#include "chrome/browser/chromeos/file_system_provider/operations/get_metadata.h"
-#include "chrome/browser/chromeos/file_system_provider/operations/test_util.h"
 #include "chrome/common/extensions/api/file_system_provider.h"
 #include "chrome/common/extensions/api/file_system_provider_capabilities/file_system_provider_capabilities_handler.h"
 #include "chrome/common/extensions/api/file_system_provider_internal.h"
diff --git a/chrome/browser/chromeos/file_system_provider/operations/get_metadata.cc b/chrome/browser/ash/file_system_provider/operations/get_metadata.cc
similarity index 98%
rename from chrome/browser/chromeos/file_system_provider/operations/get_metadata.cc
rename to chrome/browser/ash/file_system_provider/operations/get_metadata.cc
index 11756fc..44756acb 100644
--- a/chrome/browser/chromeos/file_system_provider/operations/get_metadata.cc
+++ b/chrome/browser/ash/file_system_provider/operations/get_metadata.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/chromeos/file_system_provider/operations/get_metadata.h"
+#include "chrome/browser/ash/file_system_provider/operations/get_metadata.h"
 
 #include <stdint.h>
 
diff --git a/chrome/browser/chromeos/file_system_provider/operations/get_metadata.h b/chrome/browser/ash/file_system_provider/operations/get_metadata.h
similarity index 87%
rename from chrome/browser/chromeos/file_system_provider/operations/get_metadata.h
rename to chrome/browser/ash/file_system_provider/operations/get_metadata.h
index eaf4f79..b04a966 100644
--- a/chrome/browser/chromeos/file_system_provider/operations/get_metadata.h
+++ b/chrome/browser/ash/file_system_provider/operations/get_metadata.h
@@ -2,15 +2,15 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CHROME_BROWSER_CHROMEOS_FILE_SYSTEM_PROVIDER_OPERATIONS_GET_METADATA_H_
-#define CHROME_BROWSER_CHROMEOS_FILE_SYSTEM_PROVIDER_OPERATIONS_GET_METADATA_H_
+#ifndef CHROME_BROWSER_ASH_FILE_SYSTEM_PROVIDER_OPERATIONS_GET_METADATA_H_
+#define CHROME_BROWSER_ASH_FILE_SYSTEM_PROVIDER_OPERATIONS_GET_METADATA_H_
 
 #include <memory>
 #include <string>
 
 #include "base/files/file.h"
 #include "base/macros.h"
-#include "chrome/browser/chromeos/file_system_provider/operations/operation.h"
+#include "chrome/browser/ash/file_system_provider/operations/operation.h"
 #include "chrome/browser/chromeos/file_system_provider/provided_file_system_info.h"
 #include "chrome/browser/chromeos/file_system_provider/provided_file_system_interface.h"
 #include "chrome/browser/chromeos/file_system_provider/request_value.h"
@@ -70,4 +70,4 @@
 }  // namespace file_system_provider
 }  // namespace chromeos
 
-#endif  // CHROME_BROWSER_CHROMEOS_FILE_SYSTEM_PROVIDER_OPERATIONS_GET_METADATA_H_
+#endif  // CHROME_BROWSER_ASH_FILE_SYSTEM_PROVIDER_OPERATIONS_GET_METADATA_H_
diff --git a/chrome/browser/chromeos/file_system_provider/operations/get_metadata_unittest.cc b/chrome/browser/ash/file_system_provider/operations/get_metadata_unittest.cc
similarity index 98%
rename from chrome/browser/chromeos/file_system_provider/operations/get_metadata_unittest.cc
rename to chrome/browser/ash/file_system_provider/operations/get_metadata_unittest.cc
index 4e5f47a..ffb8250 100644
--- a/chrome/browser/chromeos/file_system_provider/operations/get_metadata_unittest.cc
+++ b/chrome/browser/ash/file_system_provider/operations/get_metadata_unittest.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/chromeos/file_system_provider/operations/get_metadata.h"
+#include "chrome/browser/ash/file_system_provider/operations/get_metadata.h"
 
 #include <memory>
 #include <string>
@@ -15,8 +15,8 @@
 #include "base/json/json_reader.h"
 #include "base/macros.h"
 #include "base/values.h"
+#include "chrome/browser/ash/file_system_provider/operations/test_util.h"
 #include "chrome/browser/chromeos/file_system_provider/icon_set.h"
-#include "chrome/browser/chromeos/file_system_provider/operations/test_util.h"
 #include "chrome/common/extensions/api/file_system_provider.h"
 #include "chrome/common/extensions/api/file_system_provider_capabilities/file_system_provider_capabilities_handler.h"
 #include "chrome/common/extensions/api/file_system_provider_internal.h"
diff --git a/chrome/browser/chromeos/file_system_provider/operations/move_entry.cc b/chrome/browser/ash/file_system_provider/operations/move_entry.cc
similarity index 96%
rename from chrome/browser/chromeos/file_system_provider/operations/move_entry.cc
rename to chrome/browser/ash/file_system_provider/operations/move_entry.cc
index 26bc42d..f8a9579 100644
--- a/chrome/browser/chromeos/file_system_provider/operations/move_entry.cc
+++ b/chrome/browser/ash/file_system_provider/operations/move_entry.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/chromeos/file_system_provider/operations/move_entry.h"
+#include "chrome/browser/ash/file_system_provider/operations/move_entry.h"
 
 #include <string>
 
diff --git a/chrome/browser/chromeos/file_system_provider/operations/move_entry.h b/chrome/browser/ash/file_system_provider/operations/move_entry.h
similarity index 84%
rename from chrome/browser/chromeos/file_system_provider/operations/move_entry.h
rename to chrome/browser/ash/file_system_provider/operations/move_entry.h
index 65c9ebd5..34c8036c 100644
--- a/chrome/browser/chromeos/file_system_provider/operations/move_entry.h
+++ b/chrome/browser/ash/file_system_provider/operations/move_entry.h
@@ -2,14 +2,14 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CHROME_BROWSER_CHROMEOS_FILE_SYSTEM_PROVIDER_OPERATIONS_MOVE_ENTRY_H_
-#define CHROME_BROWSER_CHROMEOS_FILE_SYSTEM_PROVIDER_OPERATIONS_MOVE_ENTRY_H_
+#ifndef CHROME_BROWSER_ASH_FILE_SYSTEM_PROVIDER_OPERATIONS_MOVE_ENTRY_H_
+#define CHROME_BROWSER_ASH_FILE_SYSTEM_PROVIDER_OPERATIONS_MOVE_ENTRY_H_
 
 #include <memory>
 
 #include "base/files/file.h"
 #include "base/macros.h"
-#include "chrome/browser/chromeos/file_system_provider/operations/operation.h"
+#include "chrome/browser/ash/file_system_provider/operations/operation.h"
 #include "chrome/browser/chromeos/file_system_provider/provided_file_system_info.h"
 #include "chrome/browser/chromeos/file_system_provider/provided_file_system_interface.h"
 #include "chrome/browser/chromeos/file_system_provider/request_value.h"
@@ -58,4 +58,4 @@
 }  // namespace file_system_provider
 }  // namespace chromeos
 
-#endif  // CHROME_BROWSER_CHROMEOS_FILE_SYSTEM_PROVIDER_OPERATIONS_MOVE_ENTRY_H_
+#endif  // CHROME_BROWSER_ASH_FILE_SYSTEM_PROVIDER_OPERATIONS_MOVE_ENTRY_H_
diff --git a/chrome/browser/chromeos/file_system_provider/operations/move_entry_unittest.cc b/chrome/browser/ash/file_system_provider/operations/move_entry_unittest.cc
similarity index 97%
rename from chrome/browser/chromeos/file_system_provider/operations/move_entry_unittest.cc
rename to chrome/browser/ash/file_system_provider/operations/move_entry_unittest.cc
index 89e979c..082f1c1 100644
--- a/chrome/browser/chromeos/file_system_provider/operations/move_entry_unittest.cc
+++ b/chrome/browser/ash/file_system_provider/operations/move_entry_unittest.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/chromeos/file_system_provider/operations/move_entry.h"
+#include "chrome/browser/ash/file_system_provider/operations/move_entry.h"
 
 #include <memory>
 #include <string>
@@ -11,8 +11,8 @@
 #include "base/bind.h"
 #include "base/files/file.h"
 #include "base/files/file_path.h"
+#include "chrome/browser/ash/file_system_provider/operations/test_util.h"
 #include "chrome/browser/chromeos/file_system_provider/icon_set.h"
-#include "chrome/browser/chromeos/file_system_provider/operations/test_util.h"
 #include "chrome/browser/chromeos/file_system_provider/provided_file_system_interface.h"
 #include "chrome/common/extensions/api/file_system_provider.h"
 #include "chrome/common/extensions/api/file_system_provider_capabilities/file_system_provider_capabilities_handler.h"
diff --git a/chrome/browser/chromeos/file_system_provider/operations/open_file.cc b/chrome/browser/ash/file_system_provider/operations/open_file.cc
similarity index 96%
rename from chrome/browser/chromeos/file_system_provider/operations/open_file.cc
rename to chrome/browser/ash/file_system_provider/operations/open_file.cc
index f0446e2e..d39703f 100644
--- a/chrome/browser/chromeos/file_system_provider/operations/open_file.cc
+++ b/chrome/browser/ash/file_system_provider/operations/open_file.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/chromeos/file_system_provider/operations/open_file.h"
+#include "chrome/browser/ash/file_system_provider/operations/open_file.h"
 
 #include <string>
 
diff --git a/chrome/browser/chromeos/file_system_provider/operations/open_file.h b/chrome/browser/ash/file_system_provider/operations/open_file.h
similarity index 84%
rename from chrome/browser/chromeos/file_system_provider/operations/open_file.h
rename to chrome/browser/ash/file_system_provider/operations/open_file.h
index bde0e32..89722451 100644
--- a/chrome/browser/chromeos/file_system_provider/operations/open_file.h
+++ b/chrome/browser/ash/file_system_provider/operations/open_file.h
@@ -2,14 +2,14 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CHROME_BROWSER_CHROMEOS_FILE_SYSTEM_PROVIDER_OPERATIONS_OPEN_FILE_H_
-#define CHROME_BROWSER_CHROMEOS_FILE_SYSTEM_PROVIDER_OPERATIONS_OPEN_FILE_H_
+#ifndef CHROME_BROWSER_ASH_FILE_SYSTEM_PROVIDER_OPERATIONS_OPEN_FILE_H_
+#define CHROME_BROWSER_ASH_FILE_SYSTEM_PROVIDER_OPERATIONS_OPEN_FILE_H_
 
 #include <memory>
 
 #include "base/files/file.h"
 #include "base/macros.h"
-#include "chrome/browser/chromeos/file_system_provider/operations/operation.h"
+#include "chrome/browser/ash/file_system_provider/operations/operation.h"
 #include "chrome/browser/chromeos/file_system_provider/provided_file_system_info.h"
 #include "chrome/browser/chromeos/file_system_provider/provided_file_system_interface.h"
 #include "chrome/browser/chromeos/file_system_provider/request_value.h"
@@ -59,4 +59,4 @@
 }  // namespace file_system_provider
 }  // namespace chromeos
 
-#endif  // CHROME_BROWSER_CHROMEOS_FILE_SYSTEM_PROVIDER_OPERATIONS_OPEN_FILE_H_
+#endif  // CHROME_BROWSER_ASH_FILE_SYSTEM_PROVIDER_OPERATIONS_OPEN_FILE_H_
diff --git a/chrome/browser/chromeos/file_system_provider/operations/open_file_unittest.cc b/chrome/browser/ash/file_system_provider/operations/open_file_unittest.cc
similarity index 97%
rename from chrome/browser/chromeos/file_system_provider/operations/open_file_unittest.cc
rename to chrome/browser/ash/file_system_provider/operations/open_file_unittest.cc
index a411ff0..5a60e4dd 100644
--- a/chrome/browser/chromeos/file_system_provider/operations/open_file_unittest.cc
+++ b/chrome/browser/ash/file_system_provider/operations/open_file_unittest.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/chromeos/file_system_provider/operations/open_file.h"
+#include "chrome/browser/ash/file_system_provider/operations/open_file.h"
 
 #include <memory>
 #include <string>
@@ -12,8 +12,8 @@
 #include "base/files/file.h"
 #include "base/files/file_path.h"
 #include "base/macros.h"
+#include "chrome/browser/ash/file_system_provider/operations/test_util.h"
 #include "chrome/browser/chromeos/file_system_provider/icon_set.h"
-#include "chrome/browser/chromeos/file_system_provider/operations/test_util.h"
 #include "chrome/browser/chromeos/file_system_provider/provided_file_system_interface.h"
 #include "chrome/common/extensions/api/file_system_provider.h"
 #include "chrome/common/extensions/api/file_system_provider_capabilities/file_system_provider_capabilities_handler.h"
diff --git a/chrome/browser/chromeos/file_system_provider/operations/operation.cc b/chrome/browser/ash/file_system_provider/operations/operation.cc
similarity index 95%
rename from chrome/browser/chromeos/file_system_provider/operations/operation.cc
rename to chrome/browser/ash/file_system_provider/operations/operation.cc
index dc79f8d..aa567a8 100644
--- a/chrome/browser/chromeos/file_system_provider/operations/operation.cc
+++ b/chrome/browser/ash/file_system_provider/operations/operation.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/chromeos/file_system_provider/operations/operation.h"
+#include "chrome/browser/ash/file_system_provider/operations/operation.h"
 
 #include <utility>
 
diff --git a/chrome/browser/chromeos/file_system_provider/operations/operation.h b/chrome/browser/ash/file_system_provider/operations/operation.h
similarity index 90%
rename from chrome/browser/chromeos/file_system_provider/operations/operation.h
rename to chrome/browser/ash/file_system_provider/operations/operation.h
index 7f4f3d1..d56c8ca 100644
--- a/chrome/browser/chromeos/file_system_provider/operations/operation.h
+++ b/chrome/browser/ash/file_system_provider/operations/operation.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CHROME_BROWSER_CHROMEOS_FILE_SYSTEM_PROVIDER_OPERATIONS_OPERATION_H_
-#define CHROME_BROWSER_CHROMEOS_FILE_SYSTEM_PROVIDER_OPERATIONS_OPERATION_H_
+#ifndef CHROME_BROWSER_ASH_FILE_SYSTEM_PROVIDER_OPERATIONS_OPERATION_H_
+#define CHROME_BROWSER_ASH_FILE_SYSTEM_PROVIDER_OPERATIONS_OPERATION_H_
 
 #include <memory>
 #include <string>
@@ -70,4 +70,4 @@
 }  // namespace file_system_provider
 }  // namespace chromeos
 
-#endif  // CHROME_BROWSER_CHROMEOS_FILE_SYSTEM_PROVIDER_OPERATIONS_OPERATION_H_
+#endif  // CHROME_BROWSER_ASH_FILE_SYSTEM_PROVIDER_OPERATIONS_OPERATION_H_
diff --git a/chrome/browser/chromeos/file_system_provider/operations/read_directory.cc b/chrome/browser/ash/file_system_provider/operations/read_directory.cc
similarity index 95%
rename from chrome/browser/chromeos/file_system_provider/operations/read_directory.cc
rename to chrome/browser/ash/file_system_provider/operations/read_directory.cc
index ab43d1a..c5915a3 100644
--- a/chrome/browser/chromeos/file_system_provider/operations/read_directory.cc
+++ b/chrome/browser/ash/file_system_provider/operations/read_directory.cc
@@ -2,14 +2,14 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/chromeos/file_system_provider/operations/read_directory.h"
+#include "chrome/browser/ash/file_system_provider/operations/read_directory.h"
 
 #include <stddef.h>
 
 #include <string>
 #include <utility>
 
-#include "chrome/browser/chromeos/file_system_provider/operations/get_metadata.h"
+#include "chrome/browser/ash/file_system_provider/operations/get_metadata.h"
 #include "chrome/common/extensions/api/file_system_provider.h"
 #include "chrome/common/extensions/api/file_system_provider_internal.h"
 #include "components/services/filesystem/public/mojom/types.mojom.h"
diff --git a/chrome/browser/chromeos/file_system_provider/operations/read_directory.h b/chrome/browser/ash/file_system_provider/operations/read_directory.h
similarity index 83%
rename from chrome/browser/chromeos/file_system_provider/operations/read_directory.h
rename to chrome/browser/ash/file_system_provider/operations/read_directory.h
index 8d06599..ab5bdb4 100644
--- a/chrome/browser/chromeos/file_system_provider/operations/read_directory.h
+++ b/chrome/browser/ash/file_system_provider/operations/read_directory.h
@@ -2,14 +2,14 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CHROME_BROWSER_CHROMEOS_FILE_SYSTEM_PROVIDER_OPERATIONS_READ_DIRECTORY_H_
-#define CHROME_BROWSER_CHROMEOS_FILE_SYSTEM_PROVIDER_OPERATIONS_READ_DIRECTORY_H_
+#ifndef CHROME_BROWSER_ASH_FILE_SYSTEM_PROVIDER_OPERATIONS_READ_DIRECTORY_H_
+#define CHROME_BROWSER_ASH_FILE_SYSTEM_PROVIDER_OPERATIONS_READ_DIRECTORY_H_
 
 #include <memory>
 
 #include "base/files/file.h"
 #include "base/macros.h"
-#include "chrome/browser/chromeos/file_system_provider/operations/operation.h"
+#include "chrome/browser/ash/file_system_provider/operations/operation.h"
 #include "chrome/browser/chromeos/file_system_provider/provided_file_system_info.h"
 #include "chrome/browser/chromeos/file_system_provider/request_value.h"
 #include "storage/browser/file_system/async_file_util.h"
@@ -56,4 +56,4 @@
 }  // namespace file_system_provider
 }  // namespace chromeos
 
-#endif  // CHROME_BROWSER_CHROMEOS_FILE_SYSTEM_PROVIDER_OPERATIONS_READ_DIRECTORY_H_
+#endif  // CHROME_BROWSER_ASH_FILE_SYSTEM_PROVIDER_OPERATIONS_READ_DIRECTORY_H_
diff --git a/chrome/browser/chromeos/file_system_provider/operations/read_directory_unittest.cc b/chrome/browser/ash/file_system_provider/operations/read_directory_unittest.cc
similarity index 97%
rename from chrome/browser/chromeos/file_system_provider/operations/read_directory_unittest.cc
rename to chrome/browser/ash/file_system_provider/operations/read_directory_unittest.cc
index db6d256..89e7186b 100644
--- a/chrome/browser/chromeos/file_system_provider/operations/read_directory_unittest.cc
+++ b/chrome/browser/ash/file_system_provider/operations/read_directory_unittest.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/chromeos/file_system_provider/operations/read_directory.h"
+#include "chrome/browser/ash/file_system_provider/operations/read_directory.h"
 
 #include <memory>
 #include <string>
@@ -15,9 +15,9 @@
 #include "base/json/json_reader.h"
 #include "base/macros.h"
 #include "base/values.h"
+#include "chrome/browser/ash/file_system_provider/operations/get_metadata.h"
+#include "chrome/browser/ash/file_system_provider/operations/test_util.h"
 #include "chrome/browser/chromeos/file_system_provider/icon_set.h"
-#include "chrome/browser/chromeos/file_system_provider/operations/get_metadata.h"
-#include "chrome/browser/chromeos/file_system_provider/operations/test_util.h"
 #include "chrome/common/extensions/api/file_system_provider.h"
 #include "chrome/common/extensions/api/file_system_provider_capabilities/file_system_provider_capabilities_handler.h"
 #include "chrome/common/extensions/api/file_system_provider_internal.h"
diff --git a/chrome/browser/chromeos/file_system_provider/operations/read_file.cc b/chrome/browser/ash/file_system_provider/operations/read_file.cc
similarity index 97%
rename from chrome/browser/chromeos/file_system_provider/operations/read_file.cc
rename to chrome/browser/ash/file_system_provider/operations/read_file.cc
index 568a37c..ad8b355 100644
--- a/chrome/browser/chromeos/file_system_provider/operations/read_file.cc
+++ b/chrome/browser/ash/file_system_provider/operations/read_file.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/chromeos/file_system_provider/operations/read_file.h"
+#include "chrome/browser/ash/file_system_provider/operations/read_file.h"
 
 #include <stddef.h>
 
diff --git a/chrome/browser/chromeos/file_system_provider/operations/read_file.h b/chrome/browser/ash/file_system_provider/operations/read_file.h
similarity index 85%
rename from chrome/browser/chromeos/file_system_provider/operations/read_file.h
rename to chrome/browser/ash/file_system_provider/operations/read_file.h
index baec64f..b6e5b41 100644
--- a/chrome/browser/chromeos/file_system_provider/operations/read_file.h
+++ b/chrome/browser/ash/file_system_provider/operations/read_file.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CHROME_BROWSER_CHROMEOS_FILE_SYSTEM_PROVIDER_OPERATIONS_READ_FILE_H_
-#define CHROME_BROWSER_CHROMEOS_FILE_SYSTEM_PROVIDER_OPERATIONS_READ_FILE_H_
+#ifndef CHROME_BROWSER_ASH_FILE_SYSTEM_PROVIDER_OPERATIONS_READ_FILE_H_
+#define CHROME_BROWSER_ASH_FILE_SYSTEM_PROVIDER_OPERATIONS_READ_FILE_H_
 
 #include <stdint.h>
 
@@ -12,7 +12,7 @@
 #include "base/files/file.h"
 #include "base/macros.h"
 #include "base/memory/ref_counted.h"
-#include "chrome/browser/chromeos/file_system_provider/operations/operation.h"
+#include "chrome/browser/ash/file_system_provider/operations/operation.h"
 #include "chrome/browser/chromeos/file_system_provider/provided_file_system_info.h"
 #include "chrome/browser/chromeos/file_system_provider/provided_file_system_interface.h"
 #include "chrome/browser/chromeos/file_system_provider/request_value.h"
@@ -64,4 +64,4 @@
 }  // namespace file_system_provider
 }  // namespace chromeos
 
-#endif  // CHROME_BROWSER_CHROMEOS_FILE_SYSTEM_PROVIDER_OPERATIONS_READ_FILE_H_
+#endif  // CHROME_BROWSER_ASH_FILE_SYSTEM_PROVIDER_OPERATIONS_READ_FILE_H_
diff --git a/chrome/browser/chromeos/file_system_provider/operations/read_file_unittest.cc b/chrome/browser/ash/file_system_provider/operations/read_file_unittest.cc
similarity index 97%
rename from chrome/browser/chromeos/file_system_provider/operations/read_file_unittest.cc
rename to chrome/browser/ash/file_system_provider/operations/read_file_unittest.cc
index cecf1ce..1d5ecc8 100644
--- a/chrome/browser/chromeos/file_system_provider/operations/read_file_unittest.cc
+++ b/chrome/browser/ash/file_system_provider/operations/read_file_unittest.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/chromeos/file_system_provider/operations/read_file.h"
+#include "chrome/browser/ash/file_system_provider/operations/read_file.h"
 
 #include <memory>
 #include <string>
@@ -15,8 +15,8 @@
 #include "base/macros.h"
 #include "base/memory/ref_counted.h"
 #include "base/values.h"
+#include "chrome/browser/ash/file_system_provider/operations/test_util.h"
 #include "chrome/browser/chromeos/file_system_provider/icon_set.h"
-#include "chrome/browser/chromeos/file_system_provider/operations/test_util.h"
 #include "chrome/common/extensions/api/file_system_provider.h"
 #include "chrome/common/extensions/api/file_system_provider_capabilities/file_system_provider_capabilities_handler.h"
 #include "chrome/common/extensions/api/file_system_provider_internal.h"
diff --git a/chrome/browser/chromeos/file_system_provider/operations/remove_watcher.cc b/chrome/browser/ash/file_system_provider/operations/remove_watcher.cc
similarity index 95%
rename from chrome/browser/chromeos/file_system_provider/operations/remove_watcher.cc
rename to chrome/browser/ash/file_system_provider/operations/remove_watcher.cc
index 511cc894..6bee320 100644
--- a/chrome/browser/chromeos/file_system_provider/operations/remove_watcher.cc
+++ b/chrome/browser/ash/file_system_provider/operations/remove_watcher.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/chromeos/file_system_provider/operations/remove_watcher.h"
+#include "chrome/browser/ash/file_system_provider/operations/remove_watcher.h"
 
 #include <string>
 
diff --git a/chrome/browser/chromeos/file_system_provider/operations/remove_watcher.h b/chrome/browser/ash/file_system_provider/operations/remove_watcher.h
similarity index 84%
rename from chrome/browser/chromeos/file_system_provider/operations/remove_watcher.h
rename to chrome/browser/ash/file_system_provider/operations/remove_watcher.h
index 8ec6255..f0d399f 100644
--- a/chrome/browser/chromeos/file_system_provider/operations/remove_watcher.h
+++ b/chrome/browser/ash/file_system_provider/operations/remove_watcher.h
@@ -2,14 +2,14 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CHROME_BROWSER_CHROMEOS_FILE_SYSTEM_PROVIDER_OPERATIONS_REMOVE_WATCHER_H_
-#define CHROME_BROWSER_CHROMEOS_FILE_SYSTEM_PROVIDER_OPERATIONS_REMOVE_WATCHER_H_
+#ifndef CHROME_BROWSER_ASH_FILE_SYSTEM_PROVIDER_OPERATIONS_REMOVE_WATCHER_H_
+#define CHROME_BROWSER_ASH_FILE_SYSTEM_PROVIDER_OPERATIONS_REMOVE_WATCHER_H_
 
 #include <memory>
 
 #include "base/files/file.h"
 #include "base/macros.h"
-#include "chrome/browser/chromeos/file_system_provider/operations/operation.h"
+#include "chrome/browser/ash/file_system_provider/operations/operation.h"
 #include "chrome/browser/chromeos/file_system_provider/provided_file_system_info.h"
 #include "chrome/browser/chromeos/file_system_provider/provided_file_system_interface.h"
 #include "chrome/browser/chromeos/file_system_provider/request_value.h"
@@ -58,4 +58,4 @@
 }  // namespace file_system_provider
 }  // namespace chromeos
 
-#endif  // CHROME_BROWSER_CHROMEOS_FILE_SYSTEM_PROVIDER_OPERATIONS_REMOVE_WATCHER_H_
+#endif  // CHROME_BROWSER_ASH_FILE_SYSTEM_PROVIDER_OPERATIONS_REMOVE_WATCHER_H_
diff --git a/chrome/browser/chromeos/file_system_provider/operations/remove_watcher_unittest.cc b/chrome/browser/ash/file_system_provider/operations/remove_watcher_unittest.cc
similarity index 96%
rename from chrome/browser/chromeos/file_system_provider/operations/remove_watcher_unittest.cc
rename to chrome/browser/ash/file_system_provider/operations/remove_watcher_unittest.cc
index aa263d5..3c37fc4 100644
--- a/chrome/browser/chromeos/file_system_provider/operations/remove_watcher_unittest.cc
+++ b/chrome/browser/ash/file_system_provider/operations/remove_watcher_unittest.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/chromeos/file_system_provider/operations/remove_watcher.h"
+#include "chrome/browser/ash/file_system_provider/operations/remove_watcher.h"
 
 #include <memory>
 #include <string>
@@ -11,8 +11,8 @@
 #include "base/bind.h"
 #include "base/files/file.h"
 #include "base/files/file_path.h"
+#include "chrome/browser/ash/file_system_provider/operations/test_util.h"
 #include "chrome/browser/chromeos/file_system_provider/icon_set.h"
-#include "chrome/browser/chromeos/file_system_provider/operations/test_util.h"
 #include "chrome/browser/chromeos/file_system_provider/provided_file_system_interface.h"
 #include "chrome/common/extensions/api/file_system_provider.h"
 #include "chrome/common/extensions/api/file_system_provider_capabilities/file_system_provider_capabilities_handler.h"
diff --git a/chrome/browser/chromeos/file_system_provider/operations/test_util.cc b/chrome/browser/ash/file_system_provider/operations/test_util.cc
similarity index 91%
rename from chrome/browser/chromeos/file_system_provider/operations/test_util.cc
rename to chrome/browser/ash/file_system_provider/operations/test_util.cc
index a5dd70a..b753c8c 100644
--- a/chrome/browser/chromeos/file_system_provider/operations/test_util.cc
+++ b/chrome/browser/ash/file_system_provider/operations/test_util.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/chromeos/file_system_provider/operations/test_util.h"
+#include "chrome/browser/ash/file_system_provider/operations/test_util.h"
 
 #include <utility>
 
diff --git a/chrome/browser/chromeos/file_system_provider/operations/test_util.h b/chrome/browser/ash/file_system_provider/operations/test_util.h
similarity index 86%
rename from chrome/browser/chromeos/file_system_provider/operations/test_util.h
rename to chrome/browser/ash/file_system_provider/operations/test_util.h
index cbae6470..fcc2e52 100644
--- a/chrome/browser/chromeos/file_system_provider/operations/test_util.h
+++ b/chrome/browser/ash/file_system_provider/operations/test_util.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CHROME_BROWSER_CHROMEOS_FILE_SYSTEM_PROVIDER_OPERATIONS_TEST_UTIL_H_
-#define CHROME_BROWSER_CHROMEOS_FILE_SYSTEM_PROVIDER_OPERATIONS_TEST_UTIL_H_
+#ifndef CHROME_BROWSER_ASH_FILE_SYSTEM_PROVIDER_OPERATIONS_TEST_UTIL_H_
+#define CHROME_BROWSER_ASH_FILE_SYSTEM_PROVIDER_OPERATIONS_TEST_UTIL_H_
 
 #include <memory>
 #include <vector>
@@ -51,4 +51,4 @@
 }  // namespace file_system_provider
 }  // namespace chromeos
 
-#endif  // CHROME_BROWSER_CHROMEOS_FILE_SYSTEM_PROVIDER_OPERATIONS_TEST_UTIL_H_
+#endif  // CHROME_BROWSER_ASH_FILE_SYSTEM_PROVIDER_OPERATIONS_TEST_UTIL_H_
diff --git a/chrome/browser/chromeos/file_system_provider/operations/truncate.cc b/chrome/browser/ash/file_system_provider/operations/truncate.cc
similarity index 96%
rename from chrome/browser/chromeos/file_system_provider/operations/truncate.cc
rename to chrome/browser/ash/file_system_provider/operations/truncate.cc
index 866aa7a9..eb9ea34 100644
--- a/chrome/browser/chromeos/file_system_provider/operations/truncate.cc
+++ b/chrome/browser/ash/file_system_provider/operations/truncate.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/chromeos/file_system_provider/operations/truncate.h"
+#include "chrome/browser/ash/file_system_provider/operations/truncate.h"
 
 #include <string>
 
diff --git a/chrome/browser/chromeos/file_system_provider/operations/truncate.h b/chrome/browser/ash/file_system_provider/operations/truncate.h
similarity index 85%
rename from chrome/browser/chromeos/file_system_provider/operations/truncate.h
rename to chrome/browser/ash/file_system_provider/operations/truncate.h
index f13aef9..9b33903 100644
--- a/chrome/browser/chromeos/file_system_provider/operations/truncate.h
+++ b/chrome/browser/ash/file_system_provider/operations/truncate.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CHROME_BROWSER_CHROMEOS_FILE_SYSTEM_PROVIDER_OPERATIONS_TRUNCATE_H_
-#define CHROME_BROWSER_CHROMEOS_FILE_SYSTEM_PROVIDER_OPERATIONS_TRUNCATE_H_
+#ifndef CHROME_BROWSER_ASH_FILE_SYSTEM_PROVIDER_OPERATIONS_TRUNCATE_H_
+#define CHROME_BROWSER_ASH_FILE_SYSTEM_PROVIDER_OPERATIONS_TRUNCATE_H_
 
 #include <stdint.h>
 
@@ -11,7 +11,7 @@
 
 #include "base/files/file.h"
 #include "base/macros.h"
-#include "chrome/browser/chromeos/file_system_provider/operations/operation.h"
+#include "chrome/browser/ash/file_system_provider/operations/operation.h"
 #include "chrome/browser/chromeos/file_system_provider/provided_file_system_info.h"
 #include "chrome/browser/chromeos/file_system_provider/provided_file_system_interface.h"
 #include "chrome/browser/chromeos/file_system_provider/request_value.h"
@@ -61,4 +61,4 @@
 }  // namespace file_system_provider
 }  // namespace chromeos
 
-#endif  // CHROME_BROWSER_CHROMEOS_FILE_SYSTEM_PROVIDER_OPERATIONS_TRUNCATE_H_
+#endif  // CHROME_BROWSER_ASH_FILE_SYSTEM_PROVIDER_OPERATIONS_TRUNCATE_H_
diff --git a/chrome/browser/chromeos/file_system_provider/operations/truncate_unittest.cc b/chrome/browser/ash/file_system_provider/operations/truncate_unittest.cc
similarity index 97%
rename from chrome/browser/chromeos/file_system_provider/operations/truncate_unittest.cc
rename to chrome/browser/ash/file_system_provider/operations/truncate_unittest.cc
index d805fc3..639ee35 100644
--- a/chrome/browser/chromeos/file_system_provider/operations/truncate_unittest.cc
+++ b/chrome/browser/ash/file_system_provider/operations/truncate_unittest.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/chromeos/file_system_provider/operations/truncate.h"
+#include "chrome/browser/ash/file_system_provider/operations/truncate.h"
 
 #include <stdint.h>
 
@@ -13,8 +13,8 @@
 #include "base/bind.h"
 #include "base/files/file.h"
 #include "base/files/file_path.h"
+#include "chrome/browser/ash/file_system_provider/operations/test_util.h"
 #include "chrome/browser/chromeos/file_system_provider/icon_set.h"
-#include "chrome/browser/chromeos/file_system_provider/operations/test_util.h"
 #include "chrome/browser/chromeos/file_system_provider/provided_file_system_interface.h"
 #include "chrome/common/extensions/api/file_system_provider.h"
 #include "chrome/common/extensions/api/file_system_provider_capabilities/file_system_provider_capabilities_handler.h"
diff --git a/chrome/browser/chromeos/file_system_provider/operations/unmount.cc b/chrome/browser/ash/file_system_provider/operations/unmount.cc
similarity index 95%
rename from chrome/browser/chromeos/file_system_provider/operations/unmount.cc
rename to chrome/browser/ash/file_system_provider/operations/unmount.cc
index 9f0ef00..7a47258 100644
--- a/chrome/browser/chromeos/file_system_provider/operations/unmount.cc
+++ b/chrome/browser/ash/file_system_provider/operations/unmount.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/chromeos/file_system_provider/operations/unmount.h"
+#include "chrome/browser/ash/file_system_provider/operations/unmount.h"
 
 #include "base/values.h"
 #include "chrome/common/extensions/api/file_system_provider.h"
diff --git a/chrome/browser/chromeos/file_system_provider/operations/unmount.h b/chrome/browser/ash/file_system_provider/operations/unmount.h
similarity index 81%
rename from chrome/browser/chromeos/file_system_provider/operations/unmount.h
rename to chrome/browser/ash/file_system_provider/operations/unmount.h
index 347a502..09443ba 100644
--- a/chrome/browser/chromeos/file_system_provider/operations/unmount.h
+++ b/chrome/browser/ash/file_system_provider/operations/unmount.h
@@ -2,14 +2,14 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CHROME_BROWSER_CHROMEOS_FILE_SYSTEM_PROVIDER_OPERATIONS_UNMOUNT_H_
-#define CHROME_BROWSER_CHROMEOS_FILE_SYSTEM_PROVIDER_OPERATIONS_UNMOUNT_H_
+#ifndef CHROME_BROWSER_ASH_FILE_SYSTEM_PROVIDER_OPERATIONS_UNMOUNT_H_
+#define CHROME_BROWSER_ASH_FILE_SYSTEM_PROVIDER_OPERATIONS_UNMOUNT_H_
 
 #include <memory>
 
 #include "base/files/file.h"
 #include "base/macros.h"
-#include "chrome/browser/chromeos/file_system_provider/operations/operation.h"
+#include "chrome/browser/ash/file_system_provider/operations/operation.h"
 #include "storage/browser/file_system/async_file_util.h"
 
 namespace extensions {
@@ -51,4 +51,4 @@
 }  // namespace file_system_provider
 }  // namespace chromeos
 
-#endif  // CHROME_BROWSER_CHROMEOS_FILE_SYSTEM_PROVIDER_OPERATIONS_UNMOUNT_H_
+#endif  // CHROME_BROWSER_ASH_FILE_SYSTEM_PROVIDER_OPERATIONS_UNMOUNT_H_
diff --git a/chrome/browser/chromeos/file_system_provider/operations/unmount_unittest.cc b/chrome/browser/ash/file_system_provider/operations/unmount_unittest.cc
similarity index 96%
rename from chrome/browser/chromeos/file_system_provider/operations/unmount_unittest.cc
rename to chrome/browser/ash/file_system_provider/operations/unmount_unittest.cc
index e84cf2f..896f36f 100644
--- a/chrome/browser/chromeos/file_system_provider/operations/unmount_unittest.cc
+++ b/chrome/browser/ash/file_system_provider/operations/unmount_unittest.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/chromeos/file_system_provider/operations/unmount.h"
+#include "chrome/browser/ash/file_system_provider/operations/unmount.h"
 
 #include <memory>
 #include <string>
@@ -11,8 +11,8 @@
 #include "base/bind.h"
 #include "base/files/file.h"
 #include "base/files/file_path.h"
+#include "chrome/browser/ash/file_system_provider/operations/test_util.h"
 #include "chrome/browser/chromeos/file_system_provider/icon_set.h"
-#include "chrome/browser/chromeos/file_system_provider/operations/test_util.h"
 #include "chrome/browser/chromeos/file_system_provider/provided_file_system_interface.h"
 #include "chrome/common/extensions/api/file_system_provider.h"
 #include "chrome/common/extensions/api/file_system_provider_capabilities/file_system_provider_capabilities_handler.h"
diff --git a/chrome/browser/chromeos/file_system_provider/operations/write_file.cc b/chrome/browser/ash/file_system_provider/operations/write_file.cc
similarity index 97%
rename from chrome/browser/chromeos/file_system_provider/operations/write_file.cc
rename to chrome/browser/ash/file_system_provider/operations/write_file.cc
index 5ead5f4..6a0401ac 100644
--- a/chrome/browser/chromeos/file_system_provider/operations/write_file.cc
+++ b/chrome/browser/ash/file_system_provider/operations/write_file.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/chromeos/file_system_provider/operations/write_file.h"
+#include "chrome/browser/ash/file_system_provider/operations/write_file.h"
 
 #include <utility>
 
diff --git a/chrome/browser/chromeos/file_system_provider/operations/write_file.h b/chrome/browser/ash/file_system_provider/operations/write_file.h
similarity index 85%
rename from chrome/browser/chromeos/file_system_provider/operations/write_file.h
rename to chrome/browser/ash/file_system_provider/operations/write_file.h
index d1b4684..c868b2a 100644
--- a/chrome/browser/chromeos/file_system_provider/operations/write_file.h
+++ b/chrome/browser/ash/file_system_provider/operations/write_file.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CHROME_BROWSER_CHROMEOS_FILE_SYSTEM_PROVIDER_OPERATIONS_WRITE_FILE_H_
-#define CHROME_BROWSER_CHROMEOS_FILE_SYSTEM_PROVIDER_OPERATIONS_WRITE_FILE_H_
+#ifndef CHROME_BROWSER_ASH_FILE_SYSTEM_PROVIDER_OPERATIONS_WRITE_FILE_H_
+#define CHROME_BROWSER_ASH_FILE_SYSTEM_PROVIDER_OPERATIONS_WRITE_FILE_H_
 
 #include <stdint.h>
 
@@ -12,7 +12,7 @@
 #include "base/files/file.h"
 #include "base/macros.h"
 #include "base/memory/ref_counted.h"
-#include "chrome/browser/chromeos/file_system_provider/operations/operation.h"
+#include "chrome/browser/ash/file_system_provider/operations/operation.h"
 #include "chrome/browser/chromeos/file_system_provider/provided_file_system_info.h"
 #include "chrome/browser/chromeos/file_system_provider/provided_file_system_interface.h"
 #include "chrome/browser/chromeos/file_system_provider/request_value.h"
@@ -64,4 +64,4 @@
 }  // namespace file_system_provider
 }  // namespace chromeos
 
-#endif  // CHROME_BROWSER_CHROMEOS_FILE_SYSTEM_PROVIDER_OPERATIONS_WRITE_FILE_H_
+#endif  // CHROME_BROWSER_ASH_FILE_SYSTEM_PROVIDER_OPERATIONS_WRITE_FILE_H_
diff --git a/chrome/browser/chromeos/file_system_provider/operations/write_file_unittest.cc b/chrome/browser/ash/file_system_provider/operations/write_file_unittest.cc
similarity index 97%
rename from chrome/browser/chromeos/file_system_provider/operations/write_file_unittest.cc
rename to chrome/browser/ash/file_system_provider/operations/write_file_unittest.cc
index 1afc6e4..dc195bc 100644
--- a/chrome/browser/chromeos/file_system_provider/operations/write_file_unittest.cc
+++ b/chrome/browser/ash/file_system_provider/operations/write_file_unittest.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/chromeos/file_system_provider/operations/write_file.h"
+#include "chrome/browser/ash/file_system_provider/operations/write_file.h"
 
 #include <memory>
 #include <string>
@@ -14,8 +14,8 @@
 #include "base/macros.h"
 #include "base/memory/ref_counted.h"
 #include "base/values.h"
+#include "chrome/browser/ash/file_system_provider/operations/test_util.h"
 #include "chrome/browser/chromeos/file_system_provider/icon_set.h"
-#include "chrome/browser/chromeos/file_system_provider/operations/test_util.h"
 #include "chrome/common/extensions/api/file_system_provider.h"
 #include "chrome/common/extensions/api/file_system_provider_capabilities/file_system_provider_capabilities_handler.h"
 #include "chrome/common/extensions/api/file_system_provider_internal.h"
diff --git a/chrome/browser/ash/guest_os/guest_os_share_path_unittest.cc b/chrome/browser/ash/guest_os/guest_os_share_path_unittest.cc
index b0925c8..32f05e7 100644
--- a/chrome/browser/ash/guest_os/guest_os_share_path_unittest.cc
+++ b/chrome/browser/ash/guest_os/guest_os_share_path_unittest.cc
@@ -88,13 +88,13 @@
       const std::string& failure_reason) {
     const base::DictionaryValue* prefs =
         profile()->GetPrefs()->GetDictionary(prefs::kGuestOSPathsSharedToVms);
-    EXPECT_TRUE(prefs->HasKey(shared_path_.value()));
+    EXPECT_NE(prefs->FindKey(shared_path_.value()), nullptr);
     EXPECT_EQ(prefs->FindKey(shared_path_.value())->GetList().size(), 1U);
     EXPECT_EQ(prefs->FindKey(shared_path_.value())->GetList()[0].GetString(),
               crostini::kCrostiniDefaultVmName);
     if (expected_persist == Persist::YES) {
       EXPECT_EQ(prefs->DictSize(), 2U);
-      EXPECT_TRUE(prefs->HasKey(share_path_.value()));
+      EXPECT_NE(prefs->FindKey(share_path_.value()), nullptr);
       EXPECT_EQ(prefs->FindKey(share_path_.value())->GetList().size(), 1U);
       EXPECT_EQ(prefs->FindKey(share_path_.value())->GetList()[0].GetString(),
                 expected_vm_name);
@@ -174,9 +174,9 @@
     const base::DictionaryValue* prefs =
         profile()->GetPrefs()->GetDictionary(prefs::kGuestOSPathsSharedToVms);
     if (expected_persist == Persist::YES) {
-      EXPECT_TRUE(prefs->HasKey(path.value()));
+      EXPECT_NE(prefs->FindKey(path.value()), nullptr);
     } else {
-      EXPECT_FALSE(prefs->HasKey(path.value()));
+      EXPECT_EQ(prefs->FindKey(path.value()), nullptr);
     }
     EXPECT_EQ(fake_seneschal_client_->unshare_path_called(),
               expected_seneschal_client_called == SeneschalClientCalled::YES);
diff --git a/chrome/browser/ash/web_applications/media_app/media_app_integration_browsertest.cc b/chrome/browser/ash/web_applications/media_app/media_app_integration_browsertest.cc
index f4b0d1d..a9327fc7 100644
--- a/chrome/browser/ash/web_applications/media_app/media_app_integration_browsertest.cc
+++ b/chrome/browser/ash/web_applications/media_app/media_app_integration_browsertest.cc
@@ -155,26 +155,16 @@
 // params. This exercises only web_applications logic.
 IN_PROC_BROWSER_TEST_P(MediaAppIntegrationTest, MediaAppLaunchWithFile) {
   WaitForTestSystemAppInstall();
-  content::WebContents* app;
-  {
-    auto params = LaunchParamsForApp(web_app::SystemAppType::MEDIA);
-
-    // Add the 800x600 PNG image to launch params.
-    params.launch_files.push_back(TestFile(kFilePng800x600));
-
-    app = LaunchApp(std::move(params));
-  }
+  content::WebContents* app = LaunchAppWithFile(web_app::SystemAppType::MEDIA,
+                                                TestFile(kFilePng800x600));
   PrepareAppForTest(app);
 
   EXPECT_EQ("800x600", WaitForImageAlt(app, kFilePng800x600));
 
   // Relaunch with a different file. This currently re-uses the existing window,
   // so we don't wait for page load here.
-  {
-    auto params = LaunchParamsForApp(web_app::SystemAppType::MEDIA);
-    params.launch_files = {TestFile(kFileJpeg640x480)};
-    LaunchAppWithoutWaiting(std::move(params));
-  }
+  LaunchAppWithFileWithoutWaiting(web_app::SystemAppType::MEDIA,
+                                  TestFile(kFileJpeg640x480));
 
   EXPECT_EQ("640x480", WaitForImageAlt(app, kFileJpeg640x480));
 }
@@ -260,9 +250,8 @@
 
 IN_PROC_BROWSER_TEST_P(MediaAppIntegrationTest, LoadsInkForImageAnnotation) {
   WaitForTestSystemAppInstall();
-  auto params = LaunchParamsForApp(web_app::SystemAppType::MEDIA);
-  params.launch_files = {TestFile(kFileJpeg640x480)};
-  content::WebContents* app = LaunchApp(std::move(params));
+  content::WebContents* app = LaunchAppWithFile(web_app::SystemAppType::MEDIA,
+                                                TestFile(kFileJpeg640x480));
   PrepareAppForTest(app);
 
   // Ensure test image loaded.
@@ -298,9 +287,8 @@
 // information panel.
 IN_PROC_BROWSER_TEST_P(MediaAppIntegrationTest, InformationPanel) {
   WaitForTestSystemAppInstall();
-  auto params = LaunchParamsForApp(web_app::SystemAppType::MEDIA);
-  params.launch_files = {TestFile(kFileJpeg640x480)};
-  content::WebContents* app = LaunchApp(std::move(params));
+  content::WebContents* app = LaunchAppWithFile(web_app::SystemAppType::MEDIA,
+                                                TestFile(kFileJpeg640x480));
   PrepareAppForTest(app);
 
   // Ensure test image loaded.
@@ -351,16 +339,9 @@
 // Test that the MediaApp can load RAW files passed on launch params.
 IN_PROC_BROWSER_TEST_P(MediaAppIntegrationTest, HandleRawFiles) {
   WaitForTestSystemAppInstall();
-
-  content::WebContents* web_ui;
-  {
-    auto params = LaunchParamsForApp(web_app::SystemAppType::MEDIA);
-
-    // Add the handcrafted RAW file to launch params and launch.
-    params.launch_files.push_back(TestFile(kRaw378x272));
-    web_ui = LaunchApp(std::move(params));
-    PrepareAppForTest(web_ui);
-  }
+  content::WebContents* web_ui =
+      LaunchAppWithFile(web_app::SystemAppType::MEDIA, TestFile(kRaw378x272));
+  PrepareAppForTest(web_ui);
 
   EXPECT_EQ("378x272", WaitForImageAlt(web_ui, kRaw378x272));
 
@@ -382,20 +363,15 @@
 
   // Launch with a file that has a different name to ensure the rotated version
   // of the file is detected robustly.
-  {
-    auto clearFileParams = LaunchParamsForApp(web_app::SystemAppType::MEDIA);
-    clearFileParams.launch_files = {TestFile(kFileJpeg640x480)};
-    LaunchAppWithoutWaiting(std::move(clearFileParams));
-  }
+  LaunchAppWithFileWithoutWaiting(web_app::SystemAppType::MEDIA,
+                                  TestFile(kFileJpeg640x480));
+
   EXPECT_EQ("640x480", WaitForImageAlt(web_ui, kFileJpeg640x480));
 
-  {
-    auto params = LaunchParamsForApp(web_app::SystemAppType::MEDIA);
+  // Add the handcrafted RAW file to launch params and launch.
+  LaunchAppWithFileWithoutWaiting(web_app::SystemAppType::MEDIA,
+                                  TestFile(kRaw378x272));
 
-    // Add the handcrafted RAW file to launch params and launch.
-    params.launch_files.push_back(TestFile(kRaw378x272));
-    LaunchAppWithoutWaiting(std::move(params));
-  }
   // Width and height should be swapped now.
   EXPECT_EQ("272x378", WaitForImageAlt(web_ui, kRaw378x272));
 }
diff --git a/chrome/browser/ash/web_applications/system_web_app_integration_test.cc b/chrome/browser/ash/web_applications/system_web_app_integration_test.cc
index e6fcbaa..c8e5c674 100644
--- a/chrome/browser/ash/web_applications/system_web_app_integration_test.cc
+++ b/chrome/browser/ash/web_applications/system_web_app_integration_test.cc
@@ -5,6 +5,7 @@
 #include "chrome/browser/ash/web_applications/system_web_app_integration_test.h"
 
 #include "base/strings/utf_string_conversions.h"
+#include "chrome/browser/apps/app_service/app_launch_params.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/browser_window.h"
@@ -68,3 +69,19 @@
   EXPECT_EQ(base::ASCIIToUTF16(title),
             app_browser->window()->GetNativeWindow()->GetTitle());
 }
+
+content::WebContents* SystemWebAppIntegrationTest::LaunchAppWithFile(
+    web_app::SystemAppType type,
+    const base::FilePath& file_path) {
+  apps::AppLaunchParams params = LaunchParamsForApp(type);
+  params.launch_files.push_back(file_path);
+  return LaunchApp(std::move(params));
+}
+
+void SystemWebAppIntegrationTest::LaunchAppWithFileWithoutWaiting(
+    web_app::SystemAppType type,
+    const base::FilePath& file_path) {
+  apps::AppLaunchParams params = LaunchParamsForApp(type);
+  params.launch_files.push_back(file_path);
+  LaunchAppWithoutWaiting(std::move(params));
+}
diff --git a/chrome/browser/ash/web_applications/system_web_app_integration_test.h b/chrome/browser/ash/web_applications/system_web_app_integration_test.h
index caff9c60..211c893c2 100644
--- a/chrome/browser/ash/web_applications/system_web_app_integration_test.h
+++ b/chrome/browser/ash/web_applications/system_web_app_integration_test.h
@@ -32,6 +32,16 @@
 
   // Helper to obtain browser()->profile().
   Profile* profile();
+
+  // Launch the given System App |type| with the given |file_path| as a launch
+  // file, and wait for the application to finish loading.
+  content::WebContents* LaunchAppWithFile(web_app::SystemAppType type,
+                                          const base::FilePath& file_path);
+
+  // Launch the given System App |type| with the given |file_path| as a launch
+  // file, without waiting for the application to finish loading.
+  void LaunchAppWithFileWithoutWaiting(web_app::SystemAppType type,
+                                       const base::FilePath& file_path);
 };
 
 #endif  // CHROME_BROWSER_ASH_WEB_APPLICATIONS_SYSTEM_WEB_APP_INTEGRATION_TEST_H_
diff --git a/chrome/browser/cart/cart_db_content.proto b/chrome/browser/cart/cart_db_content.proto
index 5d7b6845..119fe5b 100644
--- a/chrome/browser/cart/cart_db_content.proto
+++ b/chrome/browser/cart/cart_db_content.proto
@@ -14,7 +14,9 @@
   string currency_code = 1;
   // The whole units of amount.
   // e.g. for "USD", 1 unit is one US dollar.
-  int64 units = 2;
+  // This should be a int64_t, use string because base::Value does not support
+  // int64_t.
+  string units = 2;
   // Number of nano (10^-9) units of amount.
   // The value must be between -999,999,999 and +999,999,999 inclusive.
   // If `units` is positive, `nanos` must be positive or zero.
@@ -26,14 +28,18 @@
 
 // Used storing discount for a specific rule.
 message DiscountInfoProto {
-  int64 rule_id = 1;
+  // This should be a int64_t, use string because base::Value does not support
+  // int64_t.
+  string rule_id = 1;
 
   oneof discount {
     int32 percent_off = 2;
     MoneyProto amount_off = 3;
   }
 
-  string raw_merchant_offer_id = 4;
+  string merchant_rule_id = 4;
+
+  string raw_merchant_offer_id = 5;
 }
 
 // Used for storing the discount information of this cart.
@@ -43,7 +49,11 @@
   // Timestamp on the last time the discount is fetched.
   double last_fetched_timestamp = 2;
 
-  repeated DiscountInfoProto discount_info = 3;
+  // This should be a int64_t, use string because base::Value does not support
+  // int64_t.
+  string merchant_id = 3;
+
+  repeated DiscountInfoProto discount_info = 4;
 }
 
 // Used for storing information of products within the cart.
diff --git a/chrome/browser/cart/cart_discount_fetcher.cc b/chrome/browser/cart/cart_discount_fetcher.cc
index 1a2411f..2ce96909 100644
--- a/chrome/browser/cart/cart_discount_fetcher.cc
+++ b/chrome/browser/cart/cart_discount_fetcher.cc
@@ -4,9 +4,12 @@
 
 #include "chrome/browser/cart/cart_discount_fetcher.h"
 
+#include "base/json/json_reader.h"
 #include "base/json/json_writer.h"
 #include "base/memory/scoped_refptr.h"
 #include "base/sequence_checker.h"
+#include "chrome/browser/cart/cart_db.h"
+#include "chrome/browser/cart/cart_db_content.pb.h"
 #include "chrome/browser/endpoint_fetcher/endpoint_fetcher.h"
 #include "chrome/browser/profiles/profile.h"
 #include "net/traffic_annotation/network_traffic_annotation.h"
@@ -20,8 +23,148 @@
 const char FETCH_DISCOUNTS_ENDPOINT[] =
     "https://memex-pa.googleapis.com/v1/shopping/cart/discounts";
 const int64_t TIMEOUT_MS = 1000;
+
+// TODO(crbug.com/1207197): Consolidate to one util method to get string.
+std::string GetMerchantUrl(const base::Value* merchant_identifier) {
+  DCHECK(merchant_identifier->is_dict());
+
+  // TODO(crbug.com/1207197): Use a constant instead.
+  const base::Value* value = merchant_identifier->FindKey("cartUrl");
+  if (!value || !value->is_string()) {
+    NOTREACHED() << "Missing cart_url or it is not a string";
+    return "";
+  }
+
+  return value->GetString();
+}
+
+std::string GetMerchantId(const base::Value* merchant_identifier) {
+  DCHECK(merchant_identifier->is_dict());
+
+  const base::Value* value = merchant_identifier->FindKey("merchantId");
+  if (!value || !value->is_string()) {
+    NOTREACHED() << "Missing merchant_id or it is not a string";
+    return "";
+  }
+
+  return value->GetString();
+}
+
+std::vector<cart_db::DiscountInfoProto> CovertToDiscountInfoProto(
+    const base::Value* rule_discount_list) {
+  std::vector<cart_db::DiscountInfoProto> cart_discounts;
+
+  if (!rule_discount_list || !rule_discount_list->is_list()) {
+    NOTREACHED() << "Missing rule_discounts or it is not a list";
+    return cart_discounts;
+  }
+
+  cart_discounts.reserve(rule_discount_list->GetList().size());
+
+  for (const auto& rule_discount : rule_discount_list->GetList()) {
+    cart_db::DiscountInfoProto discount_proto;
+
+    // Parse ruleId
+    const base::Value* rule_id_value = rule_discount.FindKey("ruleId");
+    if (!rule_id_value || !rule_id_value->is_string()) {
+      NOTREACHED() << "Missing rule_id or it is not a string";
+      continue;
+    }
+    discount_proto.set_rule_id(rule_id_value->GetString());
+
+    // Parse merchantRuleId
+    const base::Value* merchant_rule_id_value =
+        rule_discount.FindKey("merchantRuleId");
+    if (!merchant_rule_id_value || !merchant_rule_id_value->is_string()) {
+      NOTREACHED() << "Missing merchant_rule_id or it is not a string";
+      continue;
+    }
+    discount_proto.set_merchant_rule_id(merchant_rule_id_value->GetString());
+
+    // Parse rawMerchantOfferId
+    const base::Value* raw_merchant_offer_id_value =
+        rule_discount.FindKey("rawMerchantOfferId");
+    if (!raw_merchant_offer_id_value ||
+        !raw_merchant_offer_id_value->is_string()) {
+      NOTREACHED()
+          << "Missing raw_merchant_offer_id or rule_id is not a string";
+      continue;
+    }
+    discount_proto.set_raw_merchant_offer_id(
+        raw_merchant_offer_id_value->GetString());
+
+    // Parse discount
+    const base::Value* discount_value = rule_discount.FindKey("discount");
+    if (!discount_value || !discount_value->is_dict()) {
+      NOTREACHED() << "discount is missing or it is not a dictionary";
+      continue;
+    }
+
+    if (discount_value->FindKey("percentOff")) {
+      const base::Value* percent_off_value =
+          discount_value->FindKey("percentOff");
+      if (!percent_off_value->is_int()) {
+        NOTREACHED() << "percent_off is not a int";
+        continue;
+      }
+      discount_proto.set_percent_off(percent_off_value->GetInt());
+
+    } else {
+      const base::Value* amount_off_value =
+          discount_value->FindKey("amountOff");
+      if (!amount_off_value || !amount_off_value->is_dict()) {
+        NOTREACHED() << "amount_off is not a dictionary";
+        continue;
+      }
+
+      auto* money = discount_proto.mutable_amount_off();
+      // Parse currencyCode
+      const base::Value* currency_code_value =
+          amount_off_value->FindKey("currencyCode");
+      if (!currency_code_value || !currency_code_value->is_string()) {
+        NOTREACHED() << "Missing currency_code or it is not a string";
+        continue;
+      }
+      money->set_currency_code(currency_code_value->GetString());
+
+      // Parse units
+      const base::Value* units_value = amount_off_value->FindKey("units");
+      if (!units_value || !units_value->is_string()) {
+        NOTREACHED() << "Missing units or it is not a string";
+        continue;
+      }
+      money->set_units(units_value->GetString());
+
+      // Parse nanos
+      const base::Value* nanos_value = amount_off_value->FindKey("nanos");
+      if (!nanos_value || !nanos_value->is_int()) {
+        NOTREACHED() << "Missing nanos or it is not a int";
+        continue;
+      }
+      money->set_nanos(nanos_value->GetInt());
+    }
+
+    cart_discounts.emplace_back(std::move(discount_proto));
+  }
+
+  return cart_discounts;
+}
 }  // namespace
 
+MerchantIdAndDiscounts::MerchantIdAndDiscounts(
+    std::string merchant_id,
+    std::vector<cart_db::DiscountInfoProto> discount_list)
+    : merchant_id(std::move(merchant_id)),
+      discount_list(std::move(discount_list)) {}
+
+MerchantIdAndDiscounts::MerchantIdAndDiscounts(MerchantIdAndDiscounts&& other) =
+    default;
+
+MerchantIdAndDiscounts& MerchantIdAndDiscounts::operator=(
+    MerchantIdAndDiscounts&& other) = default;
+
+MerchantIdAndDiscounts::~MerchantIdAndDiscounts() = default;
+
 std::unique_ptr<CartDiscountFetcher>
 CartDiscountFetcherFactory::createFetcher() {
   return std::make_unique<CartDiscountFetcher>();
@@ -88,11 +231,103 @@
       network::SharedURLLoaderFactory::Create(std::move(pending_factory)));
 }
 
+std::string CartDiscountFetcher::generatePostData(
+    std::vector<CartDB::KeyAndValue> proto_pairs,
+    base::Time current_time) {
+  auto carts_list = std::make_unique<base::ListValue>();
+
+  for (const CartDB::KeyAndValue& key_and_value : proto_pairs) {
+    cart_db::ChromeCartContentProto cart_proto = key_and_value.second;
+
+    auto cart_dict = std::make_unique<base::DictionaryValue>();
+    // Set merchantIdentifier.
+    auto merchant_dict = std::make_unique<base::DictionaryValue>();
+    merchant_dict->SetString("cartUrl", cart_proto.merchant_cart_url());
+    cart_dict->SetDictionary("merchantIdentifier", std::move(merchant_dict));
+
+    // Set CartAbandonedTimeMinutes.
+    int cart_abandoned_time_mintues =
+        (current_time - base::Time::FromDoubleT(cart_proto.timestamp()))
+            .InMinutes();
+    cart_dict->SetInteger("cartAbandonedTimeMinutes",
+                          cart_abandoned_time_mintues);
+
+    // TODO(meiliang): Set real rawMerchantOffers.
+    auto offer_list = std::make_unique<base::ListValue>();
+    offer_list->Append("offer1");
+    offer_list->Append("offer2");
+    cart_dict->SetList("rawMerchantOffers", std::move(offer_list));
+
+    // Add cart_dict to cart_list.
+    carts_list->Append(std::move(cart_dict));
+  }
+
+  base::DictionaryValue request_dic;
+  request_dic.SetList("carts", std::move(carts_list));
+
+  std::string request_json;
+  base::JSONWriter::Write(request_dic, &request_json);
+  return request_json;
+}
+
 void CartDiscountFetcher::OnDiscountsAvailable(
     std::unique_ptr<EndpointFetcher> endpoint_fetcher,
     CartDiscountFetcherCallback callback,
     std::unique_ptr<EndpointResponse> responses) {
-  // TODO(meiliang): parse response;
-  CartDiscountMap result;
-  std::move(callback).Run(std::move(result));
+  CartDiscountMap cart_discount_map;
+  base::Optional<base::Value> value =
+      base::JSONReader::Read(responses->response);
+  if (!value || !value.has_value() || !value->is_dict()) {
+    NOTREACHED() << "Response is not valid or does not have value or it is "
+                    "not a dictionary";
+    std::move(callback).Run(std::move(cart_discount_map));
+    return;
+  }
+
+  const base::Value* error_value = value->FindKey("error");
+  if (error_value) {
+    NOTREACHED() << "Error: " << responses->response;
+    std::move(callback).Run(std::move(cart_discount_map));
+    return;
+  }
+
+  const base::Value* discounts_list = value->FindKey("discounts");
+  if (!discounts_list || !discounts_list->is_list()) {
+    NOTREACHED() << "Missing discounts or it is not a list";
+    std::move(callback).Run(std::move(cart_discount_map));
+    return;
+  }
+
+  for (const auto& merchant_discount : discounts_list->GetList()) {
+    // Parse merchant_identifier.
+    const base::Value* merchant_identifier =
+        merchant_discount.FindKey("merchantIdentifier");
+    if (!merchant_identifier) {
+      NOTREACHED() << "Missing merchant_identifier";
+      continue;
+    }
+    std::string merchant_url = GetMerchantUrl(merchant_identifier);
+    if (merchant_url.empty()) {
+      continue;
+    }
+
+    std::string merchant_id = GetMerchantId(merchant_identifier);
+    if (merchant_id.empty()) {
+      continue;
+    }
+
+    // Parse rule discounts.
+    auto cart_discounts =
+        CovertToDiscountInfoProto(merchant_discount.FindKey("ruleDiscounts"));
+    if (!cart_discounts.size()) {
+      continue;
+    }
+
+    MerchantIdAndDiscounts merchant_id_and_discounts(std::move(merchant_id),
+                                                     std::move(cart_discounts));
+    cart_discount_map.emplace(merchant_url,
+                              std::move(merchant_id_and_discounts));
+  }
+
+  std::move(callback).Run(std::move(cart_discount_map));
 }
diff --git a/chrome/browser/cart/cart_discount_fetcher.h b/chrome/browser/cart/cart_discount_fetcher.h
index b3cdf22..f35b3b2 100644
--- a/chrome/browser/cart/cart_discount_fetcher.h
+++ b/chrome/browser/cart/cart_discount_fetcher.h
@@ -16,13 +16,28 @@
 class PendingSharedURLLoaderFactory;
 }  // namespace network
 
+struct MerchantIdAndDiscounts {
+ public:
+  std::string merchant_id;
+  std::vector<cart_db::DiscountInfoProto> discount_list;
+
+  explicit MerchantIdAndDiscounts(
+      std::string merchant_id,
+      std::vector<cart_db::DiscountInfoProto> discount_list);
+  MerchantIdAndDiscounts(const MerchantIdAndDiscounts& other) = delete;
+  MerchantIdAndDiscounts& operator=(const MerchantIdAndDiscounts& other) =
+      delete;
+  MerchantIdAndDiscounts(MerchantIdAndDiscounts&& other);
+  MerchantIdAndDiscounts& operator=(MerchantIdAndDiscounts&& other);
+  ~MerchantIdAndDiscounts();
+};
+
 class CartDiscountFetcher {
  public:
   // base::flat_map is used here for optimization, since the number of carts are
   // expected to be low (< 100) at this stage. Need to use std::map when number
   // gets larger.
-  using CartDiscountMap =
-      base::flat_map<std::string, std::vector<cart_db::DiscountInfoProto>>;
+  using CartDiscountMap = base::flat_map<std::string, MerchantIdAndDiscounts>;
 
   using CartDiscountFetcherCallback = base::OnceCallback<void(CartDiscountMap)>;
 
@@ -33,6 +48,7 @@
       CartDiscountFetcherCallback callback);
 
  private:
+  friend class CartDiscountFetcherTest;
   // TODO(meiliang): Add param a list of carts to fetch.
   static void fetchForDiscounts(
       std::unique_ptr<network::PendingSharedURLLoaderFactory> pending_factory,
@@ -43,6 +59,9 @@
       std::unique_ptr<EndpointResponse> responses);
   static std::unique_ptr<EndpointFetcher> CreateEndpointFetcher(
       std::unique_ptr<network::PendingSharedURLLoaderFactory> pending_factory);
+  static std::string generatePostData(
+      std::vector<CartDB::KeyAndValue> proto_pairs,
+      base::Time current_timestamp);
 };
 
 class CartDiscountFetcherFactory {
diff --git a/chrome/browser/cart/cart_discount_fetcher_unittest.cc b/chrome/browser/cart/cart_discount_fetcher_unittest.cc
new file mode 100644
index 0000000..a9111549
--- /dev/null
+++ b/chrome/browser/cart/cart_discount_fetcher_unittest.cc
@@ -0,0 +1,150 @@
+// 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/cart/cart_discount_fetcher.h"
+#include "base/test/mock_callback.h"
+#include "chrome/browser/cart/fetch_discount_worker.h"
+#include "chrome/browser/endpoint_fetcher/endpoint_fetcher.h"
+#include "chrome/browser/persisted_state_db/profile_proto_db.h"
+#include "content/public/test/browser_task_environment.h"
+#include "net/traffic_annotation/network_traffic_annotation_test_helper.h"
+#include "services/network/public/cpp/weak_wrapper_shared_url_loader_factory.h"
+#include "services/network/test/test_url_loader_factory.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace {
+cart_db::ChromeCartContentProto BuildProto(const char* domain,
+                                           const char* merchant_url,
+                                           const double timestamp) {
+  cart_db::ChromeCartContentProto proto;
+  proto.set_key(domain);
+  proto.set_merchant_cart_url(merchant_url);
+  proto.set_timestamp(timestamp);
+  return proto;
+}
+using ShoppingCarts =
+    std::vector<ProfileProtoDB<cart_db::ChromeCartContentProto>::KeyAndValue>;
+
+const char kMockMerchantA[] = "foo.com";
+const char kMockMerchantCartURLA[] = "https://www.foo.com/cart";
+const int kMockMerchantALastTimestamp = 1;
+const int kThirtyMinutesInSeconds = 1800;
+
+const char kEndpointResponse[] =
+    "{ "
+    "  \"discounts\": [ "
+    "    { "
+    "      \"merchantIdentifier\": { "
+    "        \"cartUrl\": \"http://foo.com/cart\", "
+    "        \"merchantId\": \"0\" "
+    "      }, "
+    "      \"ruleDiscounts\": [ "
+    "        { "
+    "          \"ruleId\": \"0\", "
+    "          \"discount\": { "
+    "            \"percentOff\": 99 "
+    "            }, "
+    "          \"merchantRuleId\": \"1\", "
+    "          \"rawMerchantOfferId\": \"offerID\"  "
+    "        }, "
+    "        { "
+    "          \"ruleId\": \"1\", "
+    "          \"discount\": { "
+    "            \"amountOff\": { "
+    "              \"currencyCode\": \"USD\", "
+    "              \"units\": \"2\", "
+    "              \"nanos\": 1 "
+    "            } "
+    "          }, "
+    "          \"merchantRuleId\": \"1\", "
+    "          \"rawMerchantOfferId\": \"offerID\"  "
+    "        } "
+    "      ] "
+    "    } "
+    "  ] "
+    "} ";
+
+}  // namespace
+
+class MockEndpointFetcher : public EndpointFetcher {
+ public:
+  explicit MockEndpointFetcher(
+      const net::NetworkTrafficAnnotationTag& annotation_tag)
+      : EndpointFetcher(annotation_tag) {}
+
+  MOCK_METHOD(void,
+              PerformRequest,
+              (EndpointFetcherCallback endpoint_fetcher_callback,
+               const char* key),
+              (override));
+};
+
+class CartDiscountFetcherTest {
+ public:
+  static std::string generatePostData(
+      std::vector<CartDB::KeyAndValue> proto_pairs,
+      double current_timestamp) {
+    return CartDiscountFetcher::generatePostData(
+        std::move(proto_pairs), base::Time::FromDoubleT(current_timestamp));
+  }
+
+  static void OnDiscountsAvailable(
+      std::unique_ptr<EndpointFetcher> endpoint_fetcher,
+      CartDiscountFetcher::CartDiscountFetcherCallback callback,
+      std::unique_ptr<EndpointResponse> responses) {
+    CartDiscountFetcher::OnDiscountsAvailable(
+        std::move(endpoint_fetcher), std::move(callback), std::move(responses));
+  }
+};
+
+TEST(CartDiscountFetcherTest, TestGeneratePostData) {
+  ShoppingCarts shoppingcartA = {
+      {kMockMerchantA, BuildProto(kMockMerchantA, kMockMerchantCartURLA,
+                                  kMockMerchantALastTimestamp)}};
+
+  // Expected json string:
+  // "{"
+  // "  \"carts\": ["
+  // "    {"
+  // "      \"cartAbandonedTimeMinutes\": 30,"
+  // "      \"merchantIdentifier\": {"
+  // "        \"cartUrl\": \"www.foo.com/cart\","
+  // "      },"
+  // "      \"rawMerchantOffers\": ["
+  // "        \"offer1\","
+  // "        \"offer2\}"
+  // "      ]"
+  // "    }"
+  // "  ]"
+  // "}"
+  std::string expected =
+      "{\"carts\":[{\"cartAbandonedTimeMinutes\":30,\"merchantIdentifier\":{"
+      "\"cartUrl\":\"https://www.foo.com/"
+      "cart\"},\"rawMerchantOffers\":[\"offer1\",\"offer2\"]}]}";
+
+  int abandondTimeInSeconds =
+      kMockMerchantALastTimestamp + kThirtyMinutesInSeconds;  // 30 Minutes
+  std::string result = CartDiscountFetcherTest::generatePostData(
+      std::move(shoppingcartA), abandondTimeInSeconds);
+  EXPECT_EQ(expected, result);
+}
+
+TEST(CartDiscountFetcherTest, TestOnDiscountsAvailableParsing) {
+  std::unique_ptr<EndpointFetcher> mock_endpoint_fetcher =
+      std::make_unique<MockEndpointFetcher>(TRAFFIC_ANNOTATION_FOR_TESTS);
+
+  base::MockCallback<CartDiscountFetcher::CartDiscountFetcherCallback>
+      mock_callback;
+
+  auto fake_responses = std::make_unique<EndpointResponse>();
+  fake_responses->response = kEndpointResponse;
+
+  // TODO(meiliang): Test the callback argument.
+  EXPECT_CALL(mock_callback, Run(testing::_));
+
+  CartDiscountFetcherTest::OnDiscountsAvailable(
+      std::move(mock_endpoint_fetcher), mock_callback.Get(),
+      std::move(fake_responses));
+}
diff --git a/chrome/browser/cart/cart_service_unittest.cc b/chrome/browser/cart/cart_service_unittest.cc
index 698522e1..ea419b7 100644
--- a/chrome/browser/cart/cart_service_unittest.cc
+++ b/chrome/browser/cart/cart_service_unittest.cc
@@ -38,7 +38,7 @@
 cart_db::ChromeCartContentProto AddDiscountToProto(
     cart_db::ChromeCartContentProto proto,
     const double timestamp,
-    const int rule_id,
+    const std::string& rule_id,
     const int percent_off,
     const char* offer_id) {
   proto.mutable_discount_info()->set_last_fetched_timestamp(timestamp);
@@ -81,7 +81,7 @@
 const ShoppingCarts kEmptyExpected = {};
 
 // Value used for discount.
-const int kMockMerchantADiscountRuleId = 1;
+const char kMockMerchantADiscountRuleId[] = "1";
 const int kMockMerchantADiscountsPercentOff = 10;
 const char kMockMerchantADiscountsRawMerchantOfferId[] = "merchantAOfferId";
 
diff --git a/chrome/browser/cart/fetch_discount_worker.cc b/chrome/browser/cart/fetch_discount_worker.cc
index 1188cf9..ef411b7 100644
--- a/chrome/browser/cart/fetch_discount_worker.cc
+++ b/chrome/browser/cart/fetch_discount_worker.cc
@@ -9,6 +9,7 @@
 #include "chrome/browser/cart/cart_discount_fetcher.h"
 #include "content/public/browser/browser_task_traits.h"
 #include "content/public/browser/browser_thread.h"
+#include "net/base/registry_controlled_domains/registry_controlled_domain.h"
 #include "services/network/public/cpp/shared_url_loader_factory.h"
 
 namespace {
@@ -17,6 +18,11 @@
 const int64_t kDelayFetchMs = 1800000;
 const int kImmediateFetchMs = 0;
 
+std::string eTLDPlusOne(const GURL& url) {
+  return net::registry_controlled_domains::GetDomainAndRegistry(
+      url, net::registry_controlled_domains::INCLUDE_PRIVATE_REGISTRIES);
+}
+
 }  // namespace
 
 CartLoader::CartLoader(Profile* profile)
@@ -33,7 +39,13 @@
 
 CartDiscountUpdater::~CartDiscountUpdater() = default;
 
-void CartDiscountUpdater::update() {}
+void CartDiscountUpdater::update(
+    const std::string& cart_url,
+    const cart_db::ChromeCartContentProto new_proto) {
+  const GURL url(cart_url);
+  std::string domain = eTLDPlusOne(url);
+  cart_service_->AddCart(domain, url, std::move(new_proto));
+}
 
 CartLoaderAndUpdaterFactory::CartLoaderAndUpdaterFactory(Profile* profile)
     : profile_(profile) {}
@@ -149,9 +161,41 @@
     bool success,
     std::vector<CartDB::KeyAndValue> proto_pairs) {
   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
+  if (!success || !proto_pairs.size()) {
+    return;
+  }
 
-  // TODO(meiliang): Iterate over |proto_pairs| and update it based on
-  // |discounts|.
+  auto updater = cart_loader_and_updater_factory_->createCartDiscountUpdater();
+
+  double current_timestamp = base::Time::Now().ToDoubleT();
+
+  for (CartDB::KeyAndValue& key_and_value : proto_pairs) {
+    cart_db::ChromeCartContentProto cart_proto = key_and_value.second;
+    std::string cart_url = cart_proto.merchant_cart_url();
+
+    cart_db::ChromeCartDiscountProto* cart_discount_proto =
+        cart_proto.mutable_discount_info();
+
+    cart_discount_proto->set_last_fetched_timestamp(current_timestamp);
+
+    if (!discounts.count(cart_url)) {
+      cart_discount_proto->clear_discount_text();
+      cart_discount_proto->clear_discount_info();
+      updater->update(cart_url, std::move(cart_proto));
+      continue;
+    }
+
+    std::string merchant_id = discounts.at(cart_url).merchant_id;
+    cart_discount_proto->set_merchant_id(merchant_id);
+
+    std::vector<cart_db::DiscountInfoProto> discount_infos =
+        discounts.at(cart_url).discount_list;
+    cart_discount_proto->set_discount_text("discount");
+    *cart_discount_proto->mutable_discount_info() = {discount_infos.begin(),
+                                                     discount_infos.end()};
+
+    updater->update(cart_url, std::move(cart_proto));
+  }
 
   // Continue to work
   PrepareToFetch(kDelayFetchMs);
diff --git a/chrome/browser/cart/fetch_discount_worker.h b/chrome/browser/cart/fetch_discount_worker.h
index 510234d..6412ae7 100644
--- a/chrome/browser/cart/fetch_discount_worker.h
+++ b/chrome/browser/cart/fetch_discount_worker.h
@@ -30,7 +30,8 @@
  public:
   explicit CartDiscountUpdater(Profile* profile);
   virtual ~CartDiscountUpdater();
-  virtual void update();
+  virtual void update(const std::string& cart_url,
+                      const cart_db::ChromeCartContentProto new_proto);
 
  private:
   CartService* cart_service_;
diff --git a/chrome/browser/cart/fetch_discount_worker_unittest.cc b/chrome/browser/cart/fetch_discount_worker_unittest.cc
index bc6807e..15c24db 100644
--- a/chrome/browser/cart/fetch_discount_worker_unittest.cc
+++ b/chrome/browser/cart/fetch_discount_worker_unittest.cc
@@ -13,6 +13,64 @@
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
+namespace {
+cart_db::DiscountInfoProto BuildPercentOffDiscountInfoProto(
+    const std::string& rule_id,
+    const std::string& merchant_rule_id,
+    const std::string& raw_merchant_offer_id,
+    const int percent_off) {
+  cart_db::DiscountInfoProto proto;
+  proto.set_rule_id(rule_id);
+  proto.set_merchant_rule_id(merchant_rule_id);
+  proto.set_percent_off(percent_off);
+  proto.set_raw_merchant_offer_id(raw_merchant_offer_id);
+  return proto;
+}
+
+cart_db::ChromeCartContentProto BuildCartContentProto(const char* domain,
+                                                      const char* merchant_url,
+                                                      const double timestamp) {
+  cart_db::ChromeCartContentProto proto;
+  proto.set_key(domain);
+  proto.set_merchant_cart_url(merchant_url);
+  proto.set_timestamp(timestamp);
+  return proto;
+}
+
+cart_db::ChromeCartContentProto AddDiscountToProto(
+    cart_db::ChromeCartContentProto proto,
+    const std::string& merchant_id,
+    cart_db::DiscountInfoProto discount_proto) {
+  proto.mutable_discount_info()->set_merchant_id(merchant_id);
+  (*(proto.mutable_discount_info()->add_discount_info())) = discount_proto;
+  return proto;
+}
+
+MATCHER_P(EqualsProto, message, "") {
+  std::string expected_serialized, actual_serialized;
+  message.SerializeToString(&expected_serialized);
+  arg.SerializeToString(&actual_serialized);
+  return expected_serialized == actual_serialized;
+}
+
+const char kMockMerchantA[] = "foo.com";
+const char kMockMerchantACartUrl[] = "https://www.foo.com/cart";
+const char kMockMerchantAId[] = "123";
+const char kMockMerchantARuleId[] = "456";
+const char kMockMerchantARawMerchantOfferId[] = "789";
+const int kMockMerchantAPercentOff = 10;
+const double kMockMerchantATimestamp = base::Time::Now().ToDoubleT();
+const cart_db::ChromeCartContentProto kMockMerchantACartContentProto =
+    BuildCartContentProto(kMockMerchantA,
+                          kMockMerchantACartUrl,
+                          kMockMerchantATimestamp);
+const std::vector<cart_db::DiscountInfoProto> kMockMerchantADiscounts = {
+    BuildPercentOffDiscountInfoProto(kMockMerchantARuleId,
+                                     kMockMerchantARuleId,
+                                     kMockMerchantARawMerchantOfferId,
+                                     kMockMerchantAPercentOff)};
+}  // namespace
+
 class FakeCartDiscountFetcher : public CartDiscountFetcher {
  public:
   void Fetch(
@@ -74,7 +132,7 @@
   explicit FakeCartLoader(Profile* profile) : CartLoader(profile) {}
 
   void LoadAllCarts(CartDB::LoadCallback callback) override {
-    std::move(callback).Run(true, std::move(fake_cart_data_));
+    std::move(callback).Run(true, fake_cart_data_);
   }
 
   void setFakeCartData(std::vector<CartDB::KeyAndValue> proto_pairs) {
@@ -90,7 +148,33 @@
   explicit FakeCartDiscountUpdater(Profile* profile)
       : CartDiscountUpdater(profile) {}
 
-  void update() override {}
+  void setExpectedData(
+      cart_db::ChromeCartContentProto fake_updater_expected_data,
+      bool has_discounts) {
+    expected_update_data_ = fake_updater_expected_data;
+    has_discounts_ = has_discounts;
+  }
+
+  void update(const std::string& cart_url,
+              const cart_db::ChromeCartContentProto new_proto) override {
+    // Verify discount_info.
+    int new_proto_discount_size =
+        new_proto.discount_info().discount_info_size();
+    EXPECT_EQ(new_proto_discount_size,
+              expected_update_data_.discount_info().discount_info_size());
+
+    EXPECT_EQ(new_proto_discount_size != 0, has_discounts_);
+
+    for (int i = 0; i < new_proto_discount_size; i++) {
+      EXPECT_THAT(
+          new_proto.discount_info().discount_info(i),
+          EqualsProto(expected_update_data_.discount_info().discount_info(i)));
+    }
+  }
+
+ private:
+  cart_db::ChromeCartContentProto expected_update_data_;
+  bool has_discounts_;
 };
 
 class FakeCartLoaderAndUpdaterFactory : public CartLoaderAndUpdaterFactory {
@@ -100,21 +184,32 @@
 
   std::unique_ptr<CartLoader> createCartLoader() override {
     auto fake_loader = std::make_unique<FakeCartLoader>(profile_);
-    fake_loader->setFakeCartData(fake_data_);
+    fake_loader->setFakeCartData(fake_loader_data_);
     return fake_loader;
   }
   std::unique_ptr<CartDiscountUpdater> createCartDiscountUpdater() override {
     auto fake_updater = std::make_unique<FakeCartDiscountUpdater>(profile_);
+    fake_updater->setExpectedData(fake_updater_expected_data_,
+                                  fake_updater_has_discounts_);
     return fake_updater;
   }
 
   void setCartLoaderFakeData(std::vector<CartDB::KeyAndValue> fake_data) {
-    fake_data_ = fake_data;
+    fake_loader_data_ = fake_data;
+  }
+
+  void setCartDiscountUpdaterExpectedData(
+      cart_db::ChromeCartContentProto fake_updater_expected_data,
+      bool has_discounts) {
+    fake_updater_expected_data_ = fake_updater_expected_data;
+    fake_updater_has_discounts_ = has_discounts;
   }
 
  private:
   Profile* profile_;
-  std::vector<CartDB::KeyAndValue> fake_data_;
+  std::vector<CartDB::KeyAndValue> fake_loader_data_;
+  cart_db::ChromeCartContentProto fake_updater_expected_data_;
+  bool fake_updater_has_discounts_;
 };
 
 class FetchDiscountWorkerTest : public testing::Test {
@@ -163,7 +258,7 @@
 
   std::unique_ptr<MockCartDiscountFetcher> mock_fetcher_;
 
-  std::unique_ptr<CartLoaderAndUpdaterFactory>
+  std::unique_ptr<FakeCartLoaderAndUpdaterFactory>
       fake_cart_loader_and_updater_factory_;
 
   TestingProfile profile_;
@@ -189,3 +284,62 @@
   fetch_discount_worker_->Start(base::TimeDelta::FromMilliseconds(0));
   task_environment_.RunUntilIdle();
 }
+
+TEST_F(FetchDiscountWorkerTest, TestStart_DiscountUpdatedWithDiscount) {
+  CartDiscountFetcher::CartDiscountMap fake_result;
+  fake_result.emplace(
+      kMockMerchantACartUrl,
+      MerchantIdAndDiscounts(kMockMerchantAId, kMockMerchantADiscounts));
+  mock_fetcher_->DelegateToFake(std::move(fake_result));
+
+  CreateCartDiscountFetcherFactory();
+  CartDB::KeyAndValue mockMerchantACartContentKeyAndProto =
+      std::make_pair(kMockMerchantA, kMockMerchantACartContentProto);
+  std::vector<CartDB::KeyAndValue> loader_fake_data(
+      1, mockMerchantACartContentKeyAndProto);
+  fake_cart_loader_and_updater_factory_->setCartLoaderFakeData(
+      loader_fake_data);
+
+  cart_db::ChromeCartContentProto cart_content_proto = BuildCartContentProto(
+      kMockMerchantA, kMockMerchantACartUrl, kMockMerchantATimestamp);
+  cart_db::ChromeCartContentProto updater_expected_data = AddDiscountToProto(
+      cart_content_proto, kMockMerchantAId, kMockMerchantADiscounts[0]);
+  fake_cart_loader_and_updater_factory_->setCartDiscountUpdaterExpectedData(
+      updater_expected_data, true);
+
+  CreateWorker();
+
+  fetch_discount_worker_->Start(base::TimeDelta::FromMilliseconds(0));
+  task_environment_.RunUntilIdle();
+}
+
+TEST_F(FetchDiscountWorkerTest, TestStart_DiscountUpdatedClearDiscount) {
+  // No discount available.
+  CartDiscountFetcher::CartDiscountMap fake_result;
+  mock_fetcher_->DelegateToFake(std::move(fake_result));
+
+  CreateCartDiscountFetcherFactory();
+
+  // Loader fake data contatins discount.
+  cart_db::ChromeCartContentProto cart_content_proto = BuildCartContentProto(
+      kMockMerchantA, kMockMerchantACartUrl, kMockMerchantATimestamp);
+  cart_db::ChromeCartContentProto cart_with_discount = AddDiscountToProto(
+      cart_content_proto, kMockMerchantAId, kMockMerchantADiscounts[0]);
+  CartDB::KeyAndValue mockMerchantACartContentKeyAndProto =
+      std::make_pair(kMockMerchantA, cart_with_discount);
+  std::vector<CartDB::KeyAndValue> loader_fake_data(
+      1, mockMerchantACartContentKeyAndProto);
+  fake_cart_loader_and_updater_factory_->setCartLoaderFakeData(
+      loader_fake_data);
+
+  // Updater is expected data without discount.
+  cart_db::ChromeCartContentProto updater_expected_data = BuildCartContentProto(
+      kMockMerchantA, kMockMerchantACartUrl, kMockMerchantATimestamp);
+  fake_cart_loader_and_updater_factory_->setCartDiscountUpdaterExpectedData(
+      updater_expected_data, false);
+
+  CreateWorker();
+
+  fetch_discount_worker_->Start(base::TimeDelta::FromMilliseconds(0));
+  task_environment_.RunUntilIdle();
+}
diff --git a/chrome/browser/chrome_content_browser_client.cc b/chrome/browser/chrome_content_browser_client.cc
index ad6a126..ef87e64 100644
--- a/chrome/browser/chrome_content_browser_client.cc
+++ b/chrome/browser/chrome_content_browser_client.cc
@@ -419,6 +419,7 @@
 #include "chrome/browser/ash/arc/fileapi/arc_content_file_system_backend_delegate.h"
 #include "chrome/browser/ash/arc/fileapi/arc_documents_provider_backend_delegate.h"
 #include "chrome/browser/ash/drive/fileapi/drivefs_file_system_backend_delegate.h"
+#include "chrome/browser/ash/file_system_provider/fileapi/backend_delegate.h"
 #include "chrome/browser/ash/login/signin/merge_session_navigation_throttle.h"
 #include "chrome/browser/ash/login/signin/merge_session_throttling_utils.h"
 #include "chrome/browser/ash/login/signin_partition_manager.h"
@@ -430,7 +431,6 @@
 #include "chrome/browser/chromeos/chrome_browser_main_chromeos.h"
 #include "chrome/browser/chromeos/chrome_content_browser_client_chromeos_part.h"
 #include "chrome/browser/chromeos/file_manager/app_id.h"
-#include "chrome/browser/chromeos/file_system_provider/fileapi/backend_delegate.h"
 #include "chrome/browser/chromeos/fileapi/external_file_url_loader_factory.h"
 #include "chrome/browser/chromeos/fileapi/file_system_backend.h"
 #include "chrome/browser/chromeos/fileapi/mtp_file_system_backend_delegate.h"
diff --git a/chrome/browser/chromeos/BUILD.gn b/chrome/browser/chromeos/BUILD.gn
index 90b963cf..cbf7a7b 100644
--- a/chrome/browser/chromeos/BUILD.gn
+++ b/chrome/browser/chromeos/BUILD.gn
@@ -1200,6 +1200,60 @@
     "../ash/drive/fileapi/drivefs_async_file_util.h",
     "../ash/drive/fileapi/drivefs_file_system_backend_delegate.cc",
     "../ash/drive/fileapi/drivefs_file_system_backend_delegate.h",
+    "../ash/file_system_provider/fileapi/backend_delegate.cc",
+    "../ash/file_system_provider/fileapi/backend_delegate.h",
+    "../ash/file_system_provider/fileapi/buffering_file_stream_reader.cc",
+    "../ash/file_system_provider/fileapi/buffering_file_stream_reader.h",
+    "../ash/file_system_provider/fileapi/buffering_file_stream_writer.cc",
+    "../ash/file_system_provider/fileapi/buffering_file_stream_writer.h",
+    "../ash/file_system_provider/fileapi/file_stream_reader.cc",
+    "../ash/file_system_provider/fileapi/file_stream_reader.h",
+    "../ash/file_system_provider/fileapi/file_stream_writer.cc",
+    "../ash/file_system_provider/fileapi/file_stream_writer.h",
+    "../ash/file_system_provider/fileapi/provider_async_file_util.cc",
+    "../ash/file_system_provider/fileapi/provider_async_file_util.h",
+    "../ash/file_system_provider/fileapi/watcher_manager.cc",
+    "../ash/file_system_provider/fileapi/watcher_manager.h",
+    "../ash/file_system_provider/operations/abort.cc",
+    "../ash/file_system_provider/operations/abort.h",
+    "../ash/file_system_provider/operations/add_watcher.cc",
+    "../ash/file_system_provider/operations/add_watcher.h",
+    "../ash/file_system_provider/operations/close_file.cc",
+    "../ash/file_system_provider/operations/close_file.h",
+    "../ash/file_system_provider/operations/configure.cc",
+    "../ash/file_system_provider/operations/configure.h",
+    "../ash/file_system_provider/operations/copy_entry.cc",
+    "../ash/file_system_provider/operations/copy_entry.h",
+    "../ash/file_system_provider/operations/create_directory.cc",
+    "../ash/file_system_provider/operations/create_directory.h",
+    "../ash/file_system_provider/operations/create_file.cc",
+    "../ash/file_system_provider/operations/create_file.h",
+    "../ash/file_system_provider/operations/delete_entry.cc",
+    "../ash/file_system_provider/operations/delete_entry.h",
+    "../ash/file_system_provider/operations/execute_action.cc",
+    "../ash/file_system_provider/operations/execute_action.h",
+    "../ash/file_system_provider/operations/get_actions.cc",
+    "../ash/file_system_provider/operations/get_actions.h",
+    "../ash/file_system_provider/operations/get_metadata.cc",
+    "../ash/file_system_provider/operations/get_metadata.h",
+    "../ash/file_system_provider/operations/move_entry.cc",
+    "../ash/file_system_provider/operations/move_entry.h",
+    "../ash/file_system_provider/operations/open_file.cc",
+    "../ash/file_system_provider/operations/open_file.h",
+    "../ash/file_system_provider/operations/operation.cc",
+    "../ash/file_system_provider/operations/operation.h",
+    "../ash/file_system_provider/operations/read_directory.cc",
+    "../ash/file_system_provider/operations/read_directory.h",
+    "../ash/file_system_provider/operations/read_file.cc",
+    "../ash/file_system_provider/operations/read_file.h",
+    "../ash/file_system_provider/operations/remove_watcher.cc",
+    "../ash/file_system_provider/operations/remove_watcher.h",
+    "../ash/file_system_provider/operations/truncate.cc",
+    "../ash/file_system_provider/operations/truncate.h",
+    "../ash/file_system_provider/operations/unmount.cc",
+    "../ash/file_system_provider/operations/unmount.h",
+    "../ash/file_system_provider/operations/write_file.cc",
+    "../ash/file_system_provider/operations/write_file.h",
     "../ash/guest_os/guest_os_diagnostics_builder.cc",
     "../ash/guest_os/guest_os_diagnostics_builder.h",
     "../ash/guest_os/guest_os_external_protocol_handler.cc",
@@ -2158,20 +2212,6 @@
     "file_system_provider/abort_callback.h",
     "file_system_provider/extension_provider.cc",
     "file_system_provider/extension_provider.h",
-    "file_system_provider/fileapi/backend_delegate.cc",
-    "file_system_provider/fileapi/backend_delegate.h",
-    "file_system_provider/fileapi/buffering_file_stream_reader.cc",
-    "file_system_provider/fileapi/buffering_file_stream_reader.h",
-    "file_system_provider/fileapi/buffering_file_stream_writer.cc",
-    "file_system_provider/fileapi/buffering_file_stream_writer.h",
-    "file_system_provider/fileapi/file_stream_reader.cc",
-    "file_system_provider/fileapi/file_stream_reader.h",
-    "file_system_provider/fileapi/file_stream_writer.cc",
-    "file_system_provider/fileapi/file_stream_writer.h",
-    "file_system_provider/fileapi/provider_async_file_util.cc",
-    "file_system_provider/fileapi/provider_async_file_util.h",
-    "file_system_provider/fileapi/watcher_manager.cc",
-    "file_system_provider/fileapi/watcher_manager.h",
     "file_system_provider/icon_set.cc",
     "file_system_provider/icon_set.h",
     "file_system_provider/mount_path_util.cc",
@@ -2180,46 +2220,6 @@
     "file_system_provider/notification_manager.h",
     "file_system_provider/notification_manager_interface.h",
     "file_system_provider/observer.h",
-    "file_system_provider/operations/abort.cc",
-    "file_system_provider/operations/abort.h",
-    "file_system_provider/operations/add_watcher.cc",
-    "file_system_provider/operations/add_watcher.h",
-    "file_system_provider/operations/close_file.cc",
-    "file_system_provider/operations/close_file.h",
-    "file_system_provider/operations/configure.cc",
-    "file_system_provider/operations/configure.h",
-    "file_system_provider/operations/copy_entry.cc",
-    "file_system_provider/operations/copy_entry.h",
-    "file_system_provider/operations/create_directory.cc",
-    "file_system_provider/operations/create_directory.h",
-    "file_system_provider/operations/create_file.cc",
-    "file_system_provider/operations/create_file.h",
-    "file_system_provider/operations/delete_entry.cc",
-    "file_system_provider/operations/delete_entry.h",
-    "file_system_provider/operations/execute_action.cc",
-    "file_system_provider/operations/execute_action.h",
-    "file_system_provider/operations/get_actions.cc",
-    "file_system_provider/operations/get_actions.h",
-    "file_system_provider/operations/get_metadata.cc",
-    "file_system_provider/operations/get_metadata.h",
-    "file_system_provider/operations/move_entry.cc",
-    "file_system_provider/operations/move_entry.h",
-    "file_system_provider/operations/open_file.cc",
-    "file_system_provider/operations/open_file.h",
-    "file_system_provider/operations/operation.cc",
-    "file_system_provider/operations/operation.h",
-    "file_system_provider/operations/read_directory.cc",
-    "file_system_provider/operations/read_directory.h",
-    "file_system_provider/operations/read_file.cc",
-    "file_system_provider/operations/read_file.h",
-    "file_system_provider/operations/remove_watcher.cc",
-    "file_system_provider/operations/remove_watcher.h",
-    "file_system_provider/operations/truncate.cc",
-    "file_system_provider/operations/truncate.h",
-    "file_system_provider/operations/unmount.cc",
-    "file_system_provider/operations/unmount.h",
-    "file_system_provider/operations/write_file.cc",
-    "file_system_provider/operations/write_file.h",
     "file_system_provider/provided_file_system.cc",
     "file_system_provider/provided_file_system.h",
     "file_system_provider/provided_file_system_info.cc",
@@ -3767,6 +3767,32 @@
     "../ash/drive/drive_integration_service_unittest.cc",
     "../ash/drive/drivefs_native_message_host_unittest.cc",
     "../ash/drive/file_system_util_unittest.cc",
+    "../ash/file_system_provider/fileapi/buffering_file_stream_reader_unittest.cc",
+    "../ash/file_system_provider/fileapi/buffering_file_stream_writer_unittest.cc",
+    "../ash/file_system_provider/fileapi/file_stream_reader_unittest.cc",
+    "../ash/file_system_provider/fileapi/file_stream_writer_unittest.cc",
+    "../ash/file_system_provider/fileapi/provider_async_file_util_unittest.cc",
+    "../ash/file_system_provider/operations/abort_unittest.cc",
+    "../ash/file_system_provider/operations/add_watcher_unittest.cc",
+    "../ash/file_system_provider/operations/close_file_unittest.cc",
+    "../ash/file_system_provider/operations/configure_unittest.cc",
+    "../ash/file_system_provider/operations/copy_entry_unittest.cc",
+    "../ash/file_system_provider/operations/create_directory_unittest.cc",
+    "../ash/file_system_provider/operations/create_file_unittest.cc",
+    "../ash/file_system_provider/operations/delete_entry_unittest.cc",
+    "../ash/file_system_provider/operations/execute_action_unittest.cc",
+    "../ash/file_system_provider/operations/get_actions_unittest.cc",
+    "../ash/file_system_provider/operations/get_metadata_unittest.cc",
+    "../ash/file_system_provider/operations/move_entry_unittest.cc",
+    "../ash/file_system_provider/operations/open_file_unittest.cc",
+    "../ash/file_system_provider/operations/read_directory_unittest.cc",
+    "../ash/file_system_provider/operations/read_file_unittest.cc",
+    "../ash/file_system_provider/operations/remove_watcher_unittest.cc",
+    "../ash/file_system_provider/operations/test_util.cc",
+    "../ash/file_system_provider/operations/test_util.h",
+    "../ash/file_system_provider/operations/truncate_unittest.cc",
+    "../ash/file_system_provider/operations/unmount_unittest.cc",
+    "../ash/file_system_provider/operations/write_file_unittest.cc",
     "../ash/guest_os/guest_os_external_protocol_handler_unittest.cc",
     "../ash/guest_os/guest_os_registry_service_unittest.cc",
     "../ash/guest_os/guest_os_share_path_unittest.cc",
@@ -3971,35 +3997,9 @@
     "file_system_provider/fake_provided_file_system.h",
     "file_system_provider/fake_registry.cc",
     "file_system_provider/fake_registry.h",
-    "file_system_provider/fileapi/buffering_file_stream_reader_unittest.cc",
-    "file_system_provider/fileapi/buffering_file_stream_writer_unittest.cc",
-    "file_system_provider/fileapi/file_stream_reader_unittest.cc",
-    "file_system_provider/fileapi/file_stream_writer_unittest.cc",
-    "file_system_provider/fileapi/provider_async_file_util_unittest.cc",
     "file_system_provider/logging_observer.cc",
     "file_system_provider/logging_observer.h",
     "file_system_provider/mount_path_util_unittest.cc",
-    "file_system_provider/operations/abort_unittest.cc",
-    "file_system_provider/operations/add_watcher_unittest.cc",
-    "file_system_provider/operations/close_file_unittest.cc",
-    "file_system_provider/operations/configure_unittest.cc",
-    "file_system_provider/operations/copy_entry_unittest.cc",
-    "file_system_provider/operations/create_directory_unittest.cc",
-    "file_system_provider/operations/create_file_unittest.cc",
-    "file_system_provider/operations/delete_entry_unittest.cc",
-    "file_system_provider/operations/execute_action_unittest.cc",
-    "file_system_provider/operations/get_actions_unittest.cc",
-    "file_system_provider/operations/get_metadata_unittest.cc",
-    "file_system_provider/operations/move_entry_unittest.cc",
-    "file_system_provider/operations/open_file_unittest.cc",
-    "file_system_provider/operations/read_directory_unittest.cc",
-    "file_system_provider/operations/read_file_unittest.cc",
-    "file_system_provider/operations/remove_watcher_unittest.cc",
-    "file_system_provider/operations/test_util.cc",
-    "file_system_provider/operations/test_util.h",
-    "file_system_provider/operations/truncate_unittest.cc",
-    "file_system_provider/operations/unmount_unittest.cc",
-    "file_system_provider/operations/write_file_unittest.cc",
     "file_system_provider/provided_file_system_unittest.cc",
     "file_system_provider/queue_unittest.cc",
     "file_system_provider/registry_unittest.cc",
diff --git a/chrome/browser/chromeos/file_manager/file_manager_browsertest_base.cc b/chrome/browser/chromeos/file_manager/file_manager_browsertest_base.cc
index c64bc79..2ec0653 100644
--- a/chrome/browser/chromeos/file_manager/file_manager_browsertest_base.cc
+++ b/chrome/browser/chromeos/file_manager/file_manager_browsertest_base.cc
@@ -1578,7 +1578,7 @@
 
   if (ShouldInspect(host)) {
     devtools_agent_[host] =
-        std::make_unique<DevToolsListener>(host, process_id_);
+        std::make_unique<coverage::DevToolsListener>(host, process_id_);
   }
 }
 
@@ -1600,7 +1600,7 @@
     return;
 
   if (ShouldInspect(host)) {
-    LOG(INFO) << DevToolsListener::HostString(host, __FUNCTION__);
+    LOG(INFO) << coverage::DevToolsListener::HostString(host, __FUNCTION__);
     devtools_agent_.find(host)->second->Navigated(host);
   } else {
     devtools_agent_.find(host)->second->Detach(host);
@@ -1942,7 +1942,7 @@
 
   base::FilePath store =
       devtools_code_coverage_dir_.AppendASCII("devtools_code_coverage");
-  DevToolsListener::SetupCoverageStore(store);
+  coverage::DevToolsListener::SetupCoverageStore(store);
 
   for (auto& agent : devtools_agent_) {
     auto* host = agent.first;
diff --git a/chrome/browser/chromeos/file_manager/file_manager_browsertest_base.h b/chrome/browser/chromeos/file_manager/file_manager_browsertest_base.h
index a6b984e..8a1fd78 100644
--- a/chrome/browser/chromeos/file_manager/file_manager_browsertest_base.h
+++ b/chrome/browser/chromeos/file_manager/file_manager_browsertest_base.h
@@ -15,10 +15,10 @@
 #include "base/test/scoped_feature_list.h"
 #include "chrome/browser/ash/drive/drive_integration_service.h"
 #include "chrome/browser/chromeos/crostini/fake_crostini_features.h"
-#include "chrome/browser/chromeos/file_manager/devtools_listener.h"
 #include "chrome/browser/extensions/extension_apitest.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/web_applications/components/web_app_id.h"
+#include "chrome/test/base/devtools_listener.h"
 #include "content/public/browser/devtools_agent_host_observer.h"
 
 class NotificationDisplayServiceTester;
@@ -221,9 +221,11 @@
   base::HistogramTester histograms_;
   base::UserActionTester user_actions_;
 
+  using DevToolsAgentMap =
+      std::map<content::DevToolsAgentHost*,
+               std::unique_ptr<coverage::DevToolsListener>>;
   base::FilePath devtools_code_coverage_dir_;
-  std::map<content::DevToolsAgentHost*, std::unique_ptr<DevToolsListener>>
-      devtools_agent_;
+  DevToolsAgentMap devtools_agent_;
   uint32_t process_id_ = 0;
 
   DISALLOW_COPY_AND_ASSIGN(FileManagerBrowserTestBase);
diff --git a/chrome/browser/chromeos/file_system_provider/OWNERS b/chrome/browser/chromeos/file_system_provider/OWNERS
index 4dce310..944af1c 100644
--- a/chrome/browser/chromeos/file_system_provider/OWNERS
+++ b/chrome/browser/chromeos/file_system_provider/OWNERS
@@ -1 +1,3 @@
-yamaguchi@chromium.org
+# TODO(https://crbug.com/1164001): Share OWNERS until the migration of
+# //chrome/browser/chromeos/file_system_provider completes.
+file://chrome/browser/ash/file_system_provider/OWNERS
diff --git a/chrome/browser/chromeos/file_system_provider/provided_file_system.cc b/chrome/browser/chromeos/file_system_provider/provided_file_system.cc
index 2e6d84d7..79287d01 100644
--- a/chrome/browser/chromeos/file_system_provider/provided_file_system.cc
+++ b/chrome/browser/chromeos/file_system_provider/provided_file_system.cc
@@ -14,26 +14,26 @@
 #include "base/macros.h"
 #include "base/memory/ptr_util.h"
 #include "base/trace_event/trace_event.h"
+#include "chrome/browser/ash/file_system_provider/operations/abort.h"
+#include "chrome/browser/ash/file_system_provider/operations/add_watcher.h"
+#include "chrome/browser/ash/file_system_provider/operations/close_file.h"
+#include "chrome/browser/ash/file_system_provider/operations/configure.h"
+#include "chrome/browser/ash/file_system_provider/operations/copy_entry.h"
+#include "chrome/browser/ash/file_system_provider/operations/create_directory.h"
+#include "chrome/browser/ash/file_system_provider/operations/create_file.h"
+#include "chrome/browser/ash/file_system_provider/operations/delete_entry.h"
+#include "chrome/browser/ash/file_system_provider/operations/execute_action.h"
+#include "chrome/browser/ash/file_system_provider/operations/get_actions.h"
+#include "chrome/browser/ash/file_system_provider/operations/get_metadata.h"
+#include "chrome/browser/ash/file_system_provider/operations/move_entry.h"
+#include "chrome/browser/ash/file_system_provider/operations/open_file.h"
+#include "chrome/browser/ash/file_system_provider/operations/read_directory.h"
+#include "chrome/browser/ash/file_system_provider/operations/read_file.h"
+#include "chrome/browser/ash/file_system_provider/operations/remove_watcher.h"
+#include "chrome/browser/ash/file_system_provider/operations/truncate.h"
+#include "chrome/browser/ash/file_system_provider/operations/unmount.h"
+#include "chrome/browser/ash/file_system_provider/operations/write_file.h"
 #include "chrome/browser/chromeos/file_system_provider/notification_manager.h"
-#include "chrome/browser/chromeos/file_system_provider/operations/abort.h"
-#include "chrome/browser/chromeos/file_system_provider/operations/add_watcher.h"
-#include "chrome/browser/chromeos/file_system_provider/operations/close_file.h"
-#include "chrome/browser/chromeos/file_system_provider/operations/configure.h"
-#include "chrome/browser/chromeos/file_system_provider/operations/copy_entry.h"
-#include "chrome/browser/chromeos/file_system_provider/operations/create_directory.h"
-#include "chrome/browser/chromeos/file_system_provider/operations/create_file.h"
-#include "chrome/browser/chromeos/file_system_provider/operations/delete_entry.h"
-#include "chrome/browser/chromeos/file_system_provider/operations/execute_action.h"
-#include "chrome/browser/chromeos/file_system_provider/operations/get_actions.h"
-#include "chrome/browser/chromeos/file_system_provider/operations/get_metadata.h"
-#include "chrome/browser/chromeos/file_system_provider/operations/move_entry.h"
-#include "chrome/browser/chromeos/file_system_provider/operations/open_file.h"
-#include "chrome/browser/chromeos/file_system_provider/operations/read_directory.h"
-#include "chrome/browser/chromeos/file_system_provider/operations/read_file.h"
-#include "chrome/browser/chromeos/file_system_provider/operations/remove_watcher.h"
-#include "chrome/browser/chromeos/file_system_provider/operations/truncate.h"
-#include "chrome/browser/chromeos/file_system_provider/operations/unmount.h"
-#include "chrome/browser/chromeos/file_system_provider/operations/write_file.h"
 #include "chrome/browser/chromeos/file_system_provider/request_manager.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/common/extensions/api/file_system_provider.h"
diff --git a/chrome/browser/chromeos/file_system_provider/registry.cc b/chrome/browser/chromeos/file_system_provider/registry.cc
index 907b6b8..bf5a6f5c 100644
--- a/chrome/browser/chromeos/file_system_provider/registry.cc
+++ b/chrome/browser/chromeos/file_system_provider/registry.cc
@@ -120,7 +120,7 @@
     return;  // Nothing to forget.
 
   file_systems_per_extension->RemoveKey(file_system_id);
-  if (file_systems_per_extension->empty())
+  if (file_systems_per_extension->DictEmpty())
     dict_update->Remove(provider_id.ToString(), NULL);
 }
 
@@ -226,7 +226,7 @@
             base::FilePath::FromUTF8Unsafe(entry_path);
         restored_watcher.recursive = recursive;
         restored_watcher.last_tag = last_tag;
-        for (const auto& persistent_origin : *persistent_origins) {
+        for (const auto& persistent_origin : persistent_origins->GetList()) {
           std::string origin;
           if (persistent_origin.GetAsString(&origin)) {
             LOG(ERROR) << "Malformed subscriber information in preferences.";
diff --git a/chrome/browser/chromeos/full_restore/full_restore_service.cc b/chrome/browser/chromeos/full_restore/full_restore_service.cc
index cdad256..d5a8ec0b 100644
--- a/chrome/browser/chromeos/full_restore/full_restore_service.cc
+++ b/chrome/browser/chromeos/full_restore/full_restore_service.cc
@@ -179,7 +179,7 @@
   if (notification_->id() == kSetRestorePrefNotificationId) {
     // Show the 'On Startup' OS setting page.
     chrome::SettingsWindowManager::GetInstance()->ShowOSSettings(
-        profile_, chromeos::settings::mojom::kOnStartupSectionPath);
+        profile_, chromeos::settings::mojom::kOnStartupSubpagePath);
     return;
   }
 
diff --git a/chrome/browser/chromeos/input_method/native_input_method_engine.cc b/chrome/browser/chromeos/input_method/native_input_method_engine.cc
index fbcdd8e..e23f135 100644
--- a/chrome/browser/chromeos/input_method/native_input_method_engine.cc
+++ b/chrome/browser/chromeos/input_method/native_input_method_engine.cc
@@ -45,7 +45,8 @@
   // and the physical keyboard, only run the native code path if the virtual
   // keyboard is disabled. Otherwise, just let the extension handle any physical
   // key events.
-  return base::FeatureList::IsEnabled(
+  return base::FeatureList::IsEnabled(chromeos::features::kImeMojoDecoder) &&
+         base::FeatureList::IsEnabled(
              chromeos::features::kSystemLatinPhysicalTyping) &&
          base::StartsWith(engine_id, "xkb:", base::CompareCase::SENSITIVE) &&
          !ChromeKeyboardControllerClient::Get()->GetKeyboardEnabled();
diff --git a/chrome/browser/chromeos/policy/status_collector/app_info_generator_unittest.cc b/chrome/browser/chromeos/policy/status_collector/app_info_generator_unittest.cc
index 2e1d5e5..d96b2500 100644
--- a/chrome/browser/chromeos/policy/status_collector/app_info_generator_unittest.cc
+++ b/chrome/browser/chromeos/policy/status_collector/app_info_generator_unittest.cc
@@ -147,7 +147,8 @@
     explicit Instance(const std::string& app_id) {
       window_ = std::make_unique<aura::Window>(nullptr);
       window_->Init(ui::LAYER_NOT_DRAWN);
-      instance_ = std::make_unique<apps::Instance>(app_id, window_.get());
+      instance_ = std::make_unique<apps::Instance>(
+          app_id, std::make_unique<apps::Instance::InstanceKey>(window_.get()));
     }
 
     apps::Instance* instance() const { return instance_.get(); }
diff --git a/chrome/browser/chromeos/policy/status_collector/device_status_collector_browsertest.cc b/chrome/browser/chromeos/policy/status_collector/device_status_collector_browsertest.cc
index 34ccafc..9ecc248 100644
--- a/chrome/browser/chromeos/policy/status_collector/device_status_collector_browsertest.cc
+++ b/chrome/browser/chromeos/policy/status_collector/device_status_collector_browsertest.cc
@@ -3615,7 +3615,8 @@
   // Env::CreateInstance must be called for test window.
   auto env = aura::Env::CreateInstance();
   aura::Window* window = aura::test::CreateTestWindowWithId(/*id=*/0, nullptr);
-  auto instance = std::make_unique<apps::Instance>("id", window);
+  auto instance = std::make_unique<apps::Instance>(
+      "id", std::make_unique<apps::Instance::InstanceKey>(window));
   instance->UpdateState(apps::InstanceState::kStarted, start_time);
   std::vector<std::unique_ptr<apps::Instance>> deltas;
   deltas.push_back(std::move(instance));
diff --git a/chrome/browser/contextmenu/java/src/org/chromium/chrome/browser/contextmenu/ChipDelegate.java b/chrome/browser/contextmenu/java/src/org/chromium/chrome/browser/contextmenu/ChipDelegate.java
index 38b2bef..1827866e 100644
--- a/chrome/browser/contextmenu/java/src/org/chromium/chrome/browser/contextmenu/ChipDelegate.java
+++ b/chrome/browser/contextmenu/java/src/org/chromium/chrome/browser/contextmenu/ChipDelegate.java
@@ -11,6 +11,12 @@
  */
 public interface ChipDelegate {
     /**
+     * Determines whether the chip delegate is able to support a chip in the chosen context.
+     * @return Whether the chip can be supported and rendered.
+     */
+    boolean isChipSupported();
+
+    /**
      * Retrieves the data for displaying a chip below the context menu.
      * @param callback The callback will always be called with the retrieved ChipRenderParams.
      *                 The ChipRenderParams will be null in the event there is no chip to show.
diff --git a/chrome/browser/dom_distiller/tab_utils.cc b/chrome/browser/dom_distiller/tab_utils.cc
index c0546a9b..c91e3517 100644
--- a/chrome/browser/dom_distiller/tab_utils.cc
+++ b/chrome/browser/dom_distiller/tab_utils.cc
@@ -165,6 +165,12 @@
   new_web_contents->GetController().CopyStateFrom(
       &old_web_contents->GetController(), /* needs_reload */ true);
 
+#if !defined(OS_ANDROID)
+  // Use the old_web_contents to log time on the distillable page before
+  // navigating away from these contents.
+  dom_distiller::UMAHelper::LogTimeOnDistillablePage(old_web_contents);
+#endif
+
   // StartNavigationToDistillerViewer must come before swapping the tab contents
   // to avoid triggering a reload of the page.  This reloadmakes it very
   // difficult to distinguish between the intermediate reload and a user hitting
@@ -180,10 +186,6 @@
       new SourcePageHandleWebContents(old_web_contents_owned.release(), true));
 
   MaybeStartDistillation(std::move(source_page_handle));
-
-#if !defined(OS_ANDROID)
-  dom_distiller::UMAHelper::LogTimeOnDistillablePage(old_web_contents);
-#endif
 }
 
 void DistillCurrentPage(content::WebContents* source_web_contents) {
diff --git a/chrome/browser/download/android/BUILD.gn b/chrome/browser/download/android/BUILD.gn
index 0bd9631..0ffe43a1 100644
--- a/chrome/browser/download/android/BUILD.gn
+++ b/chrome/browser/download/android/BUILD.gn
@@ -50,6 +50,8 @@
     "java/src/org/chromium/chrome/browser/download/dialogs/DownloadLocationCustomView.java",
     "java/src/org/chromium/chrome/browser/download/dialogs/DownloadLocationDialogController.java",
     "java/src/org/chromium/chrome/browser/download/dialogs/DownloadLocationDialogCoordinator.java",
+    "java/src/org/chromium/chrome/browser/download/dialogs/DownloadLocationDialogProperties.java",
+    "java/src/org/chromium/chrome/browser/download/dialogs/DownloadLocationDialogViewBinder.java",
     "java/src/org/chromium/chrome/browser/download/dialogs/DownloadTimePickerDialog.java",
     "java/src/org/chromium/chrome/browser/download/home/DownloadManagerCoordinator.java",
     "java/src/org/chromium/chrome/browser/download/home/DownloadManagerUiConfig.java",
diff --git a/chrome/browser/download/android/java/src/org/chromium/chrome/browser/download/dialogs/DownloadLocationCustomView.java b/chrome/browser/download/android/java/src/org/chromium/chrome/browser/download/dialogs/DownloadLocationCustomView.java
index 4a968c6..ff13fd4 100644
--- a/chrome/browser/download/android/java/src/org/chromium/chrome/browser/download/dialogs/DownloadLocationCustomView.java
+++ b/chrome/browser/download/android/java/src/org/chromium/chrome/browser/download/dialogs/DownloadLocationCustomView.java
@@ -31,11 +31,8 @@
 import org.chromium.chrome.browser.download.StringUtils;
 import org.chromium.chrome.browser.download.settings.DownloadDirectoryAdapter;
 import org.chromium.chrome.browser.flags.ChromeFeatureList;
-import org.chromium.components.browser_ui.util.DownloadUtils;
 import org.chromium.components.browser_ui.widget.text.AlertDialogEditText;
 
-import java.io.File;
-
 /**
  * Dialog that is displayed to ask user where they want to download the file.
  */
@@ -71,58 +68,43 @@
         mDontShowAgain = findViewById(R.id.show_again_checkbox);
     }
 
-    void initialize(@DownloadLocationDialogType int dialogType, File suggestedPath, long totalBytes,
-            CharSequence title) {
+    void initialize(@DownloadLocationDialogType int dialogType, long totalBytes) {
+        // TODO(xingliu): Remove this function, currently used by smart suggestion.
         mDialogType = dialogType;
-
-        // Automatically check "don't show again" the first time the user is seeing the dialog.
-        boolean isInitial = DownloadDialogBridge.getPromptForDownloadAndroid()
-                == DownloadPromptStatus.SHOW_INITIAL;
-        mDontShowAgain.setChecked(isInitial);
-        mDontShowAgain.setOnCheckedChangeListener(this);
-
-        mFileName.setText(suggestedPath.getName());
-        mTitle.setText(title);
         mTotalBytes = totalBytes;
-
-        switch (dialogType) {
-            case DownloadLocationDialogType.DEFAULT:
-                // Show a file size subtitle if file size is available.
-                if (totalBytes > 0) {
-                    mSubtitleView.setText(
-                            DownloadUtils.getStringForBytes(getContext(), totalBytes));
-                } else {
-                    hideSubtitle();
-                }
-                break;
-
-            case DownloadLocationDialogType.LOCATION_FULL:
-                mSubtitleView.setText(R.string.download_location_download_to_default_folder);
-                break;
-
-            case DownloadLocationDialogType.LOCATION_NOT_FOUND:
-                mSubtitleView.setText(R.string.download_location_download_to_default_folder);
-                break;
-
-            case DownloadLocationDialogType.NAME_CONFLICT:
-                mSubtitleView.setText(R.string.download_location_name_exists);
-                break;
-
-            case DownloadLocationDialogType.NAME_TOO_LONG:
-                mSubtitleView.setText(R.string.download_location_name_too_long);
-                break;
-
-            case DownloadLocationDialogType.LOCATION_SUGGESTION:
-                // Show the location available space underneath the spinner.
-                mLocationAvailableSpace.setVisibility(View.VISIBLE);
-                setFileSize(totalBytes);
-                hideSubtitle();
-                break;
-        }
-
         mDirectoryAdapter.update();
     }
 
+    void setTitle(CharSequence title) {
+        mTitle.setText(title);
+    }
+
+    void setSubtitle(CharSequence subtitle) {
+        mSubtitleView.setText(subtitle);
+    }
+
+    void setFileName(CharSequence fileName) {
+        mFileName.setText(fileName);
+    }
+
+    void setFileSize(CharSequence fileSize) {
+        mFileSize.setVisibility(View.VISIBLE);
+        mFileSize.setText(fileSize);
+    }
+
+    void setDontShowAgainCheckbox(boolean checked) {
+        mDontShowAgain.setChecked(checked);
+        mDontShowAgain.setOnCheckedChangeListener(this);
+    }
+
+    void showDontShowAgainCheckbox(boolean show) {
+        mDontShowAgain.setVisibility(show ? VISIBLE : GONE);
+    }
+
+    void showLocationAvailableSpace(boolean show) {
+        mLocationAvailableSpace.setVisibility(show ? VISIBLE : GONE);
+    }
+
     // CompoundButton.OnCheckedChangeListener implementation.
     @Override
     public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
@@ -160,21 +142,14 @@
     /**
      * Hide the subtitle and adjust the bottom margin.
      */
-    private void hideSubtitle() {
-        mSubtitleView.setVisibility(View.GONE);
-        MarginLayoutParams titleMargin = (MarginLayoutParams) mTitle.getLayoutParams();
-        titleMargin.bottomMargin = getResources().getDimensionPixelSize(
-                R.dimen.download_dialog_subtitle_margin_bottom);
-        mTitle.setLayoutParams(titleMargin);
-    }
+    void showSubtitle(boolean show) {
+        mSubtitleView.setVisibility(show ? View.VISIBLE : View.GONE);
 
-    /**
-     * Show the file size below the file name.
-     * @param  totalBytes The total bytes of the download.
-     */
-    private void setFileSize(long totalBytes) {
-        mFileSize.setVisibility(View.VISIBLE);
-        mFileSize.setText(DownloadUtils.getStringForBytes(getContext(), totalBytes));
+        MarginLayoutParams titleMargin = (MarginLayoutParams) mTitle.getLayoutParams();
+        titleMargin.bottomMargin = getResources().getDimensionPixelSize(show
+                        ? R.dimen.download_dialog_title_margin_bottom
+                        : R.dimen.download_dialog_subtitle_margin_bottom);
+        mTitle.setLayoutParams(titleMargin);
     }
 
     /**
@@ -208,6 +183,7 @@
     // DownloadDirectoryAdapter.Delegate implementation.
     @Override
     public void onDirectoryOptionsUpdated() {
+        // TODO(xingliu): Move this to other places. UI shouldn't interact with the adapter.
         int selectedItemId = mDirectoryAdapter.getSelectedItemId();
         if (selectedItemId == NO_SELECTED_ITEM_ID
                 || mDialogType == DownloadLocationDialogType.LOCATION_FULL
diff --git a/chrome/browser/download/android/java/src/org/chromium/chrome/browser/download/dialogs/DownloadLocationDialogCoordinator.java b/chrome/browser/download/android/java/src/org/chromium/chrome/browser/download/dialogs/DownloadLocationDialogCoordinator.java
index f292edd5..859f8fa 100644
--- a/chrome/browser/download/android/java/src/org/chromium/chrome/browser/download/dialogs/DownloadLocationDialogCoordinator.java
+++ b/chrome/browser/download/android/java/src/org/chromium/chrome/browser/download/dialogs/DownloadLocationDialogCoordinator.java
@@ -18,10 +18,13 @@
 import org.chromium.chrome.browser.download.DownloadLocationDialogType;
 import org.chromium.chrome.browser.download.DownloadPromptStatus;
 import org.chromium.chrome.browser.download.R;
+import org.chromium.components.browser_ui.util.DownloadUtils;
 import org.chromium.ui.modaldialog.DialogDismissalCause;
 import org.chromium.ui.modaldialog.ModalDialogManager;
 import org.chromium.ui.modaldialog.ModalDialogProperties;
+import org.chromium.ui.modelutil.PropertyKey;
 import org.chromium.ui.modelutil.PropertyModel;
+import org.chromium.ui.modelutil.PropertyModelChangeProcessor;
 
 import java.io.File;
 import java.util.ArrayList;
@@ -30,11 +33,13 @@
  * The factory class that contains all dependencies for the download location dialog.
  * Also provides the public functionalties to interact with dialog.
  */
-// TODO(xingliu): Refactor download location dialog to fully use clank MVC.
 public class DownloadLocationDialogCoordinator implements ModalDialogProperties.Controller {
     @NonNull
     private DownloadLocationDialogController mController;
     private PropertyModel mDialogModel;
+    private PropertyModel mDownloadLocationDialogModel;
+    private PropertyModelChangeProcessor<PropertyModel, DownloadLocationCustomView, PropertyKey>
+            mPropertyModelChangeProcessor;
     private DownloadLocationCustomView mCustomView;
     private ModalDialogManager mModalDialogManager;
     private long mTotalBytes;
@@ -83,6 +88,7 @@
             mModalDialogManager.dismissDialog(
                     mDialogModel, DialogDismissalCause.DISMISSED_BY_NATIVE);
         }
+        if (mPropertyModelChangeProcessor != null) mPropertyModelChangeProcessor.destroy();
     }
 
     @Override
@@ -137,10 +143,13 @@
         if (mDialogModel != null) return;
 
         // Actually show the dialog.
+        mDownloadLocationDialogModel = getLocationDialogModel();
         mCustomView = (DownloadLocationCustomView) LayoutInflater.from(mContext).inflate(
                 R.layout.download_location_dialog, null);
-        mCustomView.initialize(
-                mDialogType, new File(mSuggestedPath), mTotalBytes, getTitle(mDialogType));
+        mCustomView.initialize(mDialogType, mTotalBytes);
+        mPropertyModelChangeProcessor =
+                PropertyModelChangeProcessor.create(mDownloadLocationDialogModel, mCustomView,
+                        DownloadLocationDialogViewBinder::bind, true /*performInitialBind*/);
 
         Resources resources = mContext.getResources();
         mDialogModel = new PropertyModel.Builder(ModalDialogProperties.ALL_KEYS)
@@ -156,26 +165,65 @@
         mModalDialogManager.showDialog(mDialogModel, ModalDialogManager.ModalDialogType.APP);
     }
 
-    private String getTitle(@DownloadLocationDialogType int dialogType) {
-        switch (dialogType) {
+    private PropertyModel getLocationDialogModel() {
+        boolean isInitial = DownloadDialogBridge.getPromptForDownloadAndroid()
+                == DownloadPromptStatus.SHOW_INITIAL;
+
+        PropertyModel.Builder builder =
+                new PropertyModel.Builder(DownloadLocationDialogProperties.ALL_KEYS);
+        builder.with(DownloadLocationDialogProperties.DONT_SHOW_AGAIN_CHECKBOX_CHECKED, isInitial);
+        builder.with(
+                DownloadLocationDialogProperties.FILE_NAME, new File(mSuggestedPath).getName());
+        builder.with(DownloadLocationDialogProperties.SHOW_SUBTITLE, true);
+        builder.with(DownloadLocationDialogProperties.DONT_SHOW_AGAIN_CHECKBOX_SHOWN, true);
+
+        switch (mDialogType) {
             case DownloadLocationDialogType.LOCATION_FULL:
-                return mContext.getString(R.string.download_location_not_enough_space);
-
+                builder.with(DownloadLocationDialogProperties.TITLE,
+                        mContext.getString(R.string.download_location_not_enough_space));
+                builder.with(DownloadLocationDialogProperties.SUBTITLE,
+                        mContext.getString(R.string.download_location_download_to_default_folder));
+                break;
             case DownloadLocationDialogType.LOCATION_NOT_FOUND:
-                return mContext.getString(R.string.download_location_no_sd_card);
-
+                builder.with(DownloadLocationDialogProperties.TITLE,
+                        mContext.getString(R.string.download_location_no_sd_card));
+                builder.with(DownloadLocationDialogProperties.SUBTITLE,
+                        mContext.getString(R.string.download_location_download_to_default_folder));
+                break;
             case DownloadLocationDialogType.NAME_CONFLICT:
-                return mContext.getString(R.string.download_location_download_again);
-
+                builder.with(DownloadLocationDialogProperties.TITLE,
+                        mContext.getString(R.string.download_location_download_again));
+                builder.with(DownloadLocationDialogProperties.SUBTITLE,
+                        mContext.getString(R.string.download_location_name_exists));
+                break;
             case DownloadLocationDialogType.NAME_TOO_LONG:
-                return mContext.getString(R.string.download_location_rename_file);
-
-            case DownloadLocationDialogType.LOCATION_SUGGESTION: // Intentional fall through.
+                builder.with(DownloadLocationDialogProperties.TITLE,
+                        mContext.getString(R.string.download_location_rename_file));
+                builder.with(DownloadLocationDialogProperties.SUBTITLE,
+                        mContext.getString(R.string.download_location_name_too_long));
+                break;
+            case DownloadLocationDialogType.LOCATION_SUGGESTION:
+                builder.with(DownloadLocationDialogProperties.TITLE,
+                        mContext.getString(R.string.download_location_dialog_title));
+                builder.with(DownloadLocationDialogProperties.SHOW_LOCATION_AVAILABLE_SPACE, true);
+                assert mTotalBytes > 0;
+                builder.with(DownloadLocationDialogProperties.FILE_SIZE,
+                        DownloadUtils.getStringForBytes(mContext, mTotalBytes));
+                builder.with(DownloadLocationDialogProperties.SHOW_SUBTITLE, false);
+                break;
             case DownloadLocationDialogType.DEFAULT:
-                return mContext.getString(R.string.download_location_dialog_title);
+                builder.with(DownloadLocationDialogProperties.TITLE,
+                        mContext.getString(R.string.download_location_dialog_title));
+                if (mTotalBytes > 0) {
+                    builder.with(DownloadLocationDialogProperties.SUBTITLE,
+                            DownloadUtils.getStringForBytes(mContext, mTotalBytes));
+                } else {
+                    builder.with(DownloadLocationDialogProperties.SHOW_SUBTITLE, false);
+                }
+                break;
         }
-        assert false;
-        return null;
+
+        return builder.build();
     }
 
     /**
diff --git a/chrome/browser/download/android/java/src/org/chromium/chrome/browser/download/dialogs/DownloadLocationDialogProperties.java b/chrome/browser/download/android/java/src/org/chromium/chrome/browser/download/dialogs/DownloadLocationDialogProperties.java
new file mode 100644
index 0000000..97151832
--- /dev/null
+++ b/chrome/browser/download/android/java/src/org/chromium/chrome/browser/download/dialogs/DownloadLocationDialogProperties.java
@@ -0,0 +1,67 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.chrome.browser.download.dialogs;
+
+import org.chromium.ui.modelutil.PropertyKey;
+import org.chromium.ui.modelutil.PropertyModel;
+
+/**
+ * Properties used by the MVC model between {@link DownloadLocationDialogCoordinator} and
+ * {@link DownloadLocationCustomView}.
+ */
+class DownloadLocationDialogProperties {
+    /**
+     * The title text of the download location dialog.
+     */
+    static final PropertyModel.ReadableObjectPropertyKey<CharSequence> TITLE =
+            new PropertyModel.ReadableObjectPropertyKey<>();
+
+    /**
+     * The subtitle text of the download location dialog.
+     */
+    static final PropertyModel.ReadableObjectPropertyKey<CharSequence> SUBTITLE =
+            new PropertyModel.ReadableObjectPropertyKey<>();
+
+    /**
+     * Whether to show the subtitle in the download location dialog. Default to false.
+     */
+    static final PropertyModel.ReadableBooleanPropertyKey SHOW_SUBTITLE =
+            new PropertyModel.ReadableBooleanPropertyKey();
+
+    /**
+     * The file name shown in the download location dialog.
+     */
+    static final PropertyModel.ReadableObjectPropertyKey<CharSequence> FILE_NAME =
+            new PropertyModel.ReadableObjectPropertyKey<>();
+
+    /**
+     * The file size shown under the file name, currently only used by smart suggestion.
+     */
+    static final PropertyModel.ReadableObjectPropertyKey<CharSequence> FILE_SIZE =
+            new PropertyModel.ReadableObjectPropertyKey<>();
+
+    /**
+     * Whether to show location available space text, used by the smart suggestion. Default to
+     * false.
+     */
+    static final PropertyModel.ReadableBooleanPropertyKey SHOW_LOCATION_AVAILABLE_SPACE =
+            new PropertyModel.ReadableBooleanPropertyKey();
+
+    /**
+     * Whether the don't show again checkbox is checked. Default to false.
+     */
+    static final PropertyModel.ReadableBooleanPropertyKey DONT_SHOW_AGAIN_CHECKBOX_CHECKED =
+            new PropertyModel.ReadableBooleanPropertyKey();
+
+    /**
+     * Whether to show the don't show again checkbox. Default to false.
+     */
+    static final PropertyModel.ReadableBooleanPropertyKey DONT_SHOW_AGAIN_CHECKBOX_SHOWN =
+            new PropertyModel.ReadableBooleanPropertyKey();
+
+    static final PropertyKey[] ALL_KEYS = new PropertyKey[] {TITLE, SUBTITLE, SHOW_SUBTITLE,
+            FILE_NAME, FILE_SIZE, SHOW_LOCATION_AVAILABLE_SPACE, DONT_SHOW_AGAIN_CHECKBOX_CHECKED,
+            DONT_SHOW_AGAIN_CHECKBOX_SHOWN};
+}
diff --git a/chrome/browser/download/android/java/src/org/chromium/chrome/browser/download/dialogs/DownloadLocationDialogViewBinder.java b/chrome/browser/download/android/java/src/org/chromium/chrome/browser/download/dialogs/DownloadLocationDialogViewBinder.java
new file mode 100644
index 0000000..d7dcb5b7
--- /dev/null
+++ b/chrome/browser/download/android/java/src/org/chromium/chrome/browser/download/dialogs/DownloadLocationDialogViewBinder.java
@@ -0,0 +1,39 @@
+// 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.download.dialogs;
+
+import org.chromium.ui.modelutil.PropertyKey;
+import org.chromium.ui.modelutil.PropertyModel;
+
+/**
+ * The view binder that connects {@link DownloadLocationCustomView} and
+ * {@link DownloadLocationDialogCoordinator} which defines the UI properties.
+ */
+class DownloadLocationDialogViewBinder {
+    static void bind(
+            PropertyModel model, DownloadLocationCustomView view, PropertyKey propertyKey) {
+        if (propertyKey == DownloadLocationDialogProperties.TITLE) {
+            view.setTitle(model.get(DownloadLocationDialogProperties.TITLE));
+        } else if (propertyKey == DownloadLocationDialogProperties.SUBTITLE) {
+            view.setSubtitle(model.get(DownloadLocationDialogProperties.SUBTITLE));
+        } else if (propertyKey == DownloadLocationDialogProperties.SHOW_SUBTITLE) {
+            view.showSubtitle(model.get(DownloadLocationDialogProperties.SHOW_SUBTITLE));
+        } else if (propertyKey == DownloadLocationDialogProperties.FILE_NAME) {
+            view.setFileName(model.get(DownloadLocationDialogProperties.FILE_NAME));
+        } else if (propertyKey == DownloadLocationDialogProperties.FILE_SIZE) {
+            view.setFileSize(model.get(DownloadLocationDialogProperties.FILE_SIZE));
+        } else if (propertyKey == DownloadLocationDialogProperties.SHOW_LOCATION_AVAILABLE_SPACE) {
+            view.showLocationAvailableSpace(
+                    model.get(DownloadLocationDialogProperties.SHOW_LOCATION_AVAILABLE_SPACE));
+        } else if (propertyKey
+                == DownloadLocationDialogProperties.DONT_SHOW_AGAIN_CHECKBOX_CHECKED) {
+            view.setDontShowAgainCheckbox(
+                    model.get(DownloadLocationDialogProperties.DONT_SHOW_AGAIN_CHECKBOX_CHECKED));
+        } else if (propertyKey == DownloadLocationDialogProperties.DONT_SHOW_AGAIN_CHECKBOX_SHOWN) {
+            view.showDontShowAgainCheckbox(
+                    model.get(DownloadLocationDialogProperties.DONT_SHOW_AGAIN_CHECKBOX_SHOWN));
+        }
+    }
+}
diff --git a/chrome/browser/extensions/api/virtual_keyboard_private/chrome_virtual_keyboard_delegate.cc b/chrome/browser/extensions/api/virtual_keyboard_private/chrome_virtual_keyboard_delegate.cc
index 5771782..656033ba 100644
--- a/chrome/browser/extensions/api/virtual_keyboard_private/chrome_virtual_keyboard_delegate.cc
+++ b/chrome/browser/extensions/api/virtual_keyboard_private/chrome_virtual_keyboard_delegate.cc
@@ -534,15 +534,15 @@
           chromeos::features::kVirtualKeyboardFloatingDefault)));
 
   // Flag used to enable system built-in IME decoder instead of NaCl.
-  bool mojoDecoder =
+  bool mojo_decoder =
       base::FeatureList::IsEnabled(chromeos::features::kImeMojoDecoder);
-  features->AppendString(GenerateFeatureFlag("usemojodecoder", mojoDecoder));
+  features->AppendString(GenerateFeatureFlag("usemojodecoder", mojo_decoder));
   // Enabling MojoDecoder implies the 2 previous flags are auto-enabled.
   //   * fstinputlogic
   //   * hmminputlogic
   // TODO(b/171846787): Remove the 3 flags after they are removed from clients.
-  features->AppendString(GenerateFeatureFlag("fstinputlogic", mojoDecoder));
-  features->AppendString(GenerateFeatureFlag("hmminputlogic", mojoDecoder));
+  features->AppendString(GenerateFeatureFlag("fstinputlogic", mojo_decoder));
+  features->AppendString(GenerateFeatureFlag("hmminputlogic", mojo_decoder));
   features->AppendString(GenerateFeatureFlag(
       "imemozcproto",
       base::FeatureList::IsEnabled(chromeos::features::kImeMozcProto)));
@@ -553,10 +553,10 @@
   features->AppendString(GenerateFeatureFlag(
       "assistiveAutoCorrect",
       base::FeatureList::IsEnabled(chromeos::features::kAssistAutoCorrect)));
-  features->AppendString(
-      GenerateFeatureFlag("systemlatinphysicaltyping",
-                          base::FeatureList::IsEnabled(
-                              chromeos::features::kSystemLatinPhysicalTyping)));
+  features->AppendString(GenerateFeatureFlag(
+      "systemlatinphysicaltyping",
+      mojo_decoder && base::FeatureList::IsEnabled(
+                          chromeos::features::kSystemLatinPhysicalTyping)));
   features->AppendString(GenerateFeatureFlag(
       "multilingualtyping",
       base::FeatureList::IsEnabled(chromeos::features::kMultilingualTyping)));
diff --git a/chrome/browser/flag-metadata.json b/chrome/browser/flag-metadata.json
index 0a952c2..b4909c8c 100644
--- a/chrome/browser/flag-metadata.json
+++ b/chrome/browser/flag-metadata.json
@@ -3280,14 +3280,6 @@
     "expiry_milestone": 95
   },
   {
-    "name": "insert-key-toggle-mode",
-    "owners": [ "snianu@microsoft.com", "ericlaw@microsoft.com" ],
-    // This flag is temporary as we want to eventually remove the support for
-    // Overwrite mode when insert key is pressed. Currently it is disabled
-    // by-default.
-    "expiry_milestone": 91
-  },
-  {
     "name": "installable-ink-drop",
     "owners": [ "collinbaker", "pbos" ],
     "expiry_milestone": 90
@@ -4572,7 +4564,7 @@
   {
     "name": "restrict-gamepad-access",
     "owners": [ "//device/gamepad/OWNERS", "jameshollyer@chromium.org" ],
-    "expiry_milestone": 89
+    "expiry_milestone": 96
   },
   {
     "name": "run-video-capture-service-in-browser",
diff --git a/chrome/browser/flag_descriptions.cc b/chrome/browser/flag_descriptions.cc
index c6de838..489a2a79 100644
--- a/chrome/browser/flag_descriptions.cc
+++ b/chrome/browser/flag_descriptions.cc
@@ -1453,11 +1453,6 @@
 const char kInProductHelpDemoModeChoiceDescription[] =
     "Selects the In-Product Help demo mode.";
 
-const char kInsertKeyToggleModeName[] = "Insert key toggles Overwrite mode";
-const char kInsertKeyToggleModeDescription[] =
-    "Toggles Overwrite mode on or off each time the Insert key is pressed in a "
-    "text editor.";
-
 const char kInstalledAppsInCbdName[] = "Installed Apps in Clear Browsing Data";
 const char kInstalledAppsInCbdDescription[] =
     "Adds the installed apps warning dialog to the clear browsing data flow "
diff --git a/chrome/browser/flag_descriptions.h b/chrome/browser/flag_descriptions.h
index dbdec43..5a4f3d191 100644
--- a/chrome/browser/flag_descriptions.h
+++ b/chrome/browser/flag_descriptions.h
@@ -850,9 +850,6 @@
 extern const char kInProductHelpDemoModeChoiceName[];
 extern const char kInProductHelpDemoModeChoiceDescription[];
 
-extern const char kInsertKeyToggleModeName[];
-extern const char kInsertKeyToggleModeDescription[];
-
 extern const char kInstalledAppsInCbdName[];
 extern const char kInstalledAppsInCbdDescription[];
 
diff --git a/chrome/browser/metrics/chrome_metrics_service_client.cc b/chrome/browser/metrics/chrome_metrics_service_client.cc
index eea42875..aef19cae 100644
--- a/chrome/browser/metrics/chrome_metrics_service_client.cc
+++ b/chrome/browser/metrics/chrome_metrics_service_client.cc
@@ -56,6 +56,7 @@
 #include "chrome/browser/privacy_budget/privacy_budget_ukm_entry_filter.h"
 #include "chrome/browser/profiles/profile_manager.h"
 #include "chrome/browser/safe_browsing/certificate_reporting_metrics_provider.h"
+#include "chrome/browser/safe_browsing/metrics/safe_browsing_metrics_provider.h"
 #include "chrome/browser/sync/device_info_sync_service_factory.h"
 #include "chrome/browser/sync/profile_sync_service_factory.h"
 #include "chrome/browser/tracing/background_tracing_metrics_provider.h"
@@ -692,6 +693,11 @@
   metrics_service_->RegisterMetricsProvider(MakeDemographicMetricsProvider(
       metrics::MetricsLogUploader::MetricServiceType::UMA));
 
+  // TODO(crbug.com/1207574): Add metrics registration for WebView, iOS and
+  // WebLayer.
+  metrics_service_->RegisterMetricsProvider(
+      std::make_unique<safe_browsing::SafeBrowsingMetricsProvider>());
+
 #if defined(OS_ANDROID)
   metrics_service_->RegisterMetricsProvider(
       std::make_unique<metrics::AndroidMetricsProvider>());
diff --git a/chrome/browser/metrics/chrome_metrics_service_client_unittest.cc b/chrome/browser/metrics/chrome_metrics_service_client_unittest.cc
index 2be5518..89a6c95 100644
--- a/chrome/browser/metrics/chrome_metrics_service_client_unittest.cc
+++ b/chrome/browser/metrics/chrome_metrics_service_client_unittest.cc
@@ -165,7 +165,7 @@
   size_t expected_providers = 2;
 
   // This is the number of metrics providers that are outside any #if macros.
-  expected_providers += 20;
+  expected_providers += 21;
 
   int sample_rate;
   if (ChromeMetricsServicesManagerClient::GetSamplingRatePerMille(
diff --git a/chrome/browser/policy/messaging_layer/public/report_client.cc b/chrome/browser/policy/messaging_layer/public/report_client.cc
index 27abc85..48daa0e 100644
--- a/chrome/browser/policy/messaging_layer/public/report_client.cc
+++ b/chrome/browser/policy/messaging_layer/public/report_client.cc
@@ -17,6 +17,9 @@
 #include "base/strings/strcat.h"
 #include "base/task/thread_pool.h"
 #include "base/threading/sequence_bound.h"
+#include "build/build_config.h"
+#include "build/chromeos_buildflags.h"
+#include "chrome/browser/ash/profiles/profile_helper.h"
 #include "chrome/browser/net/system_network_context_manager.h"
 #include "chrome/browser/policy/messaging_layer/public/report_queue_impl.h"
 #include "chrome/browser/policy/messaging_layer/util/get_cloud_policy_client.h"
@@ -336,6 +339,10 @@
   const auto res =
       base::PathService::Get(chrome::DIR_USER_DATA, &user_data_dir);
   DCHECK(res) << "Could not retrieve base path";
+#if BUILDFLAG(IS_CHROMEOS_ASH)
+  user_data_dir = user_data_dir.Append(
+      chromeos::ProfileHelper::Get()->GetActiveUserProfileDir());
+#endif
   reporting_path_ = user_data_dir.Append(kReportingDirectory);
 }
 
diff --git a/chrome/browser/policy/messaging_layer/public/report_client_unittest.cc b/chrome/browser/policy/messaging_layer/public/report_client_unittest.cc
index 095c963..b0aa2c1d 100644
--- a/chrome/browser/policy/messaging_layer/public/report_client_unittest.cc
+++ b/chrome/browser/policy/messaging_layer/public/report_client_unittest.cc
@@ -13,6 +13,7 @@
 #include "base/values.h"
 #include "build/build_config.h"
 #include "build/chromeos_buildflags.h"
+#include "chrome/browser/ash/profiles/profile_helper.h"
 #include "components/policy/core/common/cloud/mock_cloud_policy_client.h"
 #include "components/reporting/client/report_queue_configuration.h"
 #include "components/reporting/client/report_queue_provider.h"
@@ -61,6 +62,8 @@
         profile_->GetProfileUserName(), "12345"));
     const user_manager::User* user =
         mock_user_manager->AddPublicAccountUser(account_id);
+    chromeos::ProfileHelper::Get()->SetActiveUserIdForTesting(
+        profile_->GetProfileUserName());
     mock_user_manager->UserLoggedIn(account_id, user->username_hash(),
                                     /*browser_restart=*/false,
                                     /*is_child=*/false);
diff --git a/chrome/browser/prefs/browser_prefs.cc b/chrome/browser/prefs/browser_prefs.cc
index 7676c66..27d2a968 100644
--- a/chrome/browser/prefs/browser_prefs.cc
+++ b/chrome/browser/prefs/browser_prefs.cc
@@ -234,6 +234,7 @@
 #include "chrome/browser/media/unified_autoplay_config.h"
 #include "chrome/browser/metrics/tab_stats/tab_stats_tracker.h"
 #include "chrome/browser/nearby_sharing/common/nearby_share_prefs.h"
+#include "chrome/browser/search/drive/drive_service.h"
 #include "chrome/browser/search/instant_service.h"
 #include "chrome/browser/search/promos/promo_service.h"
 #include "chrome/browser/search/search_suggest/search_suggest_service.h"
@@ -1069,6 +1070,7 @@
   captions::CaptionController::RegisterProfilePrefs(registry);
   ChromeAuthenticatorRequestDelegate::RegisterProfilePrefs(registry);
   DevToolsWindow::RegisterProfilePrefs(registry);
+  DriveService::RegisterProfilePrefs(registry);
   enterprise_connectors::RegisterProfilePrefs(registry);
   enterprise_reporting::RegisterProfilePrefs(registry);
   extensions::CommandService::RegisterProfilePrefs(registry);
diff --git a/chrome/browser/printing/print_preview_pdf_generated_browsertest.cc b/chrome/browser/printing/print_preview_pdf_generated_browsertest.cc
index faf9ee55..a1c3bb8d 100644
--- a/chrome/browser/printing/print_preview_pdf_generated_browsertest.cc
+++ b/chrome/browser/printing/print_preview_pdf_generated_browsertest.cc
@@ -207,7 +207,7 @@
       return;
     }
 
-    ASSERT_FALSE(script_argument.empty());
+    ASSERT_FALSE(script_argument.DictEmpty());
     GetUI()->SendManipulateSettingsForTest(script_argument);
   }
 
diff --git a/chrome/browser/push_messaging/push_messaging_service_impl.cc b/chrome/browser/push_messaging/push_messaging_service_impl.cc
index c613ade..eb5bbc5 100644
--- a/chrome/browser/push_messaging/push_messaging_service_impl.cc
+++ b/chrome/browser/push_messaging/push_messaging_service_impl.cc
@@ -453,9 +453,8 @@
       payload = message.raw_data;
 
     // Dispatch the message to the appropriate Service Worker.
-    content::BrowserContext::DeliverPushMessage(
-        profile_, origin, service_worker_registration_id, message.message_id,
-        payload,
+    profile_->DeliverPushMessage(
+        origin, service_worker_registration_id, message.message_id, payload,
         base::BindOnce(&PushMessagingServiceImpl::DeliverMessageCallback,
                        weak_factory_.GetWeakPtr(), app_id, origin,
                        service_worker_registration_id, message,
@@ -1365,9 +1364,8 @@
     return;
   }
 
-  content::BrowserContext::FirePushSubscriptionChangeEvent(
-      profile_, app_identifier.origin(),
-      app_identifier.service_worker_registration_id(),
+  profile_->FirePushSubscriptionChangeEvent(
+      app_identifier.origin(), app_identifier.service_worker_registration_id(),
       std::move(new_subscription), std::move(old_subscription),
       base::BindOnce(
           &PushMessagingServiceImpl::FirePushSubscriptionChangeCallback,
diff --git a/chrome/browser/renderer_context_menu/render_view_context_menu.cc b/chrome/browser/renderer_context_menu/render_view_context_menu.cc
index 67384d7b..cc97f23 100644
--- a/chrome/browser/renderer_context_menu/render_view_context_menu.cc
+++ b/chrome/browser/renderer_context_menu/render_view_context_menu.cc
@@ -3195,9 +3195,6 @@
 }
 
 void RenderViewContextMenu::ExecRouteMedia() {
-  if (!media_router::MediaRouterEnabled(browser_context_))
-    return;
-
   media_router::MediaRouterDialogController* dialog_controller =
       media_router::MediaRouterDialogController::GetOrCreateForWebContents(
           embedder_web_contents_);
diff --git a/chrome/browser/resources/chromeos/accessibility/switch_access/action_manager.js b/chrome/browser/resources/chromeos/accessibility/switch_access/action_manager.js
index 22b38cd..a6ebd9f 100644
--- a/chrome/browser/resources/chromeos/accessibility/switch_access/action_manager.js
+++ b/chrome/browser/resources/chromeos/accessibility/switch_access/action_manager.js
@@ -105,6 +105,7 @@
         break;
       case SwitchAccessMenuAction.ITEM_SCAN:
         Navigator.byItem.restart();
+        ActionManager.exitAllMenus();
         break;
       // Point scan actions:
       case SwitchAccessMenuAction.LEFT_CLICK:
@@ -117,7 +118,6 @@
       // Item scan actions:
       default:
         ActionManager.instance.performActionOnCurrentNode_(action);
-        ActionManager.exitCurrentMenu();
     }
   }
 
@@ -287,7 +287,7 @@
 
     const response = this.actionNode_.performAction(action);
     if (response === SAConstants.ActionResponse.CLOSE_MENU) {
-      MenuManager.close();
+      ActionManager.exitAllMenus();
     } else {
       Navigator.byItem.jumpToSwitchAccessMenu();
     }
diff --git a/chrome/browser/resources/chromeos/accessibility/switch_access/item_scan_manager.js b/chrome/browser/resources/chromeos/accessibility/switch_access/item_scan_manager.js
index 22423a6..23b2c2d3 100644
--- a/chrome/browser/resources/chromeos/accessibility/switch_access/item_scan_manager.js
+++ b/chrome/browser/resources/chromeos/accessibility/switch_access/item_scan_manager.js
@@ -218,9 +218,6 @@
       return;
     }
 
-    // Make sure the menu isn't open.
-    ActionManager.exitAllMenus();
-
     const child = this.group_.firstValidChild();
     if (groupIsValid && child) {
       this.setNode_(child);
@@ -228,6 +225,11 @@
     }
 
     this.restoreFromHistory_();
+
+    // Make sure the menu isn't open unless we're still in the menu.
+    if (!this.group_.isEquivalentTo(MenuManager.menuAutomationNode)) {
+      ActionManager.exitAllMenus();
+    }
   }
 
   /** @override */
diff --git a/chrome/browser/resources/chromeos/emoji_picker/emoji_button.html b/chrome/browser/resources/chromeos/emoji_picker/emoji_button.html
index d1b9d44..0490afc 100644
--- a/chrome/browser/resources/chromeos/emoji_picker/emoji_button.html
+++ b/chrome/browser/resources/chromeos/emoji_picker/emoji_button.html
@@ -79,6 +79,7 @@
       box-shadow: var(--cr-elevation-3);
       font-family: 'Roboto', sans-serif;
       font-size: 12px;
+      margin: 4px;
       padding: 4px 8px 4px 8px;
       white-space: nowrap;
     };
diff --git a/chrome/browser/resources/chromeos/emoji_picker/emoji_search.html b/chrome/browser/resources/chromeos/emoji_picker/emoji_search.html
index a877cb5..4fc18fe 100644
--- a/chrome/browser/resources/chromeos/emoji_picker/emoji_search.html
+++ b/chrome/browser/resources/chromeos/emoji_picker/emoji_search.html
@@ -85,6 +85,7 @@
   }
 
   #results {
+    flex-grow: 1;
     margin-inline-end: calc(0px - var(--emoji-picker-side-padding));
     overflow-y: scroll;
   }
diff --git a/chrome/browser/resources/chromeos/emoji_picker/emoji_variants.html b/chrome/browser/resources/chromeos/emoji_picker/emoji_variants.html
index 0ad50868..7bea256 100644
--- a/chrome/browser/resources/chromeos/emoji_picker/emoji_variants.html
+++ b/chrome/browser/resources/chromeos/emoji_picker/emoji_variants.html
@@ -103,6 +103,7 @@
 </div>
 
 <div id="grid-variants">
+  <div id="fake-focus-target" tabindex="-1"></div>
   <template is="dom-repeat" items="[[variantRows]]" as="row">
     <div class="variant-row">
       <template is="dom-repeat" items="[[row]]" as="emoji">
diff --git a/chrome/browser/resources/chromeos/emoji_picker/emoji_variants.js b/chrome/browser/resources/chromeos/emoji_picker/emoji_variants.js
index 179d9ee..b0f2aa4 100644
--- a/chrome/browser/resources/chromeos/emoji_picker/emoji_variants.js
+++ b/chrome/browser/resources/chromeos/emoji_picker/emoji_variants.js
@@ -96,9 +96,7 @@
   }
 
   connectedCallback() {
-    beforeNextRender(
-        this,
-        () => this.shadowRoot.querySelector('emoji-button').focusButton());
+    beforeNextRender(this, () => this.$['fake-focus-target'].focus());
   }
 
   computeVariantRowLengths(variants) {
diff --git a/chrome/browser/resources/download_shelf/BUILD.gn b/chrome/browser/resources/download_shelf/BUILD.gn
index 7527da973..f2ea57f 100644
--- a/chrome/browser/resources/download_shelf/BUILD.gn
+++ b/chrome/browser/resources/download_shelf/BUILD.gn
@@ -55,6 +55,7 @@
   out_manifest = "$target_gen_dir/$preprocess_web_components_manifest"
   in_files = [
     "app.js",
+    "download_button.js",
     "download_item.js",
     "download_list.js",
   ]
@@ -86,12 +87,17 @@
                   ]
   deps = [
     ":app",
+    ":download_button",
     ":download_item",
     ":download_list",
     ":download_shelf_api_proxy",
   ]
 }
 
+js_library("download_button") {
+  deps = [ "//ui/webui/resources/js:custom_element" ]
+}
+
 js_library("download_shelf_api_proxy") {
   deps = [
     "//chrome/browser/ui/webui/download_shelf:mojo_bindings_webui_js",
@@ -102,8 +108,10 @@
 
 js_library("download_item") {
   deps = [
+    ":download_button",
     ":download_shelf_api_proxy",
     "//ui/webui/resources/js:custom_element",
+    "//ui/webui/resources/js:load_time_data.m",
   ]
   externs_list = [ "$externs_path/chrome_extensions.js" ]
 }
@@ -118,7 +126,6 @@
 
 js_library("app") {
   deps = [
-    ":download_item",
     ":download_list",
     "//ui/webui/resources/js:custom_element",
   ]
@@ -127,6 +134,7 @@
 html_to_js("web_components") {
   js_files = [
     "app.js",
+    "download_button.js",
     "download_item.js",
     "download_list.js",
   ]
diff --git a/chrome/browser/resources/download_shelf/download_button.html b/chrome/browser/resources/download_shelf/download_button.html
new file mode 100644
index 0000000..da746a4
--- /dev/null
+++ b/chrome/browser/resources/download_shelf/download_button.html
@@ -0,0 +1,36 @@
+<div>
+<style>
+:host {
+  --google-blue-600-rgb: 26, 115, 232;
+  --google-blue-600: rgb(var(--google-blue-600-rgb));
+  --google-grey-refresh-300-rgb: 218, 220, 224;
+  --google-grey-refresh-300: rgb(var(--google-grey-refresh-300-rgb));
+  --google-blue-refresh-500-rgb: 66, 133, 244;
+
+  --border-color: var(--google-grey-refresh-300);
+  --text-color: var(--google-blue-600);
+  --hover-bg-color: rgba(var(--google-blue-refresh-500-rgb), 0.12);
+  --active-bg-color: rgba(var(--google-blue-600-rgb), 0.32);
+
+  border: 1px solid var(--border-color);
+  border-radius: 4px;
+  box-sizing: border-box;
+  color: var(--text-color);
+  cursor: pointer;
+  font-family: Roboto;
+  font-weight: 500;
+  padding: 8px 16px;
+  transition: background-color 300ms;
+  user-select: none;
+}
+
+:host(:hover) {
+  background-color: var(--hover-bg-color);
+}
+
+:host(:active) {
+  background-color: var(--active-bg-color);
+}
+</style>
+<slot></slot>
+</div>
\ No newline at end of file
diff --git a/chrome/browser/resources/download_shelf/download_button.js b/chrome/browser/resources/download_shelf/download_button.js
new file mode 100644
index 0000000..3fcea46
--- /dev/null
+++ b/chrome/browser/resources/download_shelf/download_button.js
@@ -0,0 +1,21 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+/**
+ * @fileoverview Button UI for "Discard" and "Show All".
+ */
+
+import {CustomElement} from 'chrome://resources/js/custom_element.js';
+
+export class DownloadButtonElement extends CustomElement {
+  static get template() {
+    return `{__html_template__}`;
+  }
+
+  constructor() {
+    super();
+  }
+}
+
+customElements.define('download-button', DownloadButtonElement);
\ No newline at end of file
diff --git a/chrome/browser/resources/download_shelf/download_item.html b/chrome/browser/resources/download_shelf/download_item.html
index c3a4e990..5c83f0b 100644
--- a/chrome/browser/resources/download_shelf/download_item.html
+++ b/chrome/browser/resources/download_shelf/download_item.html
@@ -10,12 +10,15 @@
   --google-grey-refresh-700: rgb(var(--google-grey-refresh-700-rgb));
   --google-grey-900-rgb: 32, 33, 36;  /* #202124 */
   --google-grey-900: rgb(var(--google-grey-900-rgb));
+  --google-red-600-rgb: 217, 49, 37 ; /* #d93025 */
+  --google-red-600: rgb(var(--google-red-600-rgb));
 
   --button-hover-color: rgba(var(--google-grey-900-rgb), .1);
   --button-icon-stroke-color: var(--google-grey-refresh-700);
   --pi: 3.14159265358979;
   --spinner-color: var(--google-grey-900-rgb);
   --text-width: 140px;
+  --warn-text-width: 245px;
   --transparent-button-color: rgba(0, 0, 0, 0);
 }
 
@@ -26,15 +29,11 @@
   flex-direction: row;
   height: 100%;
   overflow: hidden;
-  padding: 6px 0 6px 12px;
+  padding: 6px 0 6px 8px;
   position: relative;
   user-select: none;
 }
 
-.download-item:hover {
-  background-color: var(--button-hover-color);
-}
-
 .progress {
   --width: 26px;
   --radius: calc(var(--width) / 2);
@@ -96,7 +95,7 @@
   flex-direction: column;
   height: 100%;
   justify-content: center;
-  padding-inline-start: 8px;
+  padding-inline-start: 10px;
   width: var(--text-width);
 }
 
@@ -116,12 +115,29 @@
 #dropdown-button {
   background-color: var(--transparent-button-color);
   border: none;
+  border-radius: 4px;
   height: 32px;
   margin: 8px;
+  transition: background-color 300ms;
   width: 32px;
+  z-index: 2;
 }
 
 #dropdown-button:hover {
+  background-color: var(--button-hover-color);
+}
+
+#shadow-mask {
+  height: 100%;
+  left: 0;
+  position: absolute;
+  top: 0;
+  transition: background-color 300ms;
+  width: 100%;
+  z-index: 1;
+}
+
+#shadow-mask:hover {
   background-color: var(--button-hover-color)
 }
 
@@ -129,32 +145,87 @@
   fill: transparent;
   stroke: var(--button-icon-stroke-color);
   stroke-width: 1.2;
+  z-index: 2;
+}
+
+#icon {
+  display: flex;
+  z-index: 1;
 }
 
 #file-icon {
   height: 16px;
-  position: absolute;
+}
+
+#error-icon {
+  display: none;
+  fill: var(--google-red-600);
+  height: 26px;
+  width: 26px;
 }
 
 #separator {
-  background-color: rgba(255, 255, 255, .1);
+  background-color: rgba(0, 0, 0, .1);
   height: 90%;
   width: 1px;
 }
 
+#warning-text {
+  display: none;
+  line-height: 150%;
+}
+
+#discard-button {
+  display: none;
+  z-index: 2;
+}
+
+[data-display-mode='warn'] #text-container {
+  width: var(--warn-text-width);
+}
+
+[data-display-mode='warn'] .progress {
+  padding: 0 0 0 3px;
+}
+
+[data-display-mode='warn'] #discard-button,
+[data-display-mode='warn'] #error-icon,
+[data-display-mode='warn'] #warning-text {
+  display: block;
+}
+
+[data-display-mode='warn'] #shadow-mask,
+[data-display-mode='warn'] #file-icon,
+[data-display-mode='warn'] #filename,
+[data-display-mode='warn'] #status-text,
+[data-display-mode='warn'] .progress-indicator {
+  display: none;
+}
+
 </style>
 <div class="download-item">
+  <div id="shadow-mask"></div>
   <div class="progress">
     <svg class="progress-indicator">
       <circle class="progress-circle"></circle>
       <circle class="progress-spinner"></circle>
     </svg>
-    <img id="file-icon">
+    <div id="icon">
+      <img id="file-icon">
+      <svg id='error-icon' viewBox="0 0 26 26">
+        <g>
+          <path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm1 15h-2v-2h2v2zm0-4h-2V7h2v6z">
+          </path>
+        </g>
+      </svg>
+    </div>
   </div>
   <div id="text-container">
     <div id="filename"></div>
     <div id="status-text"></div>
+    <div id="warning-text"></div>
   </div>
+  <download-button id="discard-button"></download-button>
   <button id="dropdown-button">
     <svg id="dropdown-icon" viewBox="0 0 16 16">
       <g>
diff --git a/chrome/browser/resources/download_shelf/download_item.js b/chrome/browser/resources/download_shelf/download_item.js
index 6c4ee61..5abf633 100644
--- a/chrome/browser/resources/download_shelf/download_item.js
+++ b/chrome/browser/resources/download_shelf/download_item.js
@@ -6,11 +6,21 @@
  * @fileoverview UI element of a download item.
  */
 
-import {CustomElement} from 'chrome://resources/js/custom_element.js';
+import './download_button.js';
+import './strings.m.js';
 
-import {DownloadItem, DownloadState} from './download_shelf.mojom-webui.js';
+import {CustomElement} from 'chrome://resources/js/custom_element.js';
+import {loadTimeData} from 'chrome://resources/js/load_time_data.m.js';
+
+import {DangerType, DownloadItem, DownloadMode, DownloadState, MixedContentStatus} from './download_shelf.mojom-webui.js';
 import {DownloadShelfApiProxy, DownloadShelfApiProxyImpl} from './download_shelf_api_proxy.js';
 
+/** @enum {string} */
+const DisplayMode = {
+  kNormal: 'normal',
+  kWarn: 'warn'
+};
+
 export class DownloadItemElement extends CustomElement {
   static get template() {
     return `{__html_template__}`;
@@ -27,7 +37,12 @@
 
     this.$('#dropdown-button')
         .addEventListener('click', e => this.onDropdownButtonClick_(e));
+    this.$('#discard-button')
+        .addEventListener('click', e => this.onDiscardButtonClick_(e));
     this.addEventListener('contextmenu', e => this.onContextMenu_(e));
+
+    this.$('#discard-button').innerText =
+        loadTimeData.getString('discardButtonText');
   }
 
   /** @param {DownloadItem} value */
@@ -87,6 +102,12 @@
     this.apiProxy_.getFileIcon(item.id).then(icon => {
       this.$('#file-icon').src = icon;
     });
+
+    downloadElement.dataset.displayMode =
+        this.item_.mode === DownloadMode.kNormal ? DisplayMode.kNormal :
+                                                   DisplayMode.kWarn;
+    this.$('#warning-text').innerText =
+        item.warningText ? item.warningText : '';
   }
 
   /** @param {number} value */
@@ -107,6 +128,12 @@
     const rect = e.target.getBoundingClientRect();
     this.apiProxy_.showContextMenu(this.item.id, rect.left, rect.top);
   }
+
+  /** @param {!Event} e */
+  onDiscardButtonClick_(e) {
+    // TODO(crbug.com/1182529): Notify C++ through mojo. Remove this item
+    // from download_list.
+  }
 }
 
 customElements.define('download-item', DownloadItemElement);
diff --git a/chrome/browser/resources/new_tab_page/modules/drive/module.html b/chrome/browser/resources/new_tab_page/modules/drive/module.html
index 6c366a5..6d17f8f 100644
--- a/chrome/browser/resources/new_tab_page/modules/drive/module.html
+++ b/chrome/browser/resources/new_tab_page/modules/drive/module.html
@@ -68,10 +68,14 @@
   }
 </style>
 <ntp-module-header
+    dismiss-text="[[i18nRecursive('',
+                                  'modulesDismissButtonText',
+                                  'modulesDriveFilesLower')]]"
     disable-text="[[i18nRecursive('',
                                   'modulesDisableButtonText',
                                   'modulesDriveSentence2')]]"
     show-info-button on-info-button-click="onInfoButtonClick_"
+    show-dismiss-button on-dismiss-button-click="onDismissButtonClick_"
     on-disable-button-click="onDisableButtonClick_">
   [[i18n('modulesDriveTitle')]]
 </ntp-module-header>
diff --git a/chrome/browser/resources/new_tab_page/modules/drive/module.js b/chrome/browser/resources/new_tab_page/modules/drive/module.js
index 522d6e73..cf5dfdd 100644
--- a/chrome/browser/resources/new_tab_page/modules/drive/module.js
+++ b/chrome/browser/resources/new_tab_page/modules/drive/module.js
@@ -43,6 +43,21 @@
   }
 
   /** @private */
+  onDismissButtonClick_() {
+    DriveProxy.getInstance().handler.dismissModule();
+    this.dispatchEvent(new CustomEvent('dismiss-module', {
+      bubbles: true,
+      composed: true,
+      detail: {
+        message: loadTimeData.getStringF(
+            'dismissModuleToastMessage',
+            loadTimeData.getString('modulesDriveFilesSentence')),
+        restoreCallback: () => DriveProxy.getInstance().handler.restoreModule(),
+      },
+    }));
+  }
+
+  /** @private */
   onDisableButtonClick_() {
     this.dispatchEvent(new CustomEvent('disable-module', {
       bubbles: true,
diff --git a/chrome/browser/resources/settings/chromeos/lazy_load.html b/chrome/browser/resources/settings/chromeos/lazy_load.html
index 585422e..416173e 100644
--- a/chrome/browser/resources/settings/chromeos/lazy_load.html
+++ b/chrome/browser/resources/settings/chromeos/lazy_load.html
@@ -4,7 +4,6 @@
   <link rel="import" href="crostini_page/crostini_page.html">
   <link rel="import" href="date_time_page/date_time_page.html">
   <link rel="import" href="date_time_page/timezone_selector.html">
-  <link rel="import" href="on_startup_page/on_startup_page.html">
   <link rel="import" href="os_a11y_page/os_a11y_page.html">
   <link rel="import" href="os_files_page/os_files_page.html">
   <link rel="import" href="os_languages_page/os_languages_section.html">
diff --git a/chrome/browser/resources/settings/chromeos/os_apps_page/os_apps_page.html b/chrome/browser/resources/settings/chromeos/os_apps_page/os_apps_page.html
index 98c6840..6913fc9 100644
--- a/chrome/browser/resources/settings/chromeos/os_apps_page/os_apps_page.html
+++ b/chrome/browser/resources/settings/chromeos/os_apps_page/os_apps_page.html
@@ -85,6 +85,15 @@
           </cr-link-row>
         </template>
       </template>
+      <template is="dom-if" if="[[showStartup]]">
+        <cr-link-row id="onStartup"
+            label="$i18n{onStartupPageTitle}"
+            sub-label="[[getAssistantEnabledDisabledLabel_(
+                    prefs.settings.voice_interaction.enabled.value)]]"
+            on-click="onStartupClick_"
+            role-description="$i18n{subpageArrowRoleDescription}">
+        </cr-link-row>
+      </template>
     </div>
 
     <!-- APP MANAGEMENT -->
@@ -139,6 +148,16 @@
         </settings-subpage>
       </template>
     </template>
+
+    <!-- On Startup -->
+    <template is="dom-if" if="[[showStartup]]" restamp>
+      <template is="dom-if" route-path="/onstartup">
+        <settings-subpage page-title="$i18n{onStartupPageTitle}">
+          <settings-on-startup-page prefs="{{prefs}}">
+          </settings-on-startup-page>
+        </settings-subpage>
+      </template>
+    </template>
   </settings-animated-pages>
   </template>
   <script src="os_apps_page.js"></script>
diff --git a/chrome/browser/resources/settings/chromeos/os_apps_page/os_apps_page.js b/chrome/browser/resources/settings/chromeos/os_apps_page/os_apps_page.js
index 6f96191..3f64f05 100644
--- a/chrome/browser/resources/settings/chromeos/os_apps_page/os_apps_page.js
+++ b/chrome/browser/resources/settings/chromeos/os_apps_page/os_apps_page.js
@@ -54,6 +54,12 @@
      */
     showPluginVm: Boolean,
 
+    /**
+     * Show On startup settings and sub-page.
+     * @type {boolean}
+     */
+    showStartup: Boolean,
+
     /** @private {!Map<string, string>} */
     focusConfig_: {
       type: Object,
@@ -67,6 +73,9 @@
               settings.routes.ANDROID_APPS_DETAILS.path,
               '#android-apps .subpage-arrow');
         }
+        if (settings.routes.ON_STARTUP) {
+          map.set(settings.routes.ON_STARTUP.path, '#onStartup');
+        }
         return map;
       },
     },
@@ -163,4 +172,10 @@
     settings.AndroidAppsBrowserProxyImpl.getInstance().showAndroidAppsSettings(
         isKeyboardAction);
   },
+
+  /** @private */
+  onStartupClick_() {
+    settings.Router.getInstance().navigateTo(settings.routes.ON_STARTUP);
+  },
+
 });
diff --git a/chrome/browser/resources/settings/chromeos/os_route.js b/chrome/browser/resources/settings/chromeos/os_route.js
index 7a161f4..31d7c02 100644
--- a/chrome/browser/resources/settings/chromeos/os_route.js
+++ b/chrome/browser/resources/settings/chromeos/os_route.js
@@ -204,6 +204,11 @@
           r.APP_MANAGEMENT, mojom.PLUGIN_VM_USB_PREFERENCES_SUBPAGE_PATH,
           Subpage.kPluginVmUsbPreferences);
     }
+    if (loadTimeData.valueExists('showStartup') &&
+        loadTimeData.getBoolean('showStartup')) {
+      r.ON_STARTUP = createSubpage(
+          r.APPS, mojom.ON_STARTUP_SUBPAGE_PATH, Subpage.kOnStartup);
+    }
 
     // Crostini section.
     if (loadTimeData.valueExists('showCrostini') &&
@@ -233,10 +238,6 @@
           Subpage.kCrostiniPortForwarding);
     }
 
-    // On Startup section.
-    r.ON_STARTUP = createSection(
-        r.BASIC, mojom.ON_STARTUP_SECTION_PATH, Section.kOnStartup);
-
     // Date and Time section.
     r.DATETIME = createSection(
         r.ADVANCED, mojom.DATE_AND_TIME_SECTION_PATH, Section.kDateAndTime);
diff --git a/chrome/browser/resources/settings/chromeos/os_settings_menu/os_settings_menu.html b/chrome/browser/resources/settings/chromeos/os_settings_menu/os_settings_menu.html
index eaaae27e..346bffe 100644
--- a/chrome/browser/resources/settings/chromeos/os_settings_menu/os_settings_menu.html
+++ b/chrome/browser/resources/settings/chromeos/os_settings_menu/os_settings_menu.html
@@ -203,10 +203,6 @@
         <iron-icon icon="os-settings:apps"></iron-icon>
         $i18n{appsPageTitle}
       </a>
-      <a href="/onstartup" hidden="[[!showStartup]]" class="item">
-        <iron-icon icon="os-settings:startup"></iron-icon>
-        $i18n{onStartupPageTitle}
-      </a>
       <cr-button id="advancedButton"
           aria-expanded$="[[boolToString_(advancedOpened)]]"
           on-click="onAdvancedButtonToggle_">
diff --git a/chrome/browser/resources/settings/chromeos/os_settings_page/os_settings_page.html b/chrome/browser/resources/settings/chromeos/os_settings_page/os_settings_page.html
index 9dc2003..de1b746 100644
--- a/chrome/browser/resources/settings/chromeos/os_settings_page/os_settings_page.html
+++ b/chrome/browser/resources/settings/chromeos/os_settings_page/os_settings_page.html
@@ -27,7 +27,6 @@
 <link rel="import" href="../internet_page/internet_page.html">
 <link rel="import" href="../kerberos_page/kerberos_page.html">
 <link rel="import" href="../multidevice_page/multidevice_page.html">
-<link rel="import" href="../on_startup_page/on_startup_page.html">
 <link rel="import" href="chrome://resources/html/assert.html">
 
 <dom-module id="os-settings-page">
@@ -203,16 +202,10 @@
               android-apps-info="[[androidAppsInfo]]"
               have-play-store-app="[[havePlayStoreApp]]"
               show-android-apps="[[showAndroidApps]]"
-              show-plugin-vm="[[showPluginVm]]">
+              show-plugin-vm="[[showPluginVm]]"
+              show-startup="[[showStartup]]">
           </os-settings-apps-page>
         </settings-section>
-        <template is="dom-if" if="[[showStartup]]">
-          <settings-section page-title="$i18n{onStartupPageTitle}"
-              section="onstartup">
-            <settings-on-startup-page prefs="{{prefs}}">
-            </settings-on-startup-page>
-          </settings-section>
-        </template>
       </div>
     </template>
 
diff --git a/chrome/browser/safe_browsing/metrics/safe_browsing_metrics_provider.cc b/chrome/browser/safe_browsing/metrics/safe_browsing_metrics_provider.cc
new file mode 100644
index 0000000..a3ec111
--- /dev/null
+++ b/chrome/browser/safe_browsing/metrics/safe_browsing_metrics_provider.cc
@@ -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.
+
+#include "chrome/browser/safe_browsing/metrics/safe_browsing_metrics_provider.h"
+
+#include "base/metrics/histogram_functions.h"
+#include "chrome/browser/profiles/profile.h"
+#include "components/prefs/pref_service.h"
+#include "components/safe_browsing/core/common/safe_browsing_prefs.h"
+
+namespace safe_browsing {
+
+SafeBrowsingMetricsProvider::SafeBrowsingMetricsProvider() = default;
+
+SafeBrowsingMetricsProvider::~SafeBrowsingMetricsProvider() = default;
+
+void SafeBrowsingMetricsProvider::ProvideCurrentSessionData(
+    metrics::ChromeUserMetricsExtension* uma_proto) {
+  Profile* profile = cached_profile_.GetMetricsProfile();
+
+  if (!profile)
+    return;
+
+  SafeBrowsingState state = GetSafeBrowsingState(*profile->GetPrefs());
+
+  base::UmaHistogramEnumeration(
+      "SafeBrowsing.Pref.MainProfile.SafeBrowsingState", state);
+}
+
+}  // namespace safe_browsing
diff --git a/chrome/browser/safe_browsing/metrics/safe_browsing_metrics_provider.h b/chrome/browser/safe_browsing/metrics/safe_browsing_metrics_provider.h
new file mode 100644
index 0000000..e3d347a3
--- /dev/null
+++ b/chrome/browser/safe_browsing/metrics/safe_browsing_metrics_provider.h
@@ -0,0 +1,37 @@
+// 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_SAFE_BROWSING_METRICS_SAFE_BROWSING_METRICS_PROVIDER_H_
+#define CHROME_BROWSER_SAFE_BROWSING_METRICS_SAFE_BROWSING_METRICS_PROVIDER_H_
+
+#include "chrome/browser/metrics/cached_metrics_profile.h"
+#include "components/metrics/metrics_provider.h"
+
+namespace metrics {
+class ChromeUserMetricsExtension;
+}  // namespace metrics
+
+namespace safe_browsing {
+
+// A registerable metrics provider that will emit the Safe Browsing state of the
+// main profile upon UMA upload.
+class SafeBrowsingMetricsProvider : public metrics::MetricsProvider {
+ public:
+  SafeBrowsingMetricsProvider();
+  ~SafeBrowsingMetricsProvider() override;
+  SafeBrowsingMetricsProvider(const SafeBrowsingMetricsProvider&) = delete;
+  SafeBrowsingMetricsProvider& operator=(const SafeBrowsingMetricsProvider&) =
+      delete;
+
+  // MetricsProvider overrides.
+  void ProvideCurrentSessionData(
+      metrics::ChromeUserMetricsExtension* uma_proto) override;
+
+ private:
+  metrics::CachedMetricsProfile cached_profile_;
+};
+
+}  // namespace safe_browsing
+
+#endif  // CHROME_BROWSER_SAFE_BROWSING_METRICS_SAFE_BROWSING_METRICS_PROVIDER_H_
diff --git a/chrome/browser/search/drive/drive.mojom b/chrome/browser/search/drive/drive.mojom
index 0fe6af88..cc9a2ed 100644
--- a/chrome/browser/search/drive/drive.mojom
+++ b/chrome/browser/search/drive/drive.mojom
@@ -24,4 +24,8 @@
 interface DriveHandler {
   // Fetches document suggestions from ItemSuggest API.
   GetFiles() => (array<File> files);
+  // Dismissed module for fixed amount of time.
+  DismissModule();
+  // Restores the module immediately.
+  RestoreModule();
 };
diff --git a/chrome/browser/search/drive/drive_handler.cc b/chrome/browser/search/drive/drive_handler.cc
index e680310..3cde28f0 100644
--- a/chrome/browser/search/drive/drive_handler.cc
+++ b/chrome/browser/search/drive/drive_handler.cc
@@ -18,3 +18,11 @@
   DriveServiceFactory::GetForProfile(profile_)->GetDriveFiles(
       std::move(callback));
 }
+
+void DriveHandler::DismissModule() {
+  DriveServiceFactory::GetForProfile(profile_)->DismissModule();
+}
+
+void DriveHandler::RestoreModule() {
+  DriveServiceFactory::GetForProfile(profile_)->RestoreModule();
+}
diff --git a/chrome/browser/search/drive/drive_handler.h b/chrome/browser/search/drive/drive_handler.h
index fd82380..c07045a5 100644
--- a/chrome/browser/search/drive/drive_handler.h
+++ b/chrome/browser/search/drive/drive_handler.h
@@ -20,6 +20,8 @@
 
   // drive::mojom::DriveHandler:
   void GetFiles(GetFilesCallback callback) override;
+  void DismissModule() override;
+  void RestoreModule() override;
 
  private:
   mojo::Receiver<drive::mojom::DriveHandler> handler_;
diff --git a/chrome/browser/search/drive/drive_service.cc b/chrome/browser/search/drive/drive_service.cc
index d2ba01ef..4cccd6d 100644
--- a/chrome/browser/search/drive/drive_service.cc
+++ b/chrome/browser/search/drive/drive_service.cc
@@ -8,8 +8,13 @@
 #include <string>
 #include <utility>
 
+#include "base/metrics/field_trial_params.h"
 #include "base/strings/stringprintf.h"
+#include "base/time/time.h"
 #include "build/build_config.h"
+#include "components/prefs/pref_registry_simple.h"
+#include "components/prefs/pref_service.h"
+#include "components/search/ntp_features.h"
 #include "components/signin/public/identity_manager/identity_manager.h"
 #include "components/signin/public/identity_manager/primary_account_access_token_fetcher.h"
 #include "components/signin/public/identity_manager/scope_set.h"
@@ -80,17 +85,70 @@
           }
         }
       })");
+constexpr char kFakeData[] = R"({
+  "item": [
+    {
+      "itemId": "foo",
+      "url": "https://docs.google.com",
+      "driveItem": {
+        "title": "foo doc",
+        "mimeType": "application/vnd.google-apps.document"
+      },
+      "justification": {
+        "displayText": { "textSegment": [{"text": "You opened yesterday"}]}
+      }
+    },
+    {
+      "itemId": "bar",
+      "url": "https://sheets.google.com",
+      "driveItem": {
+        "title": "bar sheet",
+        "mimeType": "application/vnd.google-apps.spreadsheet"
+      },
+      "justification": {
+        "displayText": { "textSegment": [{"text": "You opened today"}]}
+      }
+    },
+    {
+      "itemId": "baz",
+      "url": "https://slides.google.com",
+      "driveItem": {
+        "title": "baz slides",
+        "mimeType": "application/vnd.google-apps.presentation"
+      },
+      "justification": {
+        "displayText": { "textSegment": [{"text": "You opened on Monday"}]}
+      }
+    }
+  ]
+}
+)";
 }  // namespace
 
+// static
+const char DriveService::kLastDismissedTimePrefName[] =
+    "NewTabPage.Drive.LastDimissedTime";
+
+// static
+const base::TimeDelta DriveService::kDismissDuration =
+    base::TimeDelta::FromDays(14);
+
 DriveService::~DriveService() = default;
 
 DriveService::DriveService(
     scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
     signin::IdentityManager* identity_manager,
-    const std::string& application_locale)
+    const std::string& application_locale,
+    PrefService* pref_service)
     : url_loader_factory_(std::move(url_loader_factory)),
       identity_manager_(identity_manager),
-      application_locale_(application_locale) {}
+      application_locale_(application_locale),
+      pref_service_(pref_service) {}
+
+// static
+void DriveService::RegisterProfilePrefs(PrefRegistrySimple* registry) {
+  registry->RegisterTimePref(kLastDismissedTimePrefName, base::Time());
+}
 
 void DriveService::GetDriveFiles(GetFilesCallback callback) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
@@ -99,6 +157,27 @@
     return;
   }
 
+  // Bail if module is still dismissed.
+  if (!pref_service_->GetTime(kLastDismissedTimePrefName).is_null() &&
+      base::Time::Now() - pref_service_->GetTime(kLastDismissedTimePrefName) <
+          kDismissDuration) {
+    for (auto& callback : callbacks_) {
+      std::move(callback).Run(std::vector<drive::mojom::FilePtr>());
+    }
+    callbacks_.clear();
+    return;
+  }
+
+  // Skip fetch and jump straight to data parsing when serving fake data.
+  if (base::GetFieldTrialParamValueByFeature(
+          ntp_features::kNtpDriveModule,
+          ntp_features::kNtpDriveModuleDataParam) == "fake") {
+    data_decoder::DataDecoder::ParseJsonIsolated(
+        kFakeData, base::BindOnce(&DriveService::OnJsonParsed,
+                                  weak_factory_.GetWeakPtr()));
+    return;
+  }
+
   token_fetcher_ = std::make_unique<signin::PrimaryAccountAccessTokenFetcher>(
       "ntp_drive_module", identity_manager_, signin::ScopeSet({kDriveScope}),
       base::BindOnce(&DriveService::OnTokenReceived,
@@ -107,6 +186,14 @@
       signin::ConsentLevel::kSync);
 }
 
+void DriveService::DismissModule() {
+  pref_service_->SetTime(kLastDismissedTimePrefName, base::Time::Now());
+}
+
+void DriveService::RestoreModule() {
+  pref_service_->SetTime(kLastDismissedTimePrefName, base::Time());
+}
+
 void DriveService::OnTokenReceived(GoogleServiceAuthError error,
                                    signin::AccessTokenInfo token_info) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
diff --git a/chrome/browser/search/drive/drive_service.h b/chrome/browser/search/drive/drive_service.h
index 3887307..64b4ff6 100644
--- a/chrome/browser/search/drive/drive_service.h
+++ b/chrome/browser/search/drive/drive_service.h
@@ -17,6 +17,9 @@
 #include "services/network/public/cpp/shared_url_loader_factory.h"
 #include "services/network/public/cpp/simple_url_loader.h"
 
+class PrefRegistrySimple;
+class PrefService;
+
 namespace signin {
 class IdentityManager;
 class PrimaryAccountAccessTokenFetcher;
@@ -25,17 +28,26 @@
 // Handles requests for user Google Drive data.
 class DriveService : public KeyedService {
  public:
+  static const char kLastDismissedTimePrefName[];
+  static const base::TimeDelta kDismissDuration;
+
   DriveService(const DriveService&) = delete;
   DriveService(
       scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
       signin::IdentityManager* identity_manager,
-      const std::string& application_locale);
+      const std::string& application_locale,
+      PrefService* pref_service);
   ~DriveService() override;
 
+  static void RegisterProfilePrefs(PrefRegistrySimple* registry);
+
   using GetFilesCallback = drive::mojom::DriveHandler::GetFilesCallback;
   // Retrieves Google Drive document suggestions from ItemSuggest API.
   void GetDriveFiles(GetFilesCallback callback);
-  std::string BuildRequestBody();
+  // Makes the service not return data for a specified amount of time.
+  void DismissModule();
+  // Makes the service return data again even if dimiss time is not yet over.
+  void RestoreModule();
 
  private:
   void OnTokenReceived(GoogleServiceAuthError error,
@@ -51,6 +63,7 @@
   std::vector<GetFilesCallback> callbacks_;
   signin::IdentityManager* identity_manager_;
   std::string application_locale_;
+  PrefService* pref_service_;
   SEQUENCE_CHECKER(sequence_checker_);
   base::WeakPtrFactory<DriveService> weak_factory_{this};
 };
diff --git a/chrome/browser/search/drive/drive_service_factory.cc b/chrome/browser/search/drive/drive_service_factory.cc
index 945a46cf..d45ef40 100644
--- a/chrome/browser/search/drive/drive_service_factory.cc
+++ b/chrome/browser/search/drive/drive_service_factory.cc
@@ -35,8 +35,8 @@
     content::BrowserContext* context) const {
   auto url_loader_factory = context->GetDefaultStoragePartition()
                                 ->GetURLLoaderFactoryForBrowserProcess();
-  return new DriveService(url_loader_factory,
-                          IdentityManagerFactory::GetForProfile(
-                              Profile::FromBrowserContext(context)),
-                          g_browser_process->GetApplicationLocale());
+  auto* profile = Profile::FromBrowserContext(context);
+  return new DriveService(
+      url_loader_factory, IdentityManagerFactory::GetForProfile(profile),
+      g_browser_process->GetApplicationLocale(), profile->GetPrefs());
 }
diff --git a/chrome/browser/search/drive/drive_service_unittest.cc b/chrome/browser/search/drive/drive_service_unittest.cc
index 6f37ad72..5242525 100644
--- a/chrome/browser/search/drive/drive_service_unittest.cc
+++ b/chrome/browser/search/drive/drive_service_unittest.cc
@@ -5,7 +5,10 @@
 #include "chrome/browser/search/drive/drive_service.h"
 #include "base/json/json_reader.h"
 #include "base/test/mock_callback.h"
+#include "base/test/scoped_feature_list.h"
 #include "chrome/test/base/testing_profile.h"
+#include "components/prefs/testing_pref_service.h"
+#include "components/search/ntp_features.h"
 #include "components/signin/public/identity_manager/identity_test_environment.h"
 #include "content/public/test/browser_task_environment.h"
 #include "google_apis/gaia/google_service_auth_error.h"
@@ -17,15 +20,17 @@
 class DriveServiceTest : public testing::Test {
  public:
   DriveServiceTest()
-      : task_environment_(content::BrowserTaskEnvironment::IO_MAINLOOP) {}
+      : task_environment_(content::BrowserTaskEnvironment::IO_MAINLOOP,
+                          base::test::TaskEnvironment::TimeSource::MOCK_TIME) {}
 
   void SetUp() override {
     testing::Test::SetUp();
     service_ = std::make_unique<DriveService>(
         base::MakeRefCounted<network::WeakWrapperSharedURLLoaderFactory>(
             &test_url_loader_factory_),
-        identity_test_env.identity_manager(), "en-US");
+        identity_test_env.identity_manager(), "en-US", &prefs_);
     identity_test_env.MakePrimaryAccountAvailable("example@google.com");
+    service_->RegisterProfilePrefs(prefs_.registry());
   }
 
   void TearDown() override {
@@ -39,6 +44,7 @@
   std::unique_ptr<DriveService> service_;
   data_decoder::test::InProcessDataDecoder in_process_data_decoder_;
   signin::IdentityTestEnvironment identity_test_env;
+  TestingPrefServiceSimple prefs_;
 };
 
 TEST_F(DriveServiceTest, PassesDataOnSuccess) {
@@ -52,6 +58,10 @@
             actual_documents = std::move(documents);
           }));
 
+  // Make sure we are not in the dismissed time window.
+  prefs_.SetTime(DriveService::kLastDismissedTimePrefName, base::Time::Now());
+  task_environment_.AdvanceClock(DriveService::kDismissDuration);
+
   service_->GetDriveFiles(callback.Get());
 
   identity_test_env.WaitForAccessTokenRequestIfNecessaryAndRespondWithToken(
@@ -227,6 +237,22 @@
   EXPECT_EQ("234", response4.at(0)->id);
 }
 
+TEST_F(DriveServiceTest, PassesNoDataIfDismissed) {
+  bool passed_no_data = false;
+  base::MockCallback<DriveService::GetFilesCallback> callback;
+  EXPECT_CALL(callback, Run(testing::_))
+      .Times(1)
+      .WillOnce(testing::Invoke(
+          [&passed_no_data](std::vector<drive::mojom::FilePtr> suggestions) {
+            passed_no_data = suggestions.empty();
+          }));
+
+  prefs_.SetTime(DriveService::kLastDismissedTimePrefName, base::Time::Now());
+  service_->GetDriveFiles(callback.Get());
+
+  EXPECT_TRUE(passed_no_data);
+}
+
 TEST_F(DriveServiceTest, PassesNoDataOnAuthError) {
   bool token_is_valid = true;
 
@@ -328,3 +354,44 @@
 
   EXPECT_TRUE(actual_documents.empty());
 }
+
+TEST_F(DriveServiceTest, DismissModule) {
+  service_->DismissModule();
+  EXPECT_EQ(base::Time::Now(),
+            prefs_.GetTime(DriveService::kLastDismissedTimePrefName));
+}
+
+TEST_F(DriveServiceTest, RestoreModule) {
+  service_->RestoreModule();
+  EXPECT_EQ(base::Time(),
+            prefs_.GetTime(DriveService::kLastDismissedTimePrefName));
+}
+
+class DriveServiceFakeDataTest : public DriveServiceTest {
+ public:
+  DriveServiceFakeDataTest() {
+    features_.InitAndEnableFeatureWithParameters(
+        ntp_features::kNtpDriveModule, {{"NtpDriveModuleDataParam", "fake"}});
+  }
+
+ private:
+  base::test::ScopedFeatureList features_;
+};
+
+TEST_F(DriveServiceFakeDataTest, ReturnsFakeData) {
+  std::vector<drive::mojom::FilePtr> fake_documents;
+  base::MockCallback<DriveService::GetFilesCallback> callback;
+  EXPECT_CALL(callback, Run(testing::_))
+      .Times(1)
+      .WillOnce(
+          testing::Invoke([&](std::vector<drive::mojom::FilePtr> documents) {
+            fake_documents = std::move(documents);
+          }));
+
+  prefs_.SetTime(DriveService::kLastDismissedTimePrefName, base::Time::Now());
+  task_environment_.AdvanceClock(DriveService::kDismissDuration);
+  service_->GetDriveFiles(callback.Get());
+  task_environment_.RunUntilIdle();
+
+  EXPECT_FALSE(fake_documents.empty());
+}
diff --git a/chrome/browser/supervised_user/supervised_user_pref_store_unittest.cc b/chrome/browser/supervised_user/supervised_user_pref_store_unittest.cc
index d0b997d..241f5ce 100644
--- a/chrome/browser/supervised_user/supervised_user_pref_store_unittest.cc
+++ b/chrome/browser/supervised_user/supervised_user_pref_store_unittest.cc
@@ -101,7 +101,7 @@
   // activated yet.
   pref_store_->SetInitializationCompleted();
   EXPECT_TRUE(fixture.initialization_completed());
-  EXPECT_EQ(0u, fixture.changed_prefs()->size());
+  EXPECT_EQ(0u, fixture.changed_prefs()->DictSize());
 
   service_.SetActive(true);
 
@@ -140,7 +140,7 @@
   // Activating the service again should not change anything.
   fixture.changed_prefs()->Clear();
   service_.SetActive(true);
-  EXPECT_EQ(0u, fixture.changed_prefs()->size());
+  EXPECT_EQ(0u, fixture.changed_prefs()->DictSize());
 
   // kSupervisedModeManualHosts can be configured by the custodian.
   base::Value hosts(base::Value::Type::DICTIONARY);
@@ -148,7 +148,7 @@
   hosts.SetBoolKey("moose.org", false);
   service_.SetLocalSetting(supervised_users::kContentPackManualBehaviorHosts,
                            std::make_unique<base::Value>(hosts.Clone()));
-  EXPECT_EQ(1u, fixture.changed_prefs()->size());
+  EXPECT_EQ(1u, fixture.changed_prefs()->DictSize());
   ASSERT_TRUE(fixture.changed_prefs()->GetDictionary(
       prefs::kSupervisedUserManualHosts, &manual_hosts));
   EXPECT_TRUE(*manual_hosts == hosts);
@@ -158,7 +158,7 @@
   fixture.changed_prefs()->Clear();
   service_.SetLocalSetting(supervised_users::kForceSafeSearch,
                            std::make_unique<base::Value>(false));
-  EXPECT_EQ(1u, fixture.changed_prefs()->size());
+  EXPECT_EQ(1u, fixture.changed_prefs()->DictSize());
   EXPECT_TRUE(fixture.changed_prefs()->GetBoolean(prefs::kForceGoogleSafeSearch,
                                                   &force_google_safesearch));
   EXPECT_TRUE(fixture.changed_prefs()->GetInteger(prefs::kForceYouTubeRestrict,
@@ -179,7 +179,7 @@
   fixture.changed_prefs()->Clear();
   service_.SetLocalSetting(supervised_users::kGeolocationDisabled,
                            std::make_unique<base::Value>(false));
-  EXPECT_EQ(1u, fixture.changed_prefs()->size());
+  EXPECT_EQ(1u, fixture.changed_prefs()->DictSize());
   EXPECT_TRUE(fixture.changed_prefs()->GetBoolean(
       prefs::kSupervisedUserExtensionsMayRequestPermissions,
       &extensions_may_request_permissions));
@@ -193,7 +193,7 @@
   fixture.changed_prefs()->Clear();
   service_.SetLocalSetting(supervised_users::kGeolocationDisabled,
                            std::make_unique<base::Value>(true));
-  EXPECT_EQ(1u, fixture.changed_prefs()->size());
+  EXPECT_EQ(1u, fixture.changed_prefs()->DictSize());
   EXPECT_TRUE(fixture.changed_prefs()->GetBoolean(
       prefs::kSupervisedUserExtensionsMayRequestPermissions,
       &extensions_may_request_permissions));
@@ -213,11 +213,11 @@
 
   service_.SetActive(true);
   EXPECT_FALSE(fixture.initialization_completed());
-  EXPECT_EQ(0u, fixture.changed_prefs()->size());
+  EXPECT_EQ(0u, fixture.changed_prefs()->DictSize());
 
   pref_store_->SetInitializationCompleted();
   EXPECT_TRUE(fixture.initialization_completed());
-  EXPECT_EQ(0u, fixture.changed_prefs()->size());
+  EXPECT_EQ(0u, fixture.changed_prefs()->DictSize());
 }
 
 TEST_F(SupervisedUserPrefStoreTest, CreatePrefStoreAfterInitialization) {
@@ -226,5 +226,5 @@
 
   SupervisedUserPrefStoreFixture fixture(&service_);
   EXPECT_TRUE(fixture.initialization_completed());
-  EXPECT_EQ(0u, fixture.changed_prefs()->size());
+  EXPECT_EQ(0u, fixture.changed_prefs()->DictSize());
 }
diff --git a/chrome/browser/supervised_user/supervised_user_settings_service.cc b/chrome/browser/supervised_user/supervised_user_settings_service.cc
index 0182b5e..d23fafd 100644
--- a/chrome/browser/supervised_user/supervised_user_settings_service.cc
+++ b/chrome/browser/supervised_user/supervised_user_settings_service.cc
@@ -314,7 +314,7 @@
           MakeSplitSettingKey(it.key(), jt.key()), jt.value()));
     }
   }
-  DCHECK_EQ(0u, GetQueuedItems()->size());
+  DCHECK_EQ(0u, GetQueuedItems()->DictSize());
   return data;
 }
 
diff --git a/chrome/browser/sync/test/integration/sync_test.cc b/chrome/browser/sync/test/integration/sync_test.cc
index e3101ff..420d8b03 100644
--- a/chrome/browser/sync/test/integration/sync_test.cc
+++ b/chrome/browser/sync/test/integration/sync_test.cc
@@ -70,7 +70,6 @@
 #include "components/sync/engine/sync_scheduler_impl.h"
 #include "components/sync/invalidations/switches.h"
 #include "components/sync/invalidations/sync_invalidations_service_impl.h"
-#include "components/sync/protocol/sync.pb.h"
 #include "components/sync/test/fake_server/fake_server_network_resources.h"
 #include "content/public/browser/navigation_entry.h"
 #include "content/public/browser/network_service_instance.h"
diff --git a/chrome/browser/ui/BUILD.gn b/chrome/browser/ui/BUILD.gn
index db6a8693..c1afda9e 100644
--- a/chrome/browser/ui/BUILD.gn
+++ b/chrome/browser/ui/BUILD.gn
@@ -1842,10 +1842,13 @@
       "app_list/search/score_normalizer/balanced_reservoir.h",
       "app_list/search/score_normalizer/score_normalizer.cc",
       "app_list/search/score_normalizer/score_normalizer.h",
-      "app_list/search/search_controller.cc",
       "app_list/search/search_controller.h",
       "app_list/search/search_controller_factory.cc",
       "app_list/search/search_controller_factory.h",
+      "app_list/search/search_controller_impl.cc",
+      "app_list/search/search_controller_impl.h",
+      "app_list/search/search_controller_impl_new.cc",
+      "app_list/search/search_controller_impl_new.h",
       "app_list/search/search_metrics_observer.cc",
       "app_list/search/search_metrics_observer.h",
       "app_list/search/search_provider.cc",
@@ -2574,8 +2577,6 @@
       "webui/settings/chromeos/multidevice_handler.h",
       "webui/settings/chromeos/multidevice_section.cc",
       "webui/settings/chromeos/multidevice_section.h",
-      "webui/settings/chromeos/on_startup_section.cc",
-      "webui/settings/chromeos/on_startup_section.h",
       "webui/settings/chromeos/os_settings_features_util.cc",
       "webui/settings/chromeos/os_settings_features_util.h",
       "webui/settings/chromeos/os_settings_identifier.h",
diff --git a/chrome/browser/ui/app_list/search/chrome_search_result.h b/chrome/browser/ui/app_list/search/chrome_search_result.h
index eaf39c2..8944613 100644
--- a/chrome/browser/ui/app_list/search/chrome_search_result.h
+++ b/chrome/browser/ui/app_list/search/chrome_search_result.h
@@ -159,18 +159,9 @@
   static std::string TagsDebugStringForTest(const std::string& text,
                                             const Tags& tags);
 
-  // Subtype of a search result. -1 means no sub type. Derived classes
-  // can set this in their metadata to return useful values for rankers etc.
-  // Note set_result_subtype() does not call into ModelUpdater so changing the
-  // subtype after construction is not reflected in ash.
-  int result_subtype() const { return metadata_->result_subtype; }
-
  protected:
   // These id setters should be called in derived class constructors only.
   void set_id(const std::string& id) { metadata_->id = id; }
-  void set_result_subtype(int result_subtype) {
-    metadata_->result_subtype = result_subtype;
-  }
 
   // Get the context menu of a certain search result. This could be different
   // for different kinds of items.
diff --git a/chrome/browser/ui/app_list/search/help_app_provider.cc b/chrome/browser/ui/app_list/search/help_app_provider.cc
index 32bfefa..9adb5ba 100644
--- a/chrome/browser/ui/app_list/search/help_app_provider.cc
+++ b/chrome/browser/ui/app_list/search/help_app_provider.cc
@@ -153,7 +153,6 @@
   SetMetricsType(ash::HELP_APP);
   SetIcon(icon);
   SetDetails(result->main_category);
-  SetDetailsTags(CalculateTags(query, result->main_category));
 }
 
 HelpAppResult::~HelpAppResult() = default;
diff --git a/chrome/browser/ui/app_list/search/omnibox_result.cc b/chrome/browser/ui/app_list/search/omnibox_result.cc
index 3d63c32f..ff2596c 100644
--- a/chrome/browser/ui/app_list/search/omnibox_result.cc
+++ b/chrome/browser/ui/app_list/search/omnibox_result.cc
@@ -213,14 +213,11 @@
   set_id(match_.stripped_destination_url.spec());
   SetDisplayType(DisplayType::kList);
   SetResultType(ResultType::kOmnibox);
-  set_result_subtype(static_cast<int>(match_.type));
   SetMetricsType(GetSearchResultType());
 
   if (app_list_features::IsOmniboxRichEntitiesEnabled()) {
     if (match_.answer.has_value()) {
       SetOmniboxType(OmniboxType::kAnswer);
-      // The answer subtype overrides the match subtype.
-      set_result_subtype(static_cast<int>(match_.answer->type()));
     } else if (match_.type == AutocompleteMatchType::CALCULATOR) {
       SetOmniboxType(OmniboxType::kCalculatorAnswer);
     } else if (!match_.image_url.is_empty()) {
@@ -228,11 +225,9 @@
     }
 
     // The stripped destination URL is no longer a unique identifier, so append
-    // result types.
-    // TODO(crbug.com/1130372): Consider generating a random unique ID instead.
+    // it to the omnibox type.
     const std::string id = base::JoinString(
         {base::NumberToString(static_cast<int>(omnibox_type())),
-         base::NumberToString(static_cast<int>(result_subtype())),
          match_.stripped_destination_url.spec()},
         "-");
     set_id(id);
@@ -350,15 +345,11 @@
 }
 
 void OmniboxResult::UpdateIcon() {
-  // TODO(crbug.com/1201151): Refactor this method.
-
-  if (app_list_features::IsOmniboxRichEntitiesEnabled() &&
-      IsRichEntityResult()) {
-    // Determine if we have a local icon. Calculator and non-weather answer
-    // results have local icons.
-    if (match_.type == AutocompleteMatchType::CALCULATOR) {
+  switch (omnibox_type()) {
+    case OmniboxType::kCalculatorAnswer:
       SetIcon(CreateAnswerIcon(omnibox::kCalculatorIcon));
-    } else if (match_.answer) {
+      return;
+    case OmniboxType::kAnswer:
       if (match_.answer->type() == SuggestionAnswer::ANSWER_TYPE_WEATHER &&
           !match_.answer->image_url().is_empty()) {
         // Weather icons are downloaded. Check this first so that the local
@@ -367,40 +358,43 @@
       } else {
         SetIcon(CreateAnswerIcon(TypeToAnswerIcon(match_.answer->type())));
       }
-    } else if (!match_.image_url.is_empty()) {
-      // All remaining rich entity icons will have their image downloaded.
-      FetchRichEntityImage(match_.image_url);
-    }
-    return;
-  }
-
-  // Use a favicon if eligible. If the result should have a favicon but there
-  // isn't one in the cache, fall through to using a generic icon instead.
-  if (favicon_cache_ && MatchTypeToIconType(match_.type) == IconType::kDomain) {
-    const auto icon = favicon_cache_->GetFaviconForPageUrl(
-        match_.destination_url, base::BindOnce(&OmniboxResult::OnFaviconFetched,
-                                               weak_factory_.GetWeakPtr()));
-    if (!icon.IsEmpty()) {
-      SetOmniboxType(OmniboxType::kFavicon);
-      SetIcon(icon.AsImageSkia());
       return;
-    }
-  }
+    case OmniboxType::kRichImage:
+      FetchRichEntityImage(match_.image_url);
+      return;
+    default:
+      // Use a favicon if eligible. If the result should have a favicon but
+      // there isn't one in the cache, fall through to using a generic icon
+      // instead.
+      if (favicon_cache_ &&
+          MatchTypeToIconType(match_.type) == IconType::kDomain) {
+        const auto icon = favicon_cache_->GetFaviconForPageUrl(
+            match_.destination_url,
+            base::BindOnce(&OmniboxResult::OnFaviconFetched,
+                           weak_factory_.GetWeakPtr()));
+        if (!icon.IsEmpty()) {
+          SetOmniboxType(OmniboxType::kFavicon);
+          SetIcon(icon.AsImageSkia());
+          return;
+        }
+      }
 
-  // If this is neither a rich entity nor eligible for a favicon, use either the
-  // generic bookmark or another generic icon as appropriate.
-  BookmarkModel* bookmark_model =
-      BookmarkModelFactory::GetForBrowserContext(profile_);
-  if (bookmark_model && bookmark_model->IsBookmarked(match_.destination_url)) {
-    SetIcon(gfx::CreateVectorIcon(
-        omnibox::kBookmarkIcon,
-        ash::SharedAppListConfig::instance().search_list_icon_dimension(),
-        kListIconColor));
-  } else {
-    SetIcon(gfx::CreateVectorIcon(
-        TypeToVectorIcon(match_.type),
-        ash::SharedAppListConfig::instance().search_list_icon_dimension(),
-        kListIconColor));
+      // If this is neither a rich entity nor eligible for a favicon, use either
+      // the generic bookmark or another generic icon as appropriate.
+      BookmarkModel* bookmark_model =
+          BookmarkModelFactory::GetForBrowserContext(profile_);
+      if (bookmark_model &&
+          bookmark_model->IsBookmarked(match_.destination_url)) {
+        SetIcon(gfx::CreateVectorIcon(
+            omnibox::kBookmarkIcon,
+            ash::SharedAppListConfig::instance().search_list_icon_dimension(),
+            kListIconColor));
+      } else {
+        SetIcon(gfx::CreateVectorIcon(
+            TypeToVectorIcon(match_.type),
+            ash::SharedAppListConfig::instance().search_list_icon_dimension(),
+            kListIconColor));
+      }
   }
 }
 
@@ -465,11 +459,6 @@
          !match_.description.empty();
 }
 
-bool OmniboxResult::IsRichEntityResult() const {
-  return match_.type == AutocompleteMatchType::CALCULATOR || match_.answer ||
-         !match_.image_url.is_empty();
-}
-
 void OmniboxResult::FetchRichEntityImage(const GURL& url) {
   if (!bitmap_fetcher_) {
     bitmap_fetcher_ =
diff --git a/chrome/browser/ui/app_list/search/omnibox_result.h b/chrome/browser/ui/app_list/search/omnibox_result.h
index 6e95902..7925593 100644
--- a/chrome/browser/ui/app_list/search/omnibox_result.h
+++ b/chrome/browser/ui/app_list/search/omnibox_result.h
@@ -65,7 +65,6 @@
   // description.
   bool IsUrlResultWithDescription() const;
 
-  bool IsRichEntityResult() const;
   void FetchRichEntityImage(const GURL& url);
 
   void OnFaviconFetched(const gfx::Image& icon);
diff --git a/chrome/browser/ui/app_list/search/search_controller.h b/chrome/browser/ui/app_list/search/search_controller.h
index a3b5998..20a17fde 100644
--- a/chrome/browser/ui/app_list/search/search_controller.h
+++ b/chrome/browser/ui/app_list/search/search_controller.h
@@ -18,129 +18,94 @@
 #include "chrome/browser/ui/app_list/search/mixer.h"
 #include "chrome/browser/ui/app_list/search/search_result_ranker/app_launch_data.h"
 
-class AppListControllerDelegate;
-class AppListModelUpdater;
 class ChromeSearchResult;
-class Profile;
 
 namespace ash {
-class AppListNotifier;
 enum class AppListSearchResultType;
 }
 
 namespace app_list {
 
-class SearchMetricsObserver;
 class SearchProvider;
-class RankerDelegate;
 enum class RankingItemType;
 
+// Common types used throughout result ranking.
+
+// The type of a particular result.
+using ResultType = ash::AppListSearchResultType;
+// The type of a search provider as a whole. This is currently just the 'main'
+// ResultType returned by the provider.
+using ProviderType = ash::AppListSearchResultType;
+
+using Results = std::vector<std::unique_ptr<ChromeSearchResult>>;
+using ResultsMap = base::flat_map<ProviderType, Results>;
+
 // Controller that collects query from given SearchBoxModel, dispatches it
 // to all search providers, then invokes the mixer to mix and to publish the
 // results to the given SearchResults UI model.
+//
+// // TODO(crbug.com/1199206): The SearchController is being reimplemented with
+// a different ranking system. Once this reimplementation is finished, this pure
+// virtual class can be removed and replaced with SearchControllerImplNew.
 class SearchController {
  public:
   using ResultsChangedCallback =
       base::RepeatingCallback<void(ash::AppListSearchResultType)>;
+  virtual ~SearchController() {}
 
-  using ProviderType = ash::AppListSearchResultType;
-  using ResultType = ash::AppListSearchResultType;
+  virtual void InitializeRankers() = 0;
 
-  using Results = std::vector<std::unique_ptr<ChromeSearchResult>>;
-  using ResultsMap = base::flat_map<ProviderType, Results>;
+  virtual void Start(const std::u16string& query) = 0;
+  // TODO(crbug.com/1199206): We should rename this to AppListClosing for
+  // consistency with AppListShown.
+  virtual void ViewClosing() = 0;
 
-  SearchController(AppListModelUpdater* model_updater,
-                   AppListControllerDelegate* list_controller,
-                   ash::AppListNotifier* notifier,
-                   Profile* profile);
-  virtual ~SearchController();
-
-  SearchController(const SearchController&) = delete;
-  SearchController& operator=(const SearchController&) = delete;
-
-  void InitializeRankers();
-
-  void Start(const std::u16string& query);
-  void ViewClosing();
-
-  void OpenResult(ChromeSearchResult* result, int event_flags);
-  void InvokeResultAction(ChromeSearchResult* result, int action_index);
+  virtual void OpenResult(ChromeSearchResult* result, int event_flags) = 0;
+  virtual void InvokeResultAction(ChromeSearchResult* result,
+                                  int action_index) = 0;
 
   // Adds a new mixer group. See Mixer::AddGroup.
-  size_t AddGroup(size_t max_results);
+  virtual size_t AddGroup(size_t max_results) = 0;
 
   // Takes ownership of |provider| and associates it with given mixer group.
-  void AddProvider(size_t group_id, std::unique_ptr<SearchProvider> provider);
+  virtual void AddProvider(size_t group_id,
+                           std::unique_ptr<SearchProvider> provider) = 0;
 
   // Update the controller with the given results. Used only if the categorical
   // search feature flag is enabled.
-  void SetResults(ash::AppListSearchResultType provider_type, Results results);
+  virtual void SetResults(ash::AppListSearchResultType provider_type,
+                          Results results) = 0;
 
-  virtual ChromeSearchResult* FindSearchResult(const std::string& result_id);
-  ChromeSearchResult* GetResultByTitleForTest(const std::string& title);
+  virtual ChromeSearchResult* FindSearchResult(
+      const std::string& result_id) = 0;
+  virtual ChromeSearchResult* GetResultByTitleForTest(
+      const std::string& title) = 0;
 
   // Sends training signal to each |providers_|
-  void Train(AppLaunchData&& app_launch_data);
+  virtual void Train(AppLaunchData&& app_launch_data) = 0;
 
   // Invoked when the app list is shown.
-  void AppListShown();
+  virtual void AppListShown() = 0;
 
   // Gets the length of the most recent query.
-  int GetLastQueryLength() const;
+  // TODO(crbug.com/1199206): This should be replaced with calls to
+  // get_query().size().
+  virtual int GetLastQueryLength() const = 0;
 
+  // TODO(crbug.com/1199206): This is unused and can be deleted.
   // Called when items in the results list have been on screen for some amount
   // of time, or the user clicked a search result.
-  void OnSearchResultsImpressionMade(
+  virtual void OnSearchResultsImpressionMade(
       const std::u16string& trimmed_query,
       const ash::SearchResultIdWithPositionIndices& results,
-      int launched_index);
+      int launched_index) = 0;
 
-  void set_results_changed_callback_for_test(ResultsChangedCallback callback) {
-    results_changed_callback_ = std::move(callback);
-  }
+  virtual std::u16string get_query() = 0;
 
- private:
-  // Invoked when the search results are changed. Providers should use the one
-  // argument version, and pass the primary type of result produced by the
-  // invoking search provider.
-  void OnResultsChanged();
-  void OnResultsChangedWithType(ash::AppListSearchResultType result_type);
+  virtual base::Time session_start() = 0;
 
-  Profile* profile_;
-
-  bool dispatching_query_ = false;
-
-  // If true, the search results are shown on the launcher start page.
-  bool query_for_recommendation_ = false;
-
-  // The query associated with the most recent search.
-  std::u16string last_query_;
-
-  // The ID of the most recently launched app. This is used for app list launch
-  // recording.
-  std::string last_launched_app_id_;
-
-  // If set, called when OnResultsChanged is invoked.
-  ResultsChangedCallback results_changed_callback_;
-
-  // TODO(crbug/1199206): As part of the prototyping for category-based search,
-  // the behaviour of this class is significantly changed depending on whether
-  // the CategoricalSearch feature flag is enabled. Eventually, we intend to
-  // remove the Mixer class entirely.
-
-  // Top-level result ranker. Replaces the Mixer if the categorical search flag
-  // is enabled.
-  std::unique_ptr<RankerDelegate> ranker_;
-
-  // Storage for all search results for the current query. Only used when
-  // categorical search is enabled.
-  ResultsMap results_;
-
-  std::unique_ptr<Mixer> mixer_;
-  std::unique_ptr<SearchMetricsObserver> metrics_observer_;
-  using Providers = std::vector<std::unique_ptr<SearchProvider>>;
-  Providers providers_;
-  AppListControllerDelegate* list_controller_;
+  virtual void set_results_changed_callback_for_test(
+      ResultsChangedCallback callback) = 0;
 };
 
 }  // namespace app_list
diff --git a/chrome/browser/ui/app_list/search/search_controller_factory.cc b/chrome/browser/ui/app_list/search/search_controller_factory.cc
index abd1cf2..c1100cf 100644
--- a/chrome/browser/ui/app_list/search/search_controller_factory.cc
+++ b/chrome/browser/ui/app_list/search/search_controller_factory.cc
@@ -31,6 +31,8 @@
 #include "chrome/browser/ui/app_list/search/omnibox_provider.h"
 #include "chrome/browser/ui/app_list/search/os_settings_provider.h"
 #include "chrome/browser/ui/app_list/search/search_controller.h"
+#include "chrome/browser/ui/app_list/search/search_controller_impl.h"
+#include "chrome/browser/ui/app_list/search/search_controller_impl_new.h"
 #include "chrome/common/chrome_features.h"
 #include "chrome/common/chrome_switches.h"
 #include "chromeos/services/assistant/public/cpp/features.h"
@@ -91,9 +93,18 @@
     AppListModelUpdater* model_updater,
     AppListControllerDelegate* list_controller,
     ash::AppListNotifier* notifier) {
-  std::unique_ptr<SearchController> controller =
-      std::make_unique<SearchController>(model_updater, list_controller,
-                                         notifier, profile);
+  // TODO(crbug.com/1199206): We are prototyping new ranking, which reimplements
+  // the SearchController. Once we migrate to this new ranking, the following
+  // check can be removed and replaced by just creating a
+  // SearchControllerImplNew.
+  std::unique_ptr<SearchController> controller;
+  if (app_list_features::IsCategoricalSearchEnabled()) {
+    controller = std::make_unique<SearchControllerImplNew>(
+        model_updater, list_controller, notifier, profile);
+  } else {
+    controller = std::make_unique<SearchControllerImpl>(
+        model_updater, list_controller, notifier, profile);
+  }
 
   // Set up rankers for search results.
   controller->InitializeRankers();
diff --git a/chrome/browser/ui/app_list/search/search_controller.cc b/chrome/browser/ui/app_list/search/search_controller_impl.cc
similarity index 68%
rename from chrome/browser/ui/app_list/search/search_controller.cc
rename to chrome/browser/ui/app_list/search/search_controller_impl.cc
index 289622d7..23e4e52 100644
--- a/chrome/browser/ui/app_list/search/search_controller.cc
+++ b/chrome/browser/ui/app_list/search/search_controller_impl.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/ui/app_list/search/search_controller.h"
+#include "chrome/browser/ui/app_list/search/search_controller_impl.h"
 
 #include <algorithm>
 
@@ -19,12 +19,12 @@
 #include "base/strings/strcat.h"
 #include "base/strings/string_util.h"
 #include "base/strings/utf_string_conversions.h"
+#include "base/time/time.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/app_list/app_list_controller_delegate.h"
 #include "chrome/browser/ui/app_list/app_list_model_updater.h"
 #include "chrome/browser/ui/app_list/search/chrome_search_result.h"
 #include "chrome/browser/ui/app_list/search/cros_action_history/cros_action_recorder.h"
-#include "chrome/browser/ui/app_list/search/ranking/ranker_delegate.h"
 #include "chrome/browser/ui/app_list/search/search_metrics_observer.h"
 #include "chrome/browser/ui/app_list/search/search_provider.h"
 #include "chrome/browser/ui/app_list/search/search_result_ranker/chip_ranker.h"
@@ -71,33 +71,26 @@
 
 }  // namespace
 
-SearchController::SearchController(AppListModelUpdater* model_updater,
-                                   AppListControllerDelegate* list_controller,
-                                   ash::AppListNotifier* notifier,
-                                   Profile* profile)
+SearchControllerImpl::SearchControllerImpl(
+    AppListModelUpdater* model_updater,
+    AppListControllerDelegate* list_controller,
+    ash::AppListNotifier* notifier,
+    Profile* profile)
     : profile_(profile),
+      mixer_(std::make_unique<Mixer>(model_updater)),
       metrics_observer_(std::make_unique<SearchMetricsObserver>(notifier)),
       list_controller_(list_controller) {
-  if (app_list_features::IsCategoricalSearchEnabled()) {
-    ranker_ = std::make_unique<RankerDelegate>(profile, model_updater, this);
-    mixer_ = nullptr;
-  } else {
-    mixer_ = std::make_unique<Mixer>(model_updater);
-    ranker_ = nullptr;
-  }
+  DCHECK(!app_list_features::IsCategoricalSearchEnabled());
 }
 
-SearchController::~SearchController() {}
+SearchControllerImpl::~SearchControllerImpl() {}
 
-void SearchController::InitializeRankers() {
-  if (ranker_) {
-    // TODO(crbug.com/1199206): Implement.
-  } else {
-    mixer_->InitializeRankers(profile_, this);
-  }
+void SearchControllerImpl::InitializeRankers() {
+  mixer_->InitializeRankers(profile_, this);
 }
 
-void SearchController::Start(const std::u16string& query) {
+void SearchControllerImpl::Start(const std::u16string& query) {
+  session_start_ = base::Time::Now();
   dispatching_query_ = true;
   ash::RecordLauncherIssuedSearchQueryLength(query.length());
   if (query.length() > 0) {
@@ -117,12 +110,13 @@
   OnResultsChanged();
 }
 
-void SearchController::ViewClosing() {
+void SearchControllerImpl::ViewClosing() {
   for (const auto& provider : providers_)
     provider->ViewClosing();
 }
 
-void SearchController::OpenResult(ChromeSearchResult* result, int event_flags) {
+void SearchControllerImpl::OpenResult(ChromeSearchResult* result,
+                                      int event_flags) {
   // This can happen in certain circumstances due to races. See
   // https://crbug.com/534772
   if (!result)
@@ -144,65 +138,42 @@
   }
 }
 
-void SearchController::InvokeResultAction(ChromeSearchResult* result,
-                                          int action_index) {
+void SearchControllerImpl::InvokeResultAction(ChromeSearchResult* result,
+                                              int action_index) {
   // TODO(xiyuan): Hook up with user learning.
   result->InvokeAction(action_index);
 }
 
-size_t SearchController::AddGroup(size_t max_results) {
-  if (ranker_) {
-    return 0ul;
-  } else {
-    return mixer_->AddGroup(max_results);
-  }
+size_t SearchControllerImpl::AddGroup(size_t max_results) {
+  return mixer_->AddGroup(max_results);
 }
 
-void SearchController::AddProvider(size_t group_id,
-                                   std::unique_ptr<SearchProvider> provider) {
-  if (ranker_) {
-    provider->set_controller(this);
-  } else {
-    mixer_->AddProviderToGroup(group_id, provider.get());
-    provider->set_controller(this);
-    provider->set_result_changed_callback(
-        base::BindRepeating(&SearchController::OnResultsChangedWithType,
-                            base::Unretained(this), provider->ResultType()));
-  }
+void SearchControllerImpl::AddProvider(
+    size_t group_id,
+    std::unique_ptr<SearchProvider> provider) {
+  mixer_->AddProviderToGroup(group_id, provider.get());
+  provider->set_controller(this);
+  provider->set_result_changed_callback(
+      base::BindRepeating(&SearchControllerImpl::OnResultsChangedWithType,
+                          base::Unretained(this), provider->ResultType()));
   providers_.emplace_back(std::move(provider));
 }
 
-void SearchController::SetResults(
+void SearchControllerImpl::SetResults(
     const ash::AppListSearchResultType provider_type,
     Results results) {
-  DCHECK(ranker_);
-
-  auto ui_thread = content::GetUIThreadTaskRunner({});
-  if (!ui_thread->RunsTasksInCurrentSequence()) {
-    ui_thread->PostTask(
-        FROM_HERE,
-        base::BindOnce(&SearchController::SetResults, base::Unretained(this),
-                       provider_type, std::move(results)));
-    return;
-  }
-
-  results_[provider_type] = std::move(results);
+  // Should only be called when IsCategoricalSearchEnabled is true.
+  NOTREACHED();
 }
 
-void SearchController::OnResultsChangedWithType(
+void SearchControllerImpl::OnResultsChangedWithType(
     ash::AppListSearchResultType result_type) {
-  if (!mixer_)
-    return;
-
   OnResultsChanged();
   if (results_changed_callback_)
     results_changed_callback_.Run(result_type);
 }
 
-void SearchController::OnResultsChanged() {
-  if (!mixer_)
-    return;
-
+void SearchControllerImpl::OnResultsChanged() {
   if (dispatching_query_)
     return;
 
@@ -213,7 +184,7 @@
   mixer_->MixAndPublish(num_max_results, last_query_);
 }
 
-ChromeSearchResult* SearchController::FindSearchResult(
+ChromeSearchResult* SearchControllerImpl::FindSearchResult(
     const std::string& result_id) {
   for (const auto& provider : providers_) {
     for (const auto& result : provider->results()) {
@@ -224,7 +195,7 @@
   return nullptr;
 }
 
-void SearchController::OnSearchResultsImpressionMade(
+void SearchControllerImpl::OnSearchResultsImpressionMade(
     const std::u16string& trimmed_query,
     const ash::SearchResultIdWithPositionIndices& results,
     int launched_index) {
@@ -242,41 +213,27 @@
   }
 }
 
-ChromeSearchResult* SearchController::GetResultByTitleForTest(
+ChromeSearchResult* SearchControllerImpl::GetResultByTitleForTest(
     const std::string& title) {
   std::u16string target_title = base::ASCIIToUTF16(title);
-  if (ranker_) {
-    for (const auto& provider_results : results_) {
-      for (const auto& result : provider_results.second) {
-        if (result->title() == target_title &&
-            result->result_type() ==
-                ash::AppListSearchResultType::kInstalledApp &&
-            !result->is_recommendation()) {
-          return result.get();
-        }
+  for (const auto& provider : providers_) {
+    for (const auto& result : provider->results()) {
+      if (result->title() == target_title &&
+          result->result_type() ==
+              ash::AppListSearchResultType::kInstalledApp &&
+          !result->is_recommendation()) {
+        return result.get();
       }
     }
-    return nullptr;
-  } else {
-    for (const auto& provider : providers_) {
-      for (const auto& result : provider->results()) {
-        if (result->title() == target_title &&
-            result->result_type() ==
-                ash::AppListSearchResultType::kInstalledApp &&
-            !result->is_recommendation()) {
-          return result.get();
-        }
-      }
-    }
-    return nullptr;
   }
+  return nullptr;
 }
 
-int SearchController::GetLastQueryLength() const {
+int SearchControllerImpl::GetLastQueryLength() const {
   return last_query_.size();
 }
 
-void SearchController::Train(AppLaunchData&& app_launch_data) {
+void SearchControllerImpl::Train(AppLaunchData&& app_launch_data) {
   app_launch_data.query = base::UTF16ToUTF8(last_query_);
 
   if (app_list_features::IsAppListLaunchRecordingEnabled()) {
@@ -318,16 +275,25 @@
                      base::HashMetricName(base::UTF16ToUTF8(last_query_)))}});
 
   // Train all search result ranking models.
-  if (ranker_) {
-    // TODO(crbug.com/1199206): Implement.
-  } else {
-    mixer_->Train(app_launch_data);
-  }
+  mixer_->Train(app_launch_data);
 }
 
-void SearchController::AppListShown() {
+void SearchControllerImpl::AppListShown() {
   for (const auto& provider : providers_)
     provider->AppListShown();
 }
 
+std::u16string SearchControllerImpl::get_query() {
+  return last_query_;
+}
+
+base::Time SearchControllerImpl::session_start() {
+  return session_start_;
+}
+
+void SearchControllerImpl::set_results_changed_callback_for_test(
+    ResultsChangedCallback callback) {
+  results_changed_callback_ = std::move(callback);
+}
+
 }  // namespace app_list
diff --git a/chrome/browser/ui/app_list/search/search_controller_impl.h b/chrome/browser/ui/app_list/search/search_controller_impl.h
new file mode 100644
index 0000000..26c7f9f
--- /dev/null
+++ b/chrome/browser/ui/app_list/search/search_controller_impl.h
@@ -0,0 +1,122 @@
+// 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 CHROME_BROWSER_UI_APP_LIST_SEARCH_SEARCH_CONTROLLER_IMPL_H_
+#define CHROME_BROWSER_UI_APP_LIST_SEARCH_SEARCH_CONTROLLER_IMPL_H_
+
+#include <stddef.h>
+
+#include <memory>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "base/callback.h"
+#include "base/containers/flat_map.h"
+#include "base/macros.h"
+#include "chrome/browser/ui/app_list/search/mixer.h"
+#include "chrome/browser/ui/app_list/search/search_controller.h"
+#include "chrome/browser/ui/app_list/search/search_result_ranker/app_launch_data.h"
+
+class AppListControllerDelegate;
+class AppListModelUpdater;
+class ChromeSearchResult;
+class Profile;
+
+namespace ash {
+class AppListNotifier;
+enum class AppListSearchResultType;
+}  // namespace ash
+
+namespace app_list {
+
+class SearchMetricsObserver;
+class SearchProvider;
+enum class RankingItemType;
+
+// TODO(crbug.com/1199206): This is the old implementation of the search
+// controller. Once we have fully migrated to the new system, this can be
+// cleaned up.
+class SearchControllerImpl : public SearchController {
+ public:
+  using ResultsChangedCallback =
+      base::RepeatingCallback<void(ash::AppListSearchResultType)>;
+
+  SearchControllerImpl(AppListModelUpdater* model_updater,
+                       AppListControllerDelegate* list_controller,
+                       ash::AppListNotifier* notifier,
+                       Profile* profile);
+  ~SearchControllerImpl() override;
+
+  SearchControllerImpl(const SearchControllerImpl&) = delete;
+  SearchControllerImpl& operator=(const SearchControllerImpl&) = delete;
+
+  // SearchController:
+  void InitializeRankers() override;
+  void Start(const std::u16string& query) override;
+  void ViewClosing() override;
+  void OpenResult(ChromeSearchResult* result, int event_flags) override;
+  void InvokeResultAction(ChromeSearchResult* result,
+                          int action_index) override;
+  size_t AddGroup(size_t max_results) override;
+  void AddProvider(size_t group_id,
+                   std::unique_ptr<SearchProvider> provider) override;
+  void SetResults(ash::AppListSearchResultType provider_type,
+                  Results results) override;
+  ChromeSearchResult* FindSearchResult(const std::string& result_id) override;
+  ChromeSearchResult* GetResultByTitleForTest(
+      const std::string& title) override;
+  void Train(AppLaunchData&& app_launch_data) override;
+  void AppListShown() override;
+  int GetLastQueryLength() const override;
+  void OnSearchResultsImpressionMade(
+      const std::u16string& trimmed_query,
+      const ash::SearchResultIdWithPositionIndices& results,
+      int launched_index) override;
+  std::u16string get_query() override;
+  base::Time session_start() override;
+  void set_results_changed_callback_for_test(
+      ResultsChangedCallback callback) override;
+
+ private:
+  // Invoked when the search results are changed. Providers should use the one
+  // argument version, and pass the primary type of result produced by the
+  // invoking search provider.
+  void OnResultsChanged();
+  void OnResultsChangedWithType(ash::AppListSearchResultType result_type);
+
+  Profile* profile_;
+
+  bool dispatching_query_ = false;
+
+  // If true, the search results are shown on the launcher start page.
+  bool query_for_recommendation_ = false;
+
+  // The query associated with the most recent search.
+  std::u16string last_query_;
+
+  // The time when Start was most recently called.
+  base::Time session_start_;
+
+  // The ID of the most recently launched app. This is used for app list launch
+  // recording.
+  std::string last_launched_app_id_;
+
+  // If set, called when OnResultsChanged is invoked.
+  ResultsChangedCallback results_changed_callback_;
+
+  // Storage for all search results for the current query. Only used when
+  // categorical search is enabled.
+  ResultsMap results_;
+
+  std::unique_ptr<Mixer> mixer_;
+  std::unique_ptr<SearchMetricsObserver> metrics_observer_;
+  using Providers = std::vector<std::unique_ptr<SearchProvider>>;
+  Providers providers_;
+  AppListControllerDelegate* list_controller_;
+};
+
+}  // namespace app_list
+
+#endif  // CHROME_BROWSER_UI_APP_LIST_SEARCH_SEARCH_CONTROLLER_IMPL_H_
diff --git a/chrome/browser/ui/app_list/search/search_controller.cc b/chrome/browser/ui/app_list/search/search_controller_impl_new.cc
similarity index 64%
copy from chrome/browser/ui/app_list/search/search_controller.cc
copy to chrome/browser/ui/app_list/search/search_controller_impl_new.cc
index 289622d7..f866db6 100644
--- a/chrome/browser/ui/app_list/search/search_controller.cc
+++ b/chrome/browser/ui/app_list/search/search_controller_impl_new.cc
@@ -1,8 +1,8 @@
-// Copyright 2013 The Chromium Authors. All rights reserved.
+// 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/ui/app_list/search/search_controller.h"
+#include "chrome/browser/ui/app_list/search/search_controller_impl_new.h"
 
 #include <algorithm>
 
@@ -19,6 +19,7 @@
 #include "base/strings/strcat.h"
 #include "base/strings/string_util.h"
 #include "base/strings/utf_string_conversions.h"
+#include "base/time/time.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/app_list/app_list_controller_delegate.h"
 #include "chrome/browser/ui/app_list/app_list_model_updater.h"
@@ -71,64 +72,54 @@
 
 }  // namespace
 
-SearchController::SearchController(AppListModelUpdater* model_updater,
-                                   AppListControllerDelegate* list_controller,
-                                   ash::AppListNotifier* notifier,
-                                   Profile* profile)
+SearchControllerImplNew::SearchControllerImplNew(
+    AppListModelUpdater* model_updater,
+    AppListControllerDelegate* list_controller,
+    ash::AppListNotifier* notifier,
+    Profile* profile)
     : profile_(profile),
+      ranker_(std::make_unique<RankerDelegate>(profile, model_updater, this)),
       metrics_observer_(std::make_unique<SearchMetricsObserver>(notifier)),
       list_controller_(list_controller) {
-  if (app_list_features::IsCategoricalSearchEnabled()) {
-    ranker_ = std::make_unique<RankerDelegate>(profile, model_updater, this);
-    mixer_ = nullptr;
-  } else {
-    mixer_ = std::make_unique<Mixer>(model_updater);
-    ranker_ = nullptr;
-  }
+  DCHECK(app_list_features::IsCategoricalSearchEnabled());
 }
 
-SearchController::~SearchController() {}
+SearchControllerImplNew::~SearchControllerImplNew() {}
 
-void SearchController::InitializeRankers() {
-  if (ranker_) {
-    // TODO(crbug.com/1199206): Implement.
-  } else {
-    mixer_->InitializeRankers(profile_, this);
-  }
+void SearchControllerImplNew::InitializeRankers() {
+  // Unused
 }
 
-void SearchController::Start(const std::u16string& query) {
-  dispatching_query_ = true;
+void SearchControllerImplNew::Start(const std::u16string& query) {
+  session_start_ = base::Time::Now();
+
+  // TODO(crbug.com/1199206): We should move this histogram logic somewhere
+  // else.
   ash::RecordLauncherIssuedSearchQueryLength(query.length());
   if (query.length() > 0) {
     const int length_diff = query.length() >= last_query_.length()
                                 ? query.length() - last_query_.length()
                                 : last_query_.length() - query.length();
+
     UMA_HISTOGRAM_BOOLEAN(kLauncherSearchQueryLengthJumped, length_diff > 1);
   }
+
+  last_query_ = query;
   for (const auto& provider : providers_) {
     provider->Start(query);
   }
-
-  dispatching_query_ = false;
-  last_query_ = query;
-  query_for_recommendation_ = query.empty();
-
-  OnResultsChanged();
 }
 
-void SearchController::ViewClosing() {
-  for (const auto& provider : providers_)
-    provider->ViewClosing();
-}
-
-void SearchController::OpenResult(ChromeSearchResult* result, int event_flags) {
+void SearchControllerImplNew::OpenResult(ChromeSearchResult* result,
+                                         int event_flags) {
   // This can happen in certain circumstances due to races. See
   // https://crbug.com/534772
   if (!result)
     return;
 
   // Log the length of the last query that led to the clicked result.
+  // TODO(crbug.com/1199206): This histogram logic should be moved somewhere
+  // else.
   ash::RecordLauncherClickedSearchQueryLength(last_query_.length());
 
   const bool dismiss_view_on_open = result->dismiss_view_on_open();
@@ -144,76 +135,45 @@
   }
 }
 
-void SearchController::InvokeResultAction(ChromeSearchResult* result,
-                                          int action_index) {
-  // TODO(xiyuan): Hook up with user learning.
+void SearchControllerImplNew::InvokeResultAction(ChromeSearchResult* result,
+                                                 int action_index) {
+  if (!result)
+    return;
   result->InvokeAction(action_index);
 }
 
-size_t SearchController::AddGroup(size_t max_results) {
-  if (ranker_) {
-    return 0ul;
-  } else {
-    return mixer_->AddGroup(max_results);
-  }
+size_t SearchControllerImplNew::AddGroup(size_t max_results) {
+  // Unused.
+  return 0ul;
 }
 
-void SearchController::AddProvider(size_t group_id,
-                                   std::unique_ptr<SearchProvider> provider) {
-  if (ranker_) {
-    provider->set_controller(this);
-  } else {
-    mixer_->AddProviderToGroup(group_id, provider.get());
-    provider->set_controller(this);
-    provider->set_result_changed_callback(
-        base::BindRepeating(&SearchController::OnResultsChangedWithType,
-                            base::Unretained(this), provider->ResultType()));
-  }
+void SearchControllerImplNew::AddProvider(
+    size_t group_id,
+    std::unique_ptr<SearchProvider> provider) {
+  provider->set_controller(this);
   providers_.emplace_back(std::move(provider));
 }
 
-void SearchController::SetResults(
+void SearchControllerImplNew::SetResults(
     const ash::AppListSearchResultType provider_type,
     Results results) {
   DCHECK(ranker_);
 
+  // Re-post onto the UI sequence if not called from there.
   auto ui_thread = content::GetUIThreadTaskRunner({});
   if (!ui_thread->RunsTasksInCurrentSequence()) {
-    ui_thread->PostTask(
-        FROM_HERE,
-        base::BindOnce(&SearchController::SetResults, base::Unretained(this),
-                       provider_type, std::move(results)));
+    ui_thread->PostTask(FROM_HERE,
+                        base::BindOnce(&SearchControllerImplNew::SetResults,
+                                       base::Unretained(this), provider_type,
+                                       std::move(results)));
     return;
   }
 
   results_[provider_type] = std::move(results);
+  // TODO(crbug.com/1199206): Implement ranking.
 }
 
-void SearchController::OnResultsChangedWithType(
-    ash::AppListSearchResultType result_type) {
-  if (!mixer_)
-    return;
-
-  OnResultsChanged();
-  if (results_changed_callback_)
-    results_changed_callback_.Run(result_type);
-}
-
-void SearchController::OnResultsChanged() {
-  if (!mixer_)
-    return;
-
-  if (dispatching_query_)
-    return;
-
-  size_t num_max_results =
-      query_for_recommendation_
-          ? ash::SharedAppListConfig::instance().num_start_page_tiles()
-          : ash::SharedAppListConfig::instance().max_search_results();
-  mixer_->MixAndPublish(num_max_results, last_query_);
-}
-
-ChromeSearchResult* SearchController::FindSearchResult(
+ChromeSearchResult* SearchControllerImplNew::FindSearchResult(
     const std::string& result_id) {
   for (const auto& provider : providers_) {
     for (const auto& result : provider->results()) {
@@ -224,15 +184,11 @@
   return nullptr;
 }
 
-void SearchController::OnSearchResultsImpressionMade(
+void SearchControllerImplNew::OnSearchResultsImpressionMade(
     const std::u16string& trimmed_query,
     const ash::SearchResultIdWithPositionIndices& results,
     int launched_index) {
   if (trimmed_query.empty()) {
-    if (mixer_) {
-      mixer_->search_result_ranker()->ZeroStateResultsDisplayed(results);
-    }
-
     // Extract result types for logging.
     std::vector<RankingItemType> result_types;
     for (const auto& result : results) {
@@ -242,43 +198,30 @@
   }
 }
 
-ChromeSearchResult* SearchController::GetResultByTitleForTest(
+ChromeSearchResult* SearchControllerImplNew::GetResultByTitleForTest(
     const std::string& title) {
   std::u16string target_title = base::ASCIIToUTF16(title);
-  if (ranker_) {
-    for (const auto& provider_results : results_) {
-      for (const auto& result : provider_results.second) {
-        if (result->title() == target_title &&
-            result->result_type() ==
-                ash::AppListSearchResultType::kInstalledApp &&
-            !result->is_recommendation()) {
-          return result.get();
-        }
+  for (const auto& provider_results : results_) {
+    for (const auto& result : provider_results.second) {
+      if (result->title() == target_title &&
+          result->result_type() ==
+              ash::AppListSearchResultType::kInstalledApp &&
+          !result->is_recommendation()) {
+        return result.get();
       }
     }
-    return nullptr;
-  } else {
-    for (const auto& provider : providers_) {
-      for (const auto& result : provider->results()) {
-        if (result->title() == target_title &&
-            result->result_type() ==
-                ash::AppListSearchResultType::kInstalledApp &&
-            !result->is_recommendation()) {
-          return result.get();
-        }
-      }
-    }
-    return nullptr;
   }
+  return nullptr;
 }
 
-int SearchController::GetLastQueryLength() const {
+int SearchControllerImplNew::GetLastQueryLength() const {
   return last_query_.size();
 }
 
-void SearchController::Train(AppLaunchData&& app_launch_data) {
+void SearchControllerImplNew::Train(AppLaunchData&& app_launch_data) {
   app_launch_data.query = base::UTF16ToUTF8(last_query_);
 
+  // TODO(crbug.com/1199206): This logging code should move elsewhere.
   if (app_list_features::IsAppListLaunchRecordingEnabled()) {
     // Record a structured metrics event.
     const base::Time now = base::Time::Now();
@@ -318,16 +261,30 @@
                      base::HashMetricName(base::UTF16ToUTF8(last_query_)))}});
 
   // Train all search result ranking models.
-  if (ranker_) {
-    // TODO(crbug.com/1199206): Implement.
-  } else {
-    mixer_->Train(app_launch_data);
-  }
+  // TODO(crbug.com/1199206): Implement.
 }
 
-void SearchController::AppListShown() {
+void SearchControllerImplNew::AppListShown() {
   for (const auto& provider : providers_)
     provider->AppListShown();
 }
 
+void SearchControllerImplNew::ViewClosing() {
+  for (const auto& provider : providers_)
+    provider->ViewClosing();
+}
+
+std::u16string SearchControllerImplNew::get_query() {
+  return last_query_;
+}
+
+base::Time SearchControllerImplNew::session_start() {
+  return session_start_;
+}
+
+void SearchControllerImplNew::set_results_changed_callback_for_test(
+    ResultsChangedCallback callback) {
+  // Unused.
+}
+
 }  // namespace app_list
diff --git a/chrome/browser/ui/app_list/search/search_controller_impl_new.h b/chrome/browser/ui/app_list/search/search_controller_impl_new.h
new file mode 100644
index 0000000..60d947e
--- /dev/null
+++ b/chrome/browser/ui/app_list/search/search_controller_impl_new.h
@@ -0,0 +1,112 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_UI_APP_LIST_SEARCH_SEARCH_CONTROLLER_IMPL_NEW_H_
+#define CHROME_BROWSER_UI_APP_LIST_SEARCH_SEARCH_CONTROLLER_IMPL_NEW_H_
+
+#include <stddef.h>
+
+#include <memory>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "base/callback.h"
+#include "base/containers/flat_map.h"
+#include "base/macros.h"
+#include "chrome/browser/ui/app_list/search/mixer.h"
+#include "chrome/browser/ui/app_list/search/search_controller.h"
+#include "chrome/browser/ui/app_list/search/search_result_ranker/app_launch_data.h"
+
+class AppListControllerDelegate;
+class AppListModelUpdater;
+class ChromeSearchResult;
+class Profile;
+
+namespace ash {
+class AppListNotifier;
+enum class AppListSearchResultType;
+}  // namespace ash
+
+namespace app_list {
+
+class SearchMetricsObserver;
+class SearchProvider;
+class RankerDelegate;
+enum class RankingItemType;
+
+// TODO(crbug.com/1199206): This is the new implementation of the search
+// controller. Once we have fully migrated to the new system, this can replace
+// SearchController.
+class SearchControllerImplNew : public SearchController {
+ public:
+  using ResultsChangedCallback =
+      base::RepeatingCallback<void(ash::AppListSearchResultType)>;
+
+  SearchControllerImplNew(AppListModelUpdater* model_updater,
+                          AppListControllerDelegate* list_controller,
+                          ash::AppListNotifier* notifier,
+                          Profile* profile);
+  ~SearchControllerImplNew() override;
+
+  SearchControllerImplNew(const SearchControllerImplNew&) = delete;
+  SearchControllerImplNew& operator=(const SearchControllerImplNew&) = delete;
+
+  // SearchController:
+  void InitializeRankers() override;
+  void Start(const std::u16string& query) override;
+  void OpenResult(ChromeSearchResult* result, int event_flags) override;
+  void InvokeResultAction(ChromeSearchResult* result,
+                          int action_index) override;
+  size_t AddGroup(size_t max_results) override;
+  void AddProvider(size_t group_id,
+                   std::unique_ptr<SearchProvider> provider) override;
+  void SetResults(ash::AppListSearchResultType provider_type,
+                  Results results) override;
+  ChromeSearchResult* FindSearchResult(const std::string& result_id) override;
+  ChromeSearchResult* GetResultByTitleForTest(
+      const std::string& title) override;
+  void Train(AppLaunchData&& app_launch_data) override;
+  void AppListShown() override;
+  void ViewClosing() override;
+  int GetLastQueryLength() const override;
+  void OnSearchResultsImpressionMade(
+      const std::u16string& trimmed_query,
+      const ash::SearchResultIdWithPositionIndices& results,
+      int launched_index) override;
+  void set_results_changed_callback_for_test(
+      ResultsChangedCallback callback) override;
+  std::u16string get_query() override;
+  base::Time session_start() override;
+
+ private:
+  Profile* profile_;
+
+  // The query associated with the most recent search.
+  std::u16string last_query_;
+
+  // The time when Start was most recently called.
+  base::Time session_start_;
+
+  // The ID of the most recently launched app. This is used for app list launch
+  // recording.
+  std::string last_launched_app_id_;
+
+  // Top-level result ranker. Replaces the Mixer if the categorical search flag
+  // is enabled.
+  std::unique_ptr<RankerDelegate> ranker_;
+
+  // Storage for all search results for the current query. Only used when
+  // categorical search is enabled.
+  ResultsMap results_;
+
+  std::unique_ptr<SearchMetricsObserver> metrics_observer_;
+  using Providers = std::vector<std::unique_ptr<SearchProvider>>;
+  Providers providers_;
+  AppListControllerDelegate* list_controller_;
+};
+
+}  // namespace app_list
+
+#endif  // CHROME_BROWSER_UI_APP_LIST_SEARCH_SEARCH_CONTROLLER_IMPL_NEW_H_
diff --git a/chrome/browser/ui/app_list/search/search_controller_unittest.cc b/chrome/browser/ui/app_list/search/search_controller_unittest.cc
index faa01220..8fd490c 100644
--- a/chrome/browser/ui/app_list/search/search_controller_unittest.cc
+++ b/chrome/browser/ui/app_list/search/search_controller_unittest.cc
@@ -2,13 +2,14 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/ui/app_list/search/search_controller.h"
+#include "chrome/browser/ui/app_list/search/search_controller_impl.h"
 
 #include <memory>
 #include <vector>
 
 #include "ash/public/cpp/test/shell_test_api.h"
 #include "chrome/browser/ui/app_list/search/chrome_search_result.h"
+#include "chrome/browser/ui/app_list/search/search_controller.h"
 #include "chrome/browser/ui/app_list/test/test_app_list_controller_delegate.h"
 #include "chrome/test/base/chrome_ash_test_base.h"
 
@@ -44,9 +45,9 @@
 
  private:
   TestAppListControllerDelegate list_controller_;
-  SearchController search_controller_{/*model_updater=*/nullptr,
-                                      &list_controller_, /*profile=*/nullptr,
-                                      /*notifier=*/nullptr};
+  SearchControllerImpl search_controller_{
+      /*model_updater=*/nullptr, &list_controller_, /*profile=*/nullptr,
+      /*notifier=*/nullptr};
 };
 
 // Tests -----------------------------------------------------------------------
diff --git a/chrome/browser/ui/app_list/search/search_result_ranker/search_result_ranker_unittest.cc b/chrome/browser/ui/app_list/search/search_result_ranker/search_result_ranker_unittest.cc
index 3b60293..cc6a13f 100644
--- a/chrome/browser/ui/app_list/search/search_result_ranker/search_result_ranker_unittest.cc
+++ b/chrome/browser/ui/app_list/search/search_result_ranker/search_result_ranker_unittest.cc
@@ -25,6 +25,7 @@
 #include "chrome/browser/ui/app_list/search/chrome_search_result.h"
 #include "chrome/browser/ui/app_list/search/mixer.h"
 #include "chrome/browser/ui/app_list/search/search_controller.h"
+#include "chrome/browser/ui/app_list/search/search_controller_impl.h"
 #include "chrome/browser/ui/app_list/search/search_result_ranker/app_launch_data.h"
 #include "chrome/browser/ui/app_list/search/search_result_ranker/histogram_util.h"
 #include "chrome/browser/ui/app_list/search/search_result_ranker/ranking_item_util.h"
@@ -117,10 +118,10 @@
   return nullptr;
 }
 
-class SearchControllerFake : public SearchController {
+class SearchControllerFake : public SearchControllerImpl {
  public:
   explicit SearchControllerFake(Profile* profile)
-      : SearchController(nullptr, nullptr, nullptr, profile) {}
+      : SearchControllerImpl(nullptr, nullptr, nullptr, profile) {}
 };
 
 }  // namespace
diff --git a/chrome/browser/ui/ash/sharesheet/sharesheet_bubble_view.cc b/chrome/browser/ui/ash/sharesheet/sharesheet_bubble_view.cc
index a24b4d7..24b5fcf 100644
--- a/chrome/browser/ui/ash/sharesheet/sharesheet_bubble_view.cc
+++ b/chrome/browser/ui/ash/sharesheet/sharesheet_bubble_view.cc
@@ -182,9 +182,14 @@
       /* inside_border_insets */ gfx::Insets(),
       /* between_child_spacing */ 0, /* collapse_margins_spacing */ true));
 
+  // When there are no targets, don't show any previews. Otherwise, show
+  // previews if the flag is enabled.
+  bool show_content_previews =
+      !targets.empty() &&
+      base::FeatureList::IsEnabled(features::kSharesheetContentPreviews);
   header_view_ =
       main_view_->AddChildView(std::make_unique<SharesheetHeaderView>(
-          intent_->Clone(), delegate_->GetProfile()));
+          intent_->Clone(), delegate_->GetProfile(), show_content_previews));
 
   if (targets.empty()) {
     auto* image =
diff --git a/chrome/browser/ui/ash/sharesheet/sharesheet_header_view.cc b/chrome/browser/ui/ash/sharesheet/sharesheet_header_view.cc
index 1c12f15..143a265 100644
--- a/chrome/browser/ui/ash/sharesheet/sharesheet_header_view.cc
+++ b/chrome/browser/ui/ash/sharesheet/sharesheet_header_view.cc
@@ -199,7 +199,8 @@
 // SharesheetHeaderView --------------------------------------------------------
 
 SharesheetHeaderView::SharesheetHeaderView(apps::mojom::IntentPtr intent,
-                                           Profile* profile)
+                                           Profile* profile,
+                                           bool show_content_previews)
     : profile_(profile),
       intent_(std::move(intent)),
       thumbnail_loader_(profile) {
@@ -217,7 +218,7 @@
   const bool has_files =
       (intent_->file_urls.has_value() && !intent_->file_urls.value().empty());
   // The image view is initialised first to ensure its left most placement.
-  if (base::FeatureList::IsEnabled(features::kSharesheetContentPreviews)) {
+  if (show_content_previews) {
     auto file_count = (has_files) ? intent_->file_urls.value().size() : 0;
     image_preview_ =
         AddChildView(std::make_unique<SharesheetImagePreview>(file_count));
@@ -232,7 +233,7 @@
       CreateShareLabel(l10n_util::GetStringUTF16(IDS_SHARESHEET_TITLE_LABEL),
                        CONTEXT_SHARESHEET_BUBBLE_TITLE, kTitleTextLineHeight,
                        kTitleTextColor, gfx::ALIGN_LEFT));
-  if (base::FeatureList::IsEnabled(features::kSharesheetContentPreviews)) {
+  if (show_content_previews) {
     ShowTextPreview();
     if (has_files) {
       ResolveImages();
diff --git a/chrome/browser/ui/ash/sharesheet/sharesheet_header_view.h b/chrome/browser/ui/ash/sharesheet/sharesheet_header_view.h
index eccc642..5ddc6f2d 100644
--- a/chrome/browser/ui/ash/sharesheet/sharesheet_header_view.h
+++ b/chrome/browser/ui/ash/sharesheet/sharesheet_header_view.h
@@ -27,7 +27,8 @@
   METADATA_HEADER(SharesheetHeaderView);
 
   explicit SharesheetHeaderView(apps::mojom::IntentPtr intent,
-                                Profile* profile);
+                                Profile* profile,
+                                bool show_content_previews);
   ~SharesheetHeaderView() override;
   SharesheetHeaderView(const SharesheetHeaderView&) = delete;
   SharesheetHeaderView& operator=(const SharesheetHeaderView&) = delete;
diff --git a/chrome/browser/ui/ash/shelf/app_service/app_service_instance_registry_helper.cc b/chrome/browser/ui/ash/shelf/app_service/app_service_instance_registry_helper.cc
index 4297812..6ef954a 100644
--- a/chrome/browser/ui/ash/shelf/app_service/app_service_instance_registry_helper.cc
+++ b/chrome/browser/ui/ash/shelf/app_service/app_service_instance_registry_helper.cc
@@ -194,8 +194,8 @@
     state = apps::InstanceState::kDestroyed;
   }
 
-  std::unique_ptr<apps::Instance> instance =
-      std::make_unique<apps::Instance>(app_id, window);
+  std::unique_ptr<apps::Instance> instance = std::make_unique<apps::Instance>(
+      app_id, std::make_unique<apps::Instance::InstanceKey>(window));
   instance->SetLaunchId(launch_id);
   instance->UpdateState(state, base::Time::Now());
 
diff --git a/chrome/browser/ui/toolbar/recent_tabs_sub_menu_model.cc b/chrome/browser/ui/toolbar/recent_tabs_sub_menu_model.cc
index 337da2e..ad13ff1 100644
--- a/chrome/browser/ui/toolbar/recent_tabs_sub_menu_model.cc
+++ b/chrome/browser/ui/toolbar/recent_tabs_sub_menu_model.cc
@@ -446,9 +446,9 @@
               static_cast<const sessions::TabRestoreService::Tab&>(*entry);
           const sessions::SerializedNavigationEntry& current_navigation =
               tab.navigations.at(tab.current_navigation_index);
-          BuildLocalTabItem(entry->id, current_navigation.title(),
-                            current_navigation.virtual_url(),
-                            ++last_local_model_index_);
+          BuildLocalTabItem(
+              entry->id, tab.group_visual_data, current_navigation.title(),
+              current_navigation.virtual_url(), ++last_local_model_index_);
           break;
         }
         case sessions::TabRestoreService::WINDOW: {
@@ -529,10 +529,12 @@
   DCHECK_GT(GetItemCount(), 0);
 }
 
-void RecentTabsSubMenuModel::BuildLocalTabItem(SessionID session_id,
-                                               const std::u16string& title,
-                                               const GURL& url,
-                                               int curr_model_index) {
+void RecentTabsSubMenuModel::BuildLocalTabItem(
+    SessionID session_id,
+    base::Optional<tab_groups::TabGroupVisualData> visual_data,
+    const std::u16string& title,
+    const GURL& url,
+    int curr_model_index) {
   TabNavigationItem item(std::string(), session_id, title, url);
   int command_id = TabVectorIndexToCommandId(
       local_tab_navigation_items_.size(), kFirstLocalTabCommandId);
@@ -541,6 +543,17 @@
   InsertItemAt(curr_model_index, command_id,
                title.empty() ? base::UTF8ToUTF16(item.url.spec()) : title);
   AddTabFavicon(command_id, item.url);
+  // visual_data should only be populated if the tab was part of a tab group
+  // when closed.
+  if (visual_data.has_value()) {
+    const auto& theme =
+        ThemeService::GetThemeProviderForProfile(browser_->profile());
+    const int color_id =
+        GetTabGroupContextMenuColorId(visual_data.value().color());
+    SetMinorIcon(curr_model_index, ui::ImageModel::FromVectorIcon(
+                                       kTabGroupIcon, theme.GetColor(color_id),
+                                       gfx::kFaviconSize));
+  }
   local_tab_navigation_items_.push_back(item);
 }
 
diff --git a/chrome/browser/ui/toolbar/recent_tabs_sub_menu_model.h b/chrome/browser/ui/toolbar/recent_tabs_sub_menu_model.h
index 4b3083a..e717b599 100644
--- a/chrome/browser/ui/toolbar/recent_tabs_sub_menu_model.h
+++ b/chrome/browser/ui/toolbar/recent_tabs_sub_menu_model.h
@@ -97,10 +97,12 @@
 
   // Build a recently closed tab item with parameters needed to restore it, and
   // add it to the menumodel at |curr_model_index|.
-  void BuildLocalTabItem(SessionID session_id,
-                         const std::u16string& title,
-                         const GURL& url,
-                         int curr_model_index);
+  void BuildLocalTabItem(
+      SessionID session_id,
+      base::Optional<tab_groups::TabGroupVisualData> visual_data,
+      const std::u16string& title,
+      const GURL& url,
+      int curr_model_index);
 
   // Build the recently closed window item with parameters needed to restore it,
   // and add it to the menumodel at |curr_model_index|.
diff --git a/chrome/browser/ui/views/global_media_controls/media_notification_device_selector_view.cc b/chrome/browser/ui/views/global_media_controls/media_notification_device_selector_view.cc
index 79bfc2e..9e35c45 100644
--- a/chrome/browser/ui/views/global_media_controls/media_notification_device_selector_view.cc
+++ b/chrome/browser/ui/views/global_media_controls/media_notification_device_selector_view.cc
@@ -392,17 +392,32 @@
     cast_controller_->ClearIssue(sink.issue->id());
     return;
   }
-  // If users try to cast to a connected sink, a new cast session will get
-  // started and override the existing cast session.
-  if (sink.state == media_router::UIMediaSinkState::AVAILABLE ||
-      sink.state == media_router::UIMediaSinkState::CONNECTED) {
-    DCHECK(base::Contains(sink.cast_modes,
-                          media_router::MediaCastMode::PRESENTATION));
-    cast_controller_->StartCasting(sink.id,
-                                   media_router::MediaCastMode::PRESENTATION);
-    RecordStartCastingMetrics();
+  // When users click on a CONNECTED sink,
+  // if it is a CAST sink, a new cast session will replace the existing cast
+  // session.
+  // if it is a DIAL sink, the existing session will be terminated and users
+  // need to click on the sink again to start a new session.
+  // TODO(crbug.com/1206830): implement "terminate existing route and start a
+  // new session" in DIAL MRP.
+  if (sink.state == media_router::UIMediaSinkState::AVAILABLE) {
+    DoStartCastSession(sink);
+  } else if (sink.state == media_router::UIMediaSinkState::CONNECTED) {
+    if (sink.provider == media_router::MediaRouteProviderId::DIAL) {
+      DCHECK(sink.route);
+      cast_controller_->StopCasting(sink.route->media_route_id());
+    } else {
+      DoStartCastSession(sink);
+    }
   }
 }
+void MediaNotificationDeviceSelectorView::DoStartCastSession(
+    const media_router::UIMediaSink& sink) {
+  DCHECK(base::Contains(sink.cast_modes,
+                        media_router::MediaCastMode::PRESENTATION));
+  cast_controller_->StartCasting(sink.id,
+                                 media_router::MediaCastMode::PRESENTATION);
+  RecordStartCastingMetrics();
+}
 
 void MediaNotificationDeviceSelectorView::RecordStartCastingMetrics() {
   GlobalMediaControlsCastActionAndEntryPoint action;
diff --git a/chrome/browser/ui/views/global_media_controls/media_notification_device_selector_view.h b/chrome/browser/ui/views/global_media_controls/media_notification_device_selector_view.h
index 4d4f5e5..f322398 100644
--- a/chrome/browser/ui/views/global_media_controls/media_notification_device_selector_view.h
+++ b/chrome/browser/ui/views/global_media_controls/media_notification_device_selector_view.h
@@ -96,6 +96,7 @@
   void HideDevices();
   void RemoveDevicesOfType(DeviceEntryUIType type);
   void StartCastSession(CastDeviceEntryView* entry);
+  void DoStartCastSession(const media_router::UIMediaSink& sink);
   void RecordStartCastingMetrics();
   DeviceEntryUI* GetDeviceEntryUI(views::View* view) const;
   void RegisterAudioDeviceCallbacks();
diff --git a/chrome/browser/ui/views/global_media_controls/media_notification_device_selector_view_unittest.cc b/chrome/browser/ui/views/global_media_controls/media_notification_device_selector_view_unittest.cc
index c1691fb..52d864d7 100644
--- a/chrome/browser/ui/views/global_media_controls/media_notification_device_selector_view_unittest.cc
+++ b/chrome/browser/ui/views/global_media_controls/media_notification_device_selector_view_unittest.cc
@@ -283,15 +283,31 @@
     SimulateButtonClick(child);
   }
 
-  // Clicking on available or connected sinks will start casting.
-  view_->OnModelUpdated(CreateModelWithSinks(
-      {CreateMediaSink(), CreateMediaSink(UIMediaSinkState::CONNECTED)}));
+  // Clicking on available or connected CAST sinks will start casting.
+  auto cast_connected_sink = CreateMediaSink(UIMediaSinkState::CONNECTED);
+  cast_connected_sink.provider = media_router::MediaRouteProviderId::CAST;
+  view_->OnModelUpdated(
+      CreateModelWithSinks({CreateMediaSink(), cast_connected_sink}));
   EXPECT_CALL(*cast_controller_ptr,
               StartCasting(_, media_router::MediaCastMode::PRESENTATION))
       .Times(2);
   for (views::View* child : view_->device_entry_views_container_->children()) {
     SimulateButtonClick(child);
   }
+
+  // Clicking on connected DIAL sinks will terminate casting.
+  // TODO(crbug.com/1206830): change test cases after DIAL MRP supports
+  // launching session on a connected sink.
+  auto dial_connected_sink = CreateMediaSink(UIMediaSinkState::CONNECTED);
+  dial_connected_sink.provider = media_router::MediaRouteProviderId::DIAL;
+  dial_connected_sink.route =
+      media_router::MediaRoute("routeId1", media_router::MediaSource("source1"),
+                               "sinkId1", "description", true, true);
+  view_->OnModelUpdated(CreateModelWithSinks({dial_connected_sink}));
+  EXPECT_CALL(*cast_controller_ptr, StopCasting("routeId1"));
+  for (views::View* child : view_->device_entry_views_container_->children()) {
+    SimulateButtonClick(child);
+  }
 }
 
 TEST_F(MediaNotificationDeviceSelectorViewTest, CurrentAudioDeviceHighlighted) {
diff --git a/chrome/browser/ui/webui/download_shelf/download_shelf_ui.cc b/chrome/browser/ui/webui/download_shelf/download_shelf_ui.cc
index cb9c393..a6cd557a 100644
--- a/chrome/browser/ui/webui/download_shelf/download_shelf_ui.cc
+++ b/chrome/browser/ui/webui/download_shelf/download_shelf_ui.cc
@@ -12,6 +12,7 @@
 #include "chrome/common/webui_url_constants.h"
 #include "chrome/grit/download_shelf_resources.h"
 #include "chrome/grit/download_shelf_resources_map.h"
+#include "chrome/grit/generated_resources.h"
 #include "content/public/browser/web_contents.h"
 #include "content/public/browser/web_ui_data_source.h"
 #include "mojo/public/cpp/bindings/pending_receiver.h"
@@ -37,6 +38,9 @@
           Profile::FromWebUI(web_ui))) {
   content::WebUIDataSource* source =
       content::WebUIDataSource::Create(chrome::kChromeUIDownloadShelfHost);
+  static constexpr webui::LocalizedString kStrings[] = {
+      {"discardButtonText", IDS_DISCARD_DOWNLOAD}};
+  source->AddLocalizedStrings(kStrings);
 
   webui::SetupWebUIDataSource(
       source,
diff --git a/chrome/browser/ui/webui/new_tab_page/new_tab_page_ui.cc b/chrome/browser/ui/webui/new_tab_page/new_tab_page_ui.cc
index 0a26515..084346e 100644
--- a/chrome/browser/ui/webui/new_tab_page/new_tab_page_ui.cc
+++ b/chrome/browser/ui/webui/new_tab_page/new_tab_page_ui.cc
@@ -225,6 +225,8 @@
       {"modulesCartLowerYour", IDS_NTP_MODULES_CART_LOWER_YOUR},
       {"modulesDriveSentence", IDS_NTP_MODULES_DRIVE_SENTENCE},
       {"modulesDriveSentence2", IDS_NTP_MODULES_DRIVE_SENTENCE2},
+      {"modulesDriveFilesSentence", IDS_NTP_MODULES_DRIVE_FILES_SENTENCE},
+      {"modulesDriveFilesLower", IDS_NTP_MODULES_DRIVE_FILES_LOWER},
       {"modulesDummyLower", IDS_NTP_MODULES_DUMMY_LOWER},
       {"modulesDriveTitle", IDS_NTP_MODULES_DRIVE_TITLE},
       {"modulesDriveInfo", IDS_NTP_MODULES_DRIVE_INFO},
diff --git a/chrome/browser/ui/webui/print_preview/print_preview_handler_chromeos.cc b/chrome/browser/ui/webui/print_preview/print_preview_handler_chromeos.cc
index 14c65ca..9bbe3d4d 100644
--- a/chrome/browser/ui/webui/print_preview/print_preview_handler_chromeos.cc
+++ b/chrome/browser/ui/webui/print_preview/print_preview_handler_chromeos.cc
@@ -304,7 +304,7 @@
 void PrintPreviewHandlerChromeOS::OnGotExtensionPrinterInfo(
     const std::string& callback_id,
     const base::DictionaryValue& printer_info) {
-  if (printer_info.empty()) {
+  if (printer_info.DictEmpty()) {
     RejectJavascriptCallback(base::Value(callback_id), base::Value());
     return;
   }
diff --git a/chrome/browser/ui/webui/settings/chromeos/apps_section.cc b/chrome/browser/ui/webui/settings/chromeos/apps_section.cc
index a24a482..4c28006 100644
--- a/chrome/browser/ui/webui/settings/chromeos/apps_section.cc
+++ b/chrome/browser/ui/webui/settings/chromeos/apps_section.cc
@@ -5,6 +5,7 @@
 #include "chrome/browser/ui/webui/settings/chromeos/apps_section.h"
 
 #include "ash/constants/ash_features.h"
+#include "ash/public/cpp/ash_features.h"
 #include "base/feature_list.h"
 #include "base/no_destructor.h"
 #include "base/strings/utf_string_conversions.h"
@@ -111,6 +112,24 @@
   return *tags;
 }
 
+const std::vector<SearchConcept>& GetOnStartupSearchConcepts() {
+  static const base::NoDestructor<std::vector<SearchConcept>> tags({
+      {IDS_OS_SETTINGS_TAG_ON_STARTUP,
+       mojom::kOnStartupSubpagePath,
+       mojom::SearchResultIcon::kStartup,
+       mojom::SearchResultDefaultRank::kMedium,
+       mojom::SearchResultType::kSubpage,
+       {.subpage = mojom::Subpage::kOnStartup}},
+      {IDS_OS_SETTINGS_TAG_RESTORE_APPS_AND_PAGES,
+       mojom::kOnStartupSubpagePath,
+       mojom::SearchResultIcon::kStartup,
+       mojom::SearchResultDefaultRank::kMedium,
+       mojom::SearchResultType::kSetting,
+       {.setting = mojom::Setting::kRestoreAppsAndPages}},
+  });
+  return *tags;
+}
+
 void AddAppManagementStrings(content::WebUIDataSource* html_source) {
   static constexpr webui::LocalizedString kLocalizedStrings[] = {
       {"appManagementAppInstalledByPolicyLabel",
@@ -171,6 +190,25 @@
          pref_service.GetBoolean(plugin_vm::prefs::kPluginVmImageExists);
 }
 
+bool ShouldShowStartup() {
+  return ash::features::IsFullRestoreEnabled();
+}
+
+void AddOnStartupTimeData(content::WebUIDataSource* html_source) {
+  static constexpr webui::LocalizedString kLocalizedStrings[] = {
+      {"onStartupPageTitle", IDS_OS_SETTINGS_ON_STARTUP},
+      {"onStartupRadioGroundTitle",
+       IDS_OS_SETTINGS_ON_STARTUP_RADIO_GROUP_TITLE},
+      {"onStartupAlways", IDS_OS_SETTINGS_ON_STARTUP_ALWAYS},
+      {"onStartupAskEveryTime", IDS_OS_SETTINGS_ON_STARTUP_ASK_EVERY_TIME},
+      {"onStartupDoNotRestore", IDS_OS_SETTINGS_ON_STARTUP_DO_NOT_RESTORE},
+  };
+
+  html_source->AddLocalizedStrings(kLocalizedStrings);
+
+  html_source->AddBoolean("showStartup", ShouldShowStartup());
+}
+
 }  // namespace
 
 AppsSection::AppsSection(Profile* profile,
@@ -197,6 +235,9 @@
 
     UpdateAndroidSearchTags();
   }
+
+  if (ShouldShowStartup())
+    updater.AddSearchTags(GetOnStartupSearchConcepts());
 }
 
 AppsSection::~AppsSection() {
@@ -228,6 +269,7 @@
   AddGuestOsStrings(html_source);
   AddAndroidAppStrings(html_source);
   AddPluginVmLoadTimeData(html_source);
+  AddOnStartupTimeData(html_source);
 }
 
 void AppsSection::AddHandlers(content::WebUI* web_ui) {
@@ -303,6 +345,17 @@
                             kGooglePlayStoreSettings, generator);
   generator->RegisterTopLevelAltSetting(
       mojom::Setting::kManageAndroidPreferences);
+
+  // On startup
+  generator->RegisterTopLevelSubpage(
+      IDS_OS_SETTINGS_TAG_ON_STARTUP, mojom::Subpage::kOnStartup,
+      mojom::SearchResultIcon::kStartup,
+      mojom::SearchResultDefaultRank::kMedium, mojom::kOnStartupSubpagePath);
+  static constexpr mojom::Setting kOnStartupSettings[] = {
+      mojom::Setting::kRestoreAppsAndPages,
+  };
+  RegisterNestedSettingBulk(mojom::Subpage::kOnStartup, kOnStartupSettings,
+                            generator);
 }
 
 void AppsSection::OnAppRegistered(const std::string& app_id,
diff --git a/chrome/browser/ui/webui/settings/chromeos/constants/routes.mojom b/chrome/browser/ui/webui/settings/chromeos/constants/routes.mojom
index 48802f7..34f389f 100644
--- a/chrome/browser/ui/webui/settings/chromeos/constants/routes.mojom
+++ b/chrome/browser/ui/webui/settings/chromeos/constants/routes.mojom
@@ -28,7 +28,6 @@
   kReset = 16,
   kAboutChromeOs = 17,
   kKerberos = 18,
-  kOnStartup = 19,
 };
 
 // Chrome OS Settings subpages (i.e., nested pages within a section). Each
@@ -93,6 +92,7 @@
   kGooglePlayStore = 702,
   kPluginVmSharedPaths = 703,
   kPluginVmUsbPreferences = 704,
+  kOnStartup = 705,
 
   // Crostini section.
   kCrostiniDetails = 800,
@@ -209,6 +209,7 @@
     "app-management/pluginVm/sharedPaths";
 const string kPluginVmUsbPreferencesSubpagePath =
     "app-management/pluginVm/sharedUsbDevices";
+const string kOnStartupSubpagePath = "onstartup";
 
 // Crostini section.
 const string kCrostiniSectionPath = "crostini";
@@ -219,9 +220,6 @@
 const string kCrostiniDevelopAndroidAppsSubpagePath = "crostini/androidAdb";
 const string kCrostiniPortForwardingSubpagePath = "crostini/portForwarding";
 
-// On Startup section.
-const string kOnStartupSectionPath = "onstartup";
-
 // Date and Time section.
 const string kDateAndTimeSectionPath = "dateTime";
 const string kTimeZoneSubpagePath = "dateTime/timeZone";
diff --git a/chrome/browser/ui/webui/settings/chromeos/constants/routes_util.cc b/chrome/browser/ui/webui/settings/chromeos/constants/routes_util.cc
index 6970e89..b387dc7 100644
--- a/chrome/browser/ui/webui/settings/chromeos/constants/routes_util.cc
+++ b/chrome/browser/ui/webui/settings/chromeos/constants/routes_util.cc
@@ -74,6 +74,7 @@
       chromeos::settings::mojom::kAppDetailsSubpagePath,
       chromeos::settings::mojom::kGooglePlayStoreSubpagePath,
       chromeos::settings::mojom::kPluginVmSharedPathsSubpagePath,
+      chromeos::settings::mojom::kOnStartupSubpagePath,
 
       // Crostini section.
       chromeos::settings::mojom::kCrostiniSectionPath,
@@ -84,9 +85,6 @@
       chromeos::settings::mojom::kCrostiniDevelopAndroidAppsSubpagePath,
       chromeos::settings::mojom::kCrostiniPortForwardingSubpagePath,
 
-      // On Startup section.
-      chromeos::settings::mojom::kOnStartupSectionPath,
-
       // Date and Time section.
       chromeos::settings::mojom::kDateAndTimeSectionPath,
       chromeos::settings::mojom::kTimeZoneSubpagePath,
diff --git a/chrome/browser/ui/webui/settings/chromeos/constants/setting.mojom b/chrome/browser/ui/webui/settings/chromeos/constants/setting.mojom
index 65179c5..770030d 100644
--- a/chrome/browser/ui/webui/settings/chromeos/constants/setting.mojom
+++ b/chrome/browser/ui/webui/settings/chromeos/constants/setting.mojom
@@ -155,6 +155,7 @@
   kManageAndroidPreferences = 700,
   kRemovePlayStore = 701,
   kTurnOnPlayStore = 702,
+  kRestoreAppsAndPages = 703,
 
   // Crostini section.
   kSetUpCrostini= 800,
@@ -258,7 +259,4 @@
   kAddKerberosTicketV2 = 1800,
   kRemoveKerberosTicketV2 = 1801,
   kSetActiveKerberosTicketV2 = 1802,
-
-  // On Startup section.
-  kRestoreAppsAndPages = 1900,
 };
diff --git a/chrome/browser/ui/webui/settings/chromeos/on_startup_section.cc b/chrome/browser/ui/webui/settings/chromeos/on_startup_section.cc
deleted file mode 100644
index 2170055..0000000
--- a/chrome/browser/ui/webui/settings/chromeos/on_startup_section.cc
+++ /dev/null
@@ -1,98 +0,0 @@
-// Copyright 2020 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "chrome/browser/ui/webui/settings/chromeos/on_startup_section.h"
-
-#include "ash/public/cpp/ash_features.h"
-#include "base/no_destructor.h"
-#include "chrome/browser/ui/webui/settings/chromeos/search/search_tag_registry.h"
-#include "chrome/browser/ui/webui/webui_util.h"
-#include "chrome/grit/generated_resources.h"
-#include "content/public/browser/web_ui_data_source.h"
-#include "ui/base/webui/web_ui_util.h"
-
-namespace chromeos {
-namespace settings {
-namespace {
-
-const std::vector<SearchConcept>& GetOnStartupSearchConcepts() {
-  static const base::NoDestructor<std::vector<SearchConcept>> tags({
-      {IDS_OS_SETTINGS_TAG_ON_STARTUP,
-       mojom::kOnStartupSectionPath,
-       mojom::SearchResultIcon::kStartup,
-       mojom::SearchResultDefaultRank::kMedium,
-       mojom::SearchResultType::kSection,
-       {.section = mojom::Section::kOnStartup}},
-      {IDS_OS_SETTINGS_TAG_RESTORE_APPS_AND_PAGES,
-       mojom::kOnStartupSectionPath,
-       mojom::SearchResultIcon::kStartup,
-       mojom::SearchResultDefaultRank::kMedium,
-       mojom::SearchResultType::kSetting,
-       {.setting = mojom::Setting::kRestoreAppsAndPages}},
-  });
-  return *tags;
-}
-
-bool ShouldShowStartup() {
-  return ash::features::IsFullRestoreEnabled();
-}
-
-}  // namespace
-
-OnStartupSection::OnStartupSection(Profile* profile,
-                                   SearchTagRegistry* search_tag_registry,
-                                   PrefService* pref_service)
-    : OsSettingsSection(profile, search_tag_registry) {
-  SearchTagRegistry::ScopedTagUpdater updater = registry()->StartUpdate();
-
-  if (ShouldShowStartup()) {
-    updater.AddSearchTags(GetOnStartupSearchConcepts());
-  }
-}
-
-OnStartupSection::~OnStartupSection() = default;
-
-void OnStartupSection::AddLoadTimeData(content::WebUIDataSource* html_source) {
-  static constexpr webui::LocalizedString kLocalizedStrings[] = {
-      {"onStartupPageTitle", IDS_OS_SETTINGS_ON_STARTUP},
-      {"onStartupRadioGroundTitle",
-       IDS_OS_SETTINGS_ON_STARTUP_RADIO_GROUP_TITLE},
-      {"onStartupAlways", IDS_OS_SETTINGS_ON_STARTUP_ALWAYS},
-      {"onStartupAskEveryTime", IDS_OS_SETTINGS_ON_STARTUP_ASK_EVERY_TIME},
-      {"onStartupDoNotRestore", IDS_OS_SETTINGS_ON_STARTUP_DO_NOT_RESTORE},
-  };
-
-  html_source->AddLocalizedStrings(kLocalizedStrings);
-
-  html_source->AddBoolean("showStartup", ShouldShowStartup());
-}
-
-int OnStartupSection::GetSectionNameMessageId() const {
-  return IDS_OS_SETTINGS_ON_STARTUP;
-}
-
-mojom::Section OnStartupSection::GetSection() const {
-  return mojom::Section::kOnStartup;
-}
-
-mojom::SearchResultIcon OnStartupSection::GetSectionIcon() const {
-  return mojom::SearchResultIcon::kStartup;
-}
-
-std::string OnStartupSection::GetSectionPath() const {
-  return mojom::kOnStartupSectionPath;
-}
-
-bool OnStartupSection::LogMetric(mojom::Setting setting,
-                                 base::Value& value) const {
-  // Unimplemented.
-  return false;
-}
-
-void OnStartupSection::RegisterHierarchy(HierarchyGenerator* generator) const {
-  generator->RegisterTopLevelSetting(mojom::Setting::kRestoreAppsAndPages);
-}
-
-}  // namespace settings
-}  // namespace chromeos
diff --git a/chrome/browser/ui/webui/settings/chromeos/on_startup_section.h b/chrome/browser/ui/webui/settings/chromeos/on_startup_section.h
deleted file mode 100644
index 141f5a1..0000000
--- a/chrome/browser/ui/webui/settings/chromeos/on_startup_section.h
+++ /dev/null
@@ -1,38 +0,0 @@
-// Copyright 2020 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CHROME_BROWSER_UI_WEBUI_SETTINGS_CHROMEOS_ON_STARTUP_SECTION_H_
-#define CHROME_BROWSER_UI_WEBUI_SETTINGS_CHROMEOS_ON_STARTUP_SECTION_H_
-
-#include "chrome/browser/ui/webui/settings/chromeos/os_settings_section.h"
-#include "components/prefs/pref_change_registrar.h"
-
-namespace chromeos {
-namespace settings {
-
-class SearchTagRegistry;
-
-// Provides UI strings and search tags for On Startup settings.
-class OnStartupSection : public OsSettingsSection {
- public:
-  OnStartupSection(Profile* profile,
-                   SearchTagRegistry* search_tag_registry,
-                   PrefService* pref_service);
-  ~OnStartupSection() override;
-
- private:
-  // OsSettingsSection:
-  void AddLoadTimeData(content::WebUIDataSource* html_source) override;
-  int GetSectionNameMessageId() const override;
-  mojom::Section GetSection() const override;
-  mojom::SearchResultIcon GetSectionIcon() const override;
-  std::string GetSectionPath() const override;
-  void RegisterHierarchy(HierarchyGenerator* generator) const override;
-  bool LogMetric(mojom::Setting setting, base::Value& value) const override;
-};
-
-}  // namespace settings
-}  // namespace chromeos
-
-#endif  // CHROME_BROWSER_UI_WEBUI_SETTINGS_CHROMEOS_ON_STARTUP_SECTION_H_
diff --git a/chrome/browser/ui/webui/settings/chromeos/os_settings_sections.cc b/chrome/browser/ui/webui/settings/chromeos/os_settings_sections.cc
index 63a5f52..1679131 100644
--- a/chrome/browser/ui/webui/settings/chromeos/os_settings_sections.cc
+++ b/chrome/browser/ui/webui/settings/chromeos/os_settings_sections.cc
@@ -19,7 +19,6 @@
 #include "chrome/browser/ui/webui/settings/chromeos/languages_section.h"
 #include "chrome/browser/ui/webui/settings/chromeos/main_section.h"
 #include "chrome/browser/ui/webui/settings/chromeos/multidevice_section.h"
-#include "chrome/browser/ui/webui/settings/chromeos/on_startup_section.h"
 #include "chrome/browser/ui/webui/settings/chromeos/people_section.h"
 #include "chrome/browser/ui/webui/settings/chromeos/personalization_section.h"
 #include "chrome/browser/ui/webui/settings/chromeos/printing_section.h"
@@ -97,11 +96,6 @@
   sections_map_[mojom::Section::kCrostini] = crostini_section.get();
   sections_.push_back(std::move(crostini_section));
 
-  auto on_startup_section = std::make_unique<OnStartupSection>(
-      profile, search_tag_registry, profile->GetPrefs());
-  sections_map_[mojom::Section::kOnStartup] = on_startup_section.get();
-  sections_.push_back(std::move(on_startup_section));
-
   auto date_time_section =
       std::make_unique<DateTimeSection>(profile, search_tag_registry);
   sections_map_[mojom::Section::kDateAndTime] = date_time_section.get();
diff --git a/chrome/browser/web_applications/components/app_icon_manager.cc b/chrome/browser/web_applications/components/app_icon_manager.cc
index fc2f91c9f..6d94a55 100644
--- a/chrome/browser/web_applications/components/app_icon_manager.cc
+++ b/chrome/browser/web_applications/components/app_icon_manager.cc
@@ -27,6 +27,10 @@
 
 }  // namespace
 
+WebAppIconManager* AppIconManager::AsWebAppIconManager() {
+  return nullptr;
+}
+
 void AppIconManager::ReadSmallestIconAny(const AppId& app_id,
                                          SquareSizePx min_icon_size,
                                          ReadIconCallback callback) const {
diff --git a/chrome/browser/web_applications/components/app_icon_manager.h b/chrome/browser/web_applications/components/app_icon_manager.h
index bb36dd6..27e3e47 100644
--- a/chrome/browser/web_applications/components/app_icon_manager.h
+++ b/chrome/browser/web_applications/components/app_icon_manager.h
@@ -17,6 +17,8 @@
 
 namespace web_app {
 
+class WebAppIconManager;
+
 // Exclusively used from the UI thread.
 class AppIconManager {
  public:
@@ -28,6 +30,8 @@
   virtual void Start() = 0;
   virtual void Shutdown() = 0;
 
+  virtual WebAppIconManager* AsWebAppIconManager();
+
   // Returns false if any icon in |icon_sizes_in_px| is missing from downloaded
   // icons for a given app and |purpose|.
   virtual bool HasIcons(const AppId& app_id,
diff --git a/chrome/browser/web_applications/extensions/bookmark_app_registrar.cc b/chrome/browser/web_applications/extensions/bookmark_app_registrar.cc
index fbafe67..a9829ad 100644
--- a/chrome/browser/web_applications/extensions/bookmark_app_registrar.cc
+++ b/chrome/browser/web_applications/extensions/bookmark_app_registrar.cc
@@ -45,7 +45,7 @@
 BookmarkAppRegistrar::~BookmarkAppRegistrar() = default;
 
 void BookmarkAppRegistrar::Start() {
-  extension_observer_.Add(ExtensionRegistry::Get(profile()));
+  extension_observation_.Observe(ExtensionRegistry::Get(profile()));
 }
 
 bool BookmarkAppRegistrar::IsInstalled(const web_app::AppId& app_id) const {
@@ -118,7 +118,7 @@
 
 void BookmarkAppRegistrar::OnShutdown(ExtensionRegistry* registry) {
   NotifyAppRegistrarShutdown();
-  extension_observer_.RemoveAll();
+  extension_observation_.Reset();
 }
 
 std::string BookmarkAppRegistrar::GetAppShortName(
diff --git a/chrome/browser/web_applications/extensions/bookmark_app_registrar.h b/chrome/browser/web_applications/extensions/bookmark_app_registrar.h
index 3a563a35..ceedca28 100644
--- a/chrome/browser/web_applications/extensions/bookmark_app_registrar.h
+++ b/chrome/browser/web_applications/extensions/bookmark_app_registrar.h
@@ -8,7 +8,7 @@
 #include <string>
 #include <vector>
 
-#include "base/scoped_observer.h"
+#include "base/scoped_observation.h"
 #include "chrome/browser/web_applications/components/app_registrar.h"
 #include "chrome/browser/web_applications/components/web_app_constants.h"
 #include "chrome/browser/web_applications/components/web_application_info.h"
@@ -123,8 +123,8 @@
   const Extension* GetBookmarkAppDchecked(const web_app::AppId& app_id) const;
   const Extension* GetEnabledExtension(const web_app::AppId& app_id) const;
 
-  ScopedObserver<ExtensionRegistry, ExtensionRegistryObserver>
-      extension_observer_{this};
+  base::ScopedObservation<ExtensionRegistry, ExtensionRegistryObserver>
+      extension_observation_{this};
 
   // Observers may find this pointer via FindExtension method.
   const Extension* bookmark_app_being_observed_ = nullptr;
diff --git a/chrome/browser/web_applications/manifest_update_manager.cc b/chrome/browser/web_applications/manifest_update_manager.cc
index e9616d3..2cd7494 100644
--- a/chrome/browser/web_applications/manifest_update_manager.cc
+++ b/chrome/browser/web_applications/manifest_update_manager.cc
@@ -43,14 +43,14 @@
 }
 
 void ManifestUpdateManager::Start() {
-  registrar_observer_.Add(registrar_);
+  registrar_observation_.Observe(registrar_);
 
   DCHECK(!started_);
   started_ = true;
 }
 
 void ManifestUpdateManager::Shutdown() {
-  registrar_observer_.RemoveAll();
+  registrar_observation_.Reset();
 
   tasks_.clear();
   started_ = false;
diff --git a/chrome/browser/web_applications/manifest_update_manager.h b/chrome/browser/web_applications/manifest_update_manager.h
index 33a351f..d07e49cc 100644
--- a/chrome/browser/web_applications/manifest_update_manager.h
+++ b/chrome/browser/web_applications/manifest_update_manager.h
@@ -10,7 +10,7 @@
 #include "base/callback.h"
 #include "base/containers/flat_map.h"
 #include "base/optional.h"
-#include "base/scoped_observer.h"
+#include "base/scoped_observation.h"
 #include "base/time/time.h"
 #include "chrome/browser/web_applications/components/app_registrar.h"
 #include "chrome/browser/web_applications/components/app_registrar_observer.h"
@@ -92,7 +92,8 @@
   SystemWebAppManager* system_web_app_manager_ = nullptr;
   OsIntegrationManager* os_integration_manager_ = nullptr;
 
-  ScopedObserver<AppRegistrar, AppRegistrarObserver> registrar_observer_{this};
+  base::ScopedObservation<AppRegistrar, AppRegistrarObserver>
+      registrar_observation_{this};
 
   base::flat_map<AppId, std::unique_ptr<ManifestUpdateTask>> tasks_;
 
diff --git a/chrome/browser/web_applications/test/web_app_icon_test_utils.cc b/chrome/browser/web_applications/test/web_app_icon_test_utils.cc
index 937efcd..485f3c9 100644
--- a/chrome/browser/web_applications/test/web_app_icon_test_utils.cc
+++ b/chrome/browser/web_applications/test/web_app_icon_test_utils.cc
@@ -11,8 +11,10 @@
 #include "chrome/browser/web_applications/components/web_app_icon_generator.h"
 #include "chrome/browser/web_applications/components/web_app_utils.h"
 #include "chrome/browser/web_applications/file_utils_wrapper.h"
+#include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/skia/include/core/SkBitmap.h"
 #include "ui/gfx/codec/png_codec.h"
+#include "ui/gfx/image/image_skia.h"
 
 namespace web_app {
 
@@ -115,4 +117,17 @@
   return true;
 }
 
+void ExpectImageSkiaRep(const gfx::ImageSkia& image_skia,
+                        float scale,
+                        SquareSizePx size_px,
+                        SkColor color) {
+  ASSERT_TRUE(image_skia.HasRepresentation(scale));
+
+  EXPECT_EQ(size_px, image_skia.GetRepresentation(scale).GetBitmap().width());
+  EXPECT_EQ(size_px, image_skia.GetRepresentation(scale).GetBitmap().height());
+
+  EXPECT_EQ(color,
+            image_skia.GetRepresentation(scale).GetBitmap().getColor(0, 0));
+}
+
 }  // namespace web_app
diff --git a/chrome/browser/web_applications/test/web_app_icon_test_utils.h b/chrome/browser/web_applications/test/web_app_icon_test_utils.h
index ddf7e3e..0156d47 100644
--- a/chrome/browser/web_applications/test/web_app_icon_test_utils.h
+++ b/chrome/browser/web_applications/test/web_app_icon_test_utils.h
@@ -18,6 +18,10 @@
 class GURL;
 class Profile;
 
+namespace gfx {
+class ImageSkia;
+}
+
 namespace web_app {
 
 class FileUtilsWrapper;
@@ -50,6 +54,11 @@
 bool ContainsOneIconOfEachSize(
     const std::map<SquareSizePx, SkBitmap>& icon_bitmaps);
 
+void ExpectImageSkiaRep(const gfx::ImageSkia& image_skia,
+                        float scale,
+                        SquareSizePx size_px,
+                        SkColor color);
+
 }  // namespace web_app
 
 #endif  // CHROME_BROWSER_WEB_APPLICATIONS_TEST_WEB_APP_ICON_TEST_UTILS_H_
diff --git a/chrome/browser/web_applications/test/web_app_install_observer.cc b/chrome/browser/web_applications/test/web_app_install_observer.cc
index 70e093e..64c261a 100644
--- a/chrome/browser/web_applications/test/web_app_install_observer.cc
+++ b/chrome/browser/web_applications/test/web_app_install_observer.cc
@@ -13,7 +13,7 @@
 namespace web_app {
 
 WebAppInstallObserver::WebAppInstallObserver(AppRegistrar* registrar) {
-  observer_.Add(registrar);
+  observation_.Observe(registrar);
 }
 WebAppInstallObserver::WebAppInstallObserver(
     AppRegistrar* registrar,
@@ -24,7 +24,7 @@
       listening_for_uninstall_app_ids_(listening_for_uninstall_app_ids),
       listening_for_install_with_os_hooks_app_ids_(
           listening_for_install_with_os_hooks_app_ids) {
-  observer_.Add(registrar);
+  observation_.Observe(registrar);
 #if DCHECK_IS_ON()
   DCHECK(!listening_for_install_app_ids_.empty() ||
          !listening_for_uninstall_app_ids_.empty() ||
diff --git a/chrome/browser/web_applications/test/web_app_install_observer.h b/chrome/browser/web_applications/test/web_app_install_observer.h
index c2ba754..edc5b16 100644
--- a/chrome/browser/web_applications/test/web_app_install_observer.h
+++ b/chrome/browser/web_applications/test/web_app_install_observer.h
@@ -9,7 +9,7 @@
 #include <set>
 
 #include "base/callback.h"
-#include "base/scoped_observer.h"
+#include "base/scoped_observation.h"
 #include "chrome/browser/web_applications/components/app_registrar.h"
 #include "chrome/browser/web_applications/components/app_registrar_observer.h"
 #include "chrome/browser/web_applications/components/web_app_id.h"
@@ -148,8 +148,8 @@
   SingleAppUninstalledDelegate single_app_uninstalled_delegate_;
   WebAppProfileWillBeDeletedDelegate app_profile_will_be_deleted_delegate_;
 
-  ScopedObserver<AppRegistrar, AppRegistrarObserver> observer_{this};
-
+  base::ScopedObservation<AppRegistrar, AppRegistrarObserver> observation_{
+      this};
 };
 
 // Convenience method to crreate an observer to wait for the next install
diff --git a/chrome/browser/web_applications/test/web_app_uninstall_waiter.cc b/chrome/browser/web_applications/test/web_app_uninstall_waiter.cc
index ae3c13f..5f94d18 100644
--- a/chrome/browser/web_applications/test/web_app_uninstall_waiter.cc
+++ b/chrome/browser/web_applications/test/web_app_uninstall_waiter.cc
@@ -10,7 +10,8 @@
 
 WebAppUninstallWaiter::WebAppUninstallWaiter(Profile* profile, AppId app_id)
     : app_id_(std::move(app_id)) {
-  observer_.Add(&WebAppProviderBase::GetProviderBase(profile)->registrar());
+  observation_.Observe(
+      &WebAppProviderBase::GetProviderBase(profile)->registrar());
 }
 WebAppUninstallWaiter::~WebAppUninstallWaiter() = default;
 
diff --git a/chrome/browser/web_applications/test/web_app_uninstall_waiter.h b/chrome/browser/web_applications/test/web_app_uninstall_waiter.h
index 3fe2a342..6a3d8851 100644
--- a/chrome/browser/web_applications/test/web_app_uninstall_waiter.h
+++ b/chrome/browser/web_applications/test/web_app_uninstall_waiter.h
@@ -6,7 +6,7 @@
 #define CHROME_BROWSER_WEB_APPLICATIONS_TEST_WEB_APP_UNINSTALL_WAITER_H_
 
 #include "base/run_loop.h"
-#include "base/scoped_observer.h"
+#include "base/scoped_observation.h"
 #include "chrome/browser/web_applications/components/app_registrar.h"
 #include "chrome/browser/web_applications/components/app_registrar_observer.h"
 #include "chrome/browser/web_applications/components/web_app_id.h"
@@ -25,7 +25,8 @@
  private:
   AppId app_id_;
   base::RunLoop run_loop_;
-  ScopedObserver<AppRegistrar, AppRegistrarObserver> observer_{this};
+  base::ScopedObservation<AppRegistrar, AppRegistrarObserver> observation_{
+      this};
 };
 
 }  // namespace web_app
diff --git a/chrome/browser/web_applications/web_app_icon_manager.cc b/chrome/browser/web_applications/web_app_icon_manager.cc
index 13f38ca..4642725 100644
--- a/chrome/browser/web_applications/web_app_icon_manager.cc
+++ b/chrome/browser/web_applications/web_app_icon_manager.cc
@@ -25,7 +25,7 @@
 #include "chrome/browser/web_applications/web_app_registrar.h"
 #include "content/public/browser/browser_thread.h"
 #include "skia/ext/image_operations.h"
-#include "third_party/skia/include/core/SkBitmap.h"
+#include "ui/base/layout.h"
 #include "ui/gfx/codec/png_codec.h"
 #include "ui/gfx/favicon_size.h"
 
@@ -542,6 +542,38 @@
   std::move(callback).Run(purpose, std::move(data));
 }
 
+gfx::ImageSkia ConvertUiScaleFactorsBitmapsToImageSkia(
+    const std::map<SquareSizePx, SkBitmap>& icon_bitmaps,
+    SquareSizeDip size_in_dip) {
+  gfx::ImageSkia image_skia;
+  auto it = icon_bitmaps.begin();
+  for (ui::ScaleFactor scale_factor : ui::GetSupportedScaleFactors()) {
+    float icon_scale = ui::GetScaleForScaleFactor(scale_factor);
+    SquareSizePx icon_size_in_px =
+        gfx::ScaleToFlooredSize(gfx::Size(size_in_dip, size_in_dip), icon_scale)
+            .width();
+
+    while (it != icon_bitmaps.end() && it->first < icon_size_in_px)
+      ++it;
+
+    if (it == icon_bitmaps.end() || it->second.empty())
+      break;
+
+    SkBitmap bitmap = it->second;
+
+    // Resize |bitmap| to match |icon_scale|.
+    if (bitmap.width() != icon_size_in_px) {
+      bitmap = skia::ImageOperations::Resize(bitmap,
+                                             skia::ImageOperations::RESIZE_BEST,
+                                             icon_size_in_px, icon_size_in_px);
+    }
+
+    image_skia.AddRepresentation(gfx::ImageSkiaRep(bitmap, icon_scale));
+  }
+
+  return image_skia;
+}
+
 constexpr base::TaskTraits kTaskTraits = {
     base::MayBlock(), base::TaskPriority::USER_VISIBLE,
     base::TaskShutdownBehavior::BLOCK_SHUTDOWN};
@@ -593,11 +625,15 @@
       std::move(callback));
 }
 
+WebAppIconManager* WebAppIconManager::AsWebAppIconManager() {
+  return this;
+}
+
 void WebAppIconManager::Start() {
   for (const AppId& app_id : registrar_.GetAppIds()) {
     ReadFavicon(app_id);
   }
-  registrar_observer_.Add(&registrar_);
+  registrar_observation_.Observe(&registrar_);
 }
 
 void WebAppIconManager::Shutdown() {}
@@ -750,7 +786,18 @@
   auto iter = favicon_cache_.find(app_id);
   if (iter == favicon_cache_.end())
     return SkBitmap();
-  return iter->second;
+
+  const gfx::ImageSkia& image_skia = iter->second;
+
+  // A representation for 1.0 UI scale factor is mandatory. GetRepresentation()
+  // should create one.
+  return image_skia.GetRepresentation(1.0f).GetBitmap();
+}
+
+gfx::ImageSkia WebAppIconManager::GetFaviconImageSkia(
+    const AppId& app_id) const {
+  auto iter = favicon_cache_.find(app_id);
+  return iter != favicon_cache_.end() ? iter->second : gfx::ImageSkia();
 }
 
 void WebAppIconManager::OnWebAppInstalled(const AppId& app_id) {
@@ -758,7 +805,7 @@
 }
 
 void WebAppIconManager::OnAppRegistrarDestroyed() {
-  registrar_observer_.RemoveAll();
+  registrar_observation_.Reset();
 }
 
 void WebAppIconManager::ReadIconAndResize(const AppId& app_id,
@@ -785,6 +832,40 @@
       std::move(callback));
 }
 
+void WebAppIconManager::ReadUiScaleFactorsIcons(
+    const AppId& app_id,
+    SquareSizeDip size_in_dip,
+    ReadImageSkiaCallback callback) {
+  SortedSizesPx ui_scale_factors_px_sizes;
+  for (ui::ScaleFactor scale_factor : ui::GetSupportedScaleFactors()) {
+    auto size_and_purpose = FindIconMatchBigger(
+        app_id, {IconPurpose::ANY},
+        gfx::ScaleToFlooredSize(gfx::Size(size_in_dip, size_in_dip),
+                                ui::GetScaleForScaleFactor(scale_factor))
+            .width());
+    if (size_and_purpose.has_value())
+      ui_scale_factors_px_sizes.insert(size_and_purpose->size_px);
+  }
+
+  if (ui_scale_factors_px_sizes.empty()) {
+    std::move(callback).Run(gfx::ImageSkia());
+    return;
+  }
+
+  ReadIcons(app_id, IconPurpose::ANY, ui_scale_factors_px_sizes,
+            base::BindOnce(&WebAppIconManager::OnReadUiScaleFactorsIcons,
+                           weak_ptr_factory_.GetWeakPtr(), size_in_dip,
+                           std::move(callback)));
+}
+
+void WebAppIconManager::OnReadUiScaleFactorsIcons(
+    SquareSizeDip size_in_dip,
+    ReadImageSkiaCallback callback,
+    std::map<SquareSizePx, SkBitmap> icon_bitmaps) {
+  std::move(callback).Run(
+      ConvertUiScaleFactorsBitmapsToImageSkia(icon_bitmaps, size_in_dip));
+}
+
 void WebAppIconManager::SetFaviconReadCallbackForTesting(
     FaviconReadCallback callback) {
   favicon_read_callback_ = std::move(callback);
@@ -814,20 +895,17 @@
 }
 
 void WebAppIconManager::ReadFavicon(const AppId& app_id) {
-  if (!HasSmallestIcon(app_id, {IconPurpose::ANY}, gfx::kFaviconSize))
-    return;
-
-  ReadIconAndResize(app_id, IconPurpose::ANY, gfx::kFaviconSize,
-                    base::BindOnce(&WebAppIconManager::OnReadFavicon,
-                                   weak_ptr_factory_.GetWeakPtr(), app_id));
+  ReadUiScaleFactorsIcons(
+      app_id, gfx::kFaviconSize,
+      base::BindOnce(&WebAppIconManager::OnReadFavicon,
+                     weak_ptr_factory_.GetWeakPtr(), app_id));
 }
 
-void WebAppIconManager::OnReadFavicon(
-    const AppId& app_id,
-    const std::map<SquareSizePx, SkBitmap> icons) {
-  const auto it = icons.find(gfx::kFaviconSize);
-  if (it != icons.end())
-    favicon_cache_[app_id] = it->second;
+void WebAppIconManager::OnReadFavicon(const AppId& app_id,
+                                      gfx::ImageSkia image_skia) {
+  if (!image_skia.isNull())
+    favicon_cache_[app_id] = image_skia;
+
   if (favicon_read_callback_)
     favicon_read_callback_.Run(app_id);
 }
diff --git a/chrome/browser/web_applications/web_app_icon_manager.h b/chrome/browser/web_applications/web_app_icon_manager.h
index af66a89..4ab2287 100644
--- a/chrome/browser/web_applications/web_app_icon_manager.h
+++ b/chrome/browser/web_applications/web_app_icon_manager.h
@@ -13,11 +13,13 @@
 #include "base/files/file_path.h"
 #include "base/memory/weak_ptr.h"
 #include "base/optional.h"
-#include "base/scoped_observer.h"
+#include "base/scoped_observation.h"
 #include "chrome/browser/web_applications/components/app_icon_manager.h"
 #include "chrome/browser/web_applications/components/app_registrar.h"
 #include "chrome/browser/web_applications/components/app_registrar_observer.h"
 #include "chrome/browser/web_applications/components/web_application_info.h"
+#include "third_party/skia/include/core/SkBitmap.h"
+#include "ui/gfx/image/image_skia.h"
 
 class Profile;
 
@@ -26,11 +28,15 @@
 class FileUtilsWrapper;
 class WebAppRegistrar;
 
+using SquareSizeDip = int;
+
 // Exclusively used from the UI thread.
 class WebAppIconManager : public AppIconManager, public AppRegistrarObserver {
  public:
   using FaviconReadCallback =
       base::RepeatingCallback<void(const AppId& app_id)>;
+  using ReadImageSkiaCallback =
+      base::OnceCallback<void(gfx::ImageSkia image_skia)>;
 
   WebAppIconManager(Profile* profile,
                     WebAppRegistrar& registrar,
@@ -52,6 +58,7 @@
   void DeleteData(AppId app_id, WriteDataCallback callback);
 
   // AppIconManager:
+  WebAppIconManager* AsWebAppIconManager() override;
   void Start() override;
   void Shutdown() override;
   bool HasIcons(const AppId& app_id,
@@ -84,6 +91,8 @@
       ReadCompressedIconWithPurposeCallback callback) const override;
   SkBitmap GetFavicon(const AppId& app_id) const override;
 
+  gfx::ImageSkia GetFaviconImageSkia(const AppId& app_id) const;
+
   // AppRegistrarObserver:
   void OnWebAppInstalled(const AppId& app_id) override;
   void OnAppRegistrarDestroyed() override;
@@ -97,6 +106,14 @@
                          SquareSizePx desired_icon_size,
                          ReadIconsCallback callback) const;
 
+  // Reads multiple densities of the icon for each supported UI scale factor.
+  // See ui/base/layout.h. Returns null image in |callback| if no icons found
+  // for all supported UI scale factors (matches only bigger icons, no
+  // upscaling).
+  void ReadUiScaleFactorsIcons(const AppId& app_id,
+                               SquareSizeDip size_in_dip,
+                               ReadImageSkiaCallback callback);
+
   void SetFaviconReadCallbackForTesting(FaviconReadCallback callback);
 
  private:
@@ -105,18 +122,22 @@
       const std::vector<IconPurpose>& purposes,
       SquareSizePx max_size) const;
 
+  void OnReadUiScaleFactorsIcons(SquareSizeDip size_in_dip,
+                                 ReadImageSkiaCallback callback,
+                                 std::map<SquareSizePx, SkBitmap> icon_bitmaps);
+
   void ReadFavicon(const AppId& app_id);
-  void OnReadFavicon(const AppId& app_id,
-                     std::map<SquareSizePx, SkBitmap> icons);
+  void OnReadFavicon(const AppId& app_id, gfx::ImageSkia image_skia);
 
   WebAppRegistrar& registrar_;
   base::FilePath web_apps_directory_;
   std::unique_ptr<FileUtilsWrapper> utils_;
 
-  ScopedObserver<AppRegistrar, AppRegistrarObserver> registrar_observer_{this};
+  base::ScopedObservation<AppRegistrar, AppRegistrarObserver>
+      registrar_observation_{this};
 
-  // We cache a single low-resolution icon for each app.
-  std::map<AppId, SkBitmap> favicon_cache_;
+  // We cache different densities for high-DPI displays per each app.
+  std::map<AppId, gfx::ImageSkia> favicon_cache_;
 
   FaviconReadCallback favicon_read_callback_;
 
diff --git a/chrome/browser/web_applications/web_app_icon_manager_unittest.cc b/chrome/browser/web_applications/web_app_icon_manager_unittest.cc
index a53dbcd..673cc24 100644
--- a/chrome/browser/web_applications/web_app_icon_manager_unittest.cc
+++ b/chrome/browser/web_applications/web_app_icon_manager_unittest.cc
@@ -33,6 +33,8 @@
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/skia/include/core/SkBitmap.h"
 #include "third_party/skia/include/core/SkColor.h"
+#include "ui/base/layout.h"
+#include "ui/base/resource/scale_factor.h"
 #include "ui/gfx/favicon_size.h"
 
 namespace web_app {
@@ -278,6 +280,17 @@
     return web_app;
   }
 
+  void StartIconManagerWaitFavicon(const AppId& app_id) {
+    base::RunLoop run_loop;
+    icon_manager().SetFaviconReadCallbackForTesting(
+        base::BindLambdaForTesting([&](const AppId& cached_app_id) {
+          EXPECT_EQ(cached_app_id, app_id);
+          run_loop.Quit();
+        }));
+    icon_manager().Start();
+    run_loop.Run();
+  }
+
   TestWebAppRegistryController& controller() {
     return *test_registry_controller_;
   }
@@ -1160,15 +1173,7 @@
 
   controller().RegisterApp(std::move(web_app));
 
-  base::RunLoop run_loop;
-  icon_manager().SetFaviconReadCallbackForTesting(
-      base::BindLambdaForTesting([&](const AppId& cached_app_id) {
-        EXPECT_EQ(cached_app_id, app_id);
-        run_loop.Quit();
-      }));
-
-  icon_manager().Start();
-  run_loop.Run();
+  StartIconManagerWaitFavicon(app_id);
 
   SkBitmap bitmap = icon_manager().GetFavicon(app_id);
   EXPECT_FALSE(bitmap.empty());
@@ -1191,15 +1196,7 @@
 
   controller().RegisterApp(std::move(web_app));
 
-  base::RunLoop run_loop;
-  icon_manager().SetFaviconReadCallbackForTesting(
-      base::BindLambdaForTesting([&](const AppId& cached_app_id) {
-        EXPECT_EQ(cached_app_id, app_id);
-        run_loop.Quit();
-      }));
-
-  icon_manager().Start();
-  run_loop.Run();
+  StartIconManagerWaitFavicon(app_id);
 
   SkBitmap bitmap = icon_manager().GetFavicon(app_id);
   EXPECT_FALSE(bitmap.empty());
@@ -1240,4 +1237,179 @@
   EXPECT_EQ(SK_ColorBLUE, bitmap.getColor(0, 0));
 }
 
+TEST_F(WebAppIconManagerTest, CacheAppFavicon_UiScaleFactors_NoMissingIcons) {
+  ui::SetSupportedScaleFactors(
+      {ui::SCALE_FACTOR_100P, ui::SCALE_FACTOR_200P, ui::SCALE_FACTOR_300P});
+
+  std::unique_ptr<WebApp> web_app = CreateWebApp();
+  const AppId app_id = web_app->app_id();
+
+  // App declares icons precisely matching suspported UI scale factors.
+  const std::vector<int> sizes_px{icon_size::k16, icon_size::k32,
+                                  icon_size::k64};
+  ASSERT_TRUE(base::Contains(sizes_px, gfx::kFaviconSize));
+
+  const std::vector<SkColor> colors{SK_ColorYELLOW, SK_ColorGREEN, SK_ColorRED};
+  WriteIcons(app_id, {IconPurpose::ANY}, sizes_px, colors);
+
+  web_app->SetDownloadedIconSizes(IconPurpose::ANY, sizes_px);
+
+  controller().RegisterApp(std::move(web_app));
+
+  StartIconManagerWaitFavicon(app_id);
+
+  gfx::ImageSkia image_skia = icon_manager().GetFaviconImageSkia(app_id);
+  ASSERT_FALSE(image_skia.isNull());
+
+  EXPECT_EQ(gfx::kFaviconSize, image_skia.width());
+  EXPECT_EQ(gfx::kFaviconSize, image_skia.height());
+  {
+    SCOPED_TRACE(icon_size::k16);
+    ExpectImageSkiaRep(image_skia, /*scale=*/1.0f, /*size_px=*/icon_size::k16,
+                       SK_ColorYELLOW);
+  }
+  {
+    SCOPED_TRACE(icon_size::k32);
+    ExpectImageSkiaRep(image_skia, /*scale=*/2.0f, /*size_px=*/icon_size::k32,
+                       SK_ColorGREEN);
+  }
+  {
+    SCOPED_TRACE(icon_size::k48);
+    ExpectImageSkiaRep(image_skia, /*scale=*/3.0f, /*size_px=*/icon_size::k48,
+                       SK_ColorRED);
+  }
+}
+
+TEST_F(WebAppIconManagerTest, CacheAppFavicon_UiScaleFactors_DownsizingIcons) {
+  ui::SetSupportedScaleFactors({ui::SCALE_FACTOR_100P, ui::SCALE_FACTOR_200P});
+
+  std::unique_ptr<WebApp> web_app = CreateWebApp();
+  const AppId app_id = web_app->app_id();
+
+  // App declares only bigger icons, forcing a downsize to suspported UI scale
+  // factors.
+  const std::vector<int> sizes_px{icon_size::k24, icon_size::k48};
+  ASSERT_FALSE(base::Contains(sizes_px, gfx::kFaviconSize));
+
+  const std::vector<SkColor> colors{SK_ColorCYAN, SK_ColorMAGENTA};
+  WriteIcons(app_id, {IconPurpose::ANY}, sizes_px, colors);
+
+  web_app->SetDownloadedIconSizes(IconPurpose::ANY, sizes_px);
+
+  controller().RegisterApp(std::move(web_app));
+
+  StartIconManagerWaitFavicon(app_id);
+
+  gfx::ImageSkia image_skia = icon_manager().GetFaviconImageSkia(app_id);
+  ASSERT_FALSE(image_skia.isNull());
+
+  EXPECT_EQ(gfx::kFaviconSize, image_skia.width());
+  EXPECT_EQ(gfx::kFaviconSize, image_skia.height());
+  {
+    SCOPED_TRACE(icon_size::k16);
+    ExpectImageSkiaRep(image_skia, /*scale=*/1.0f, /*size_px=*/icon_size::k16,
+                       SK_ColorCYAN);
+  }
+  {
+    SCOPED_TRACE(icon_size::k32);
+    ExpectImageSkiaRep(image_skia, /*scale=*/2.0f, /*size_px=*/icon_size::k32,
+                       SK_ColorMAGENTA);
+  }
+}
+
+TEST_F(WebAppIconManagerTest, CacheAppFavicon_UiScaleFactors_NoIcons) {
+  ui::SetSupportedScaleFactors({ui::SCALE_FACTOR_100P, ui::SCALE_FACTOR_200P});
+
+  std::unique_ptr<WebApp> web_app = CreateWebApp();
+  const AppId app_id = web_app->app_id();
+  controller().RegisterApp(std::move(web_app));
+
+  StartIconManagerWaitFavicon(app_id);
+
+  gfx::ImageSkia image_skia = icon_manager().GetFaviconImageSkia(app_id);
+  EXPECT_TRUE(image_skia.isNull());
+}
+
+TEST_F(WebAppIconManagerTest, CacheAppFavicon_UiScaleFactors_NoMatchSmaller) {
+  ui::SetSupportedScaleFactors({ui::SCALE_FACTOR_200P, ui::SCALE_FACTOR_300P});
+
+  std::unique_ptr<WebApp> web_app = CreateWebApp();
+  const AppId app_id = web_app->app_id();
+
+  // App declares only smaller icon and implementations ignore it: no upsizing.
+  const std::vector<int> sizes_px{icon_size::k16};
+  WriteIcons(app_id, {IconPurpose::ANY}, sizes_px, /*colors=*/{SK_ColorRED});
+  web_app->SetDownloadedIconSizes(IconPurpose::ANY, sizes_px);
+
+  controller().RegisterApp(std::move(web_app));
+
+  StartIconManagerWaitFavicon(app_id);
+
+  gfx::ImageSkia image_skia = icon_manager().GetFaviconImageSkia(app_id);
+  EXPECT_TRUE(image_skia.isNull());
+}
+
+TEST_F(WebAppIconManagerTest,
+       CacheAppFavicon_UiScaleFactors_DownsizingFromSingleIcon) {
+  ui::SetSupportedScaleFactors({ui::SCALE_FACTOR_100P, ui::SCALE_FACTOR_200P});
+
+  std::unique_ptr<WebApp> web_app = CreateWebApp();
+  const AppId app_id = web_app->app_id();
+
+  // App declares only one jumbo icon.
+  const std::vector<int> sizes_px{icon_size::k512};
+  WriteIcons(app_id, {IconPurpose::ANY}, sizes_px, /*colors=*/{SK_ColorLTGRAY});
+  web_app->SetDownloadedIconSizes(IconPurpose::ANY, sizes_px);
+
+  controller().RegisterApp(std::move(web_app));
+
+  StartIconManagerWaitFavicon(app_id);
+
+  gfx::ImageSkia image_skia = icon_manager().GetFaviconImageSkia(app_id);
+  ASSERT_FALSE(image_skia.isNull());
+
+  EXPECT_EQ(gfx::kFaviconSize, image_skia.width());
+  EXPECT_EQ(gfx::kFaviconSize, image_skia.height());
+  {
+    SCOPED_TRACE(icon_size::k16);
+    ExpectImageSkiaRep(image_skia, /*scale=*/1.0f, /*size_px=*/icon_size::k16,
+                       SK_ColorLTGRAY);
+  }
+  {
+    SCOPED_TRACE(icon_size::k32);
+    ExpectImageSkiaRep(image_skia, /*scale=*/2.0f, /*size_px=*/icon_size::k32,
+                       SK_ColorLTGRAY);
+  }
+}
+
+TEST_F(WebAppIconManagerTest,
+       CacheAppFavicon_UiScaleFactors_BiggerUiScaleFactorIconMissing) {
+  ui::SetSupportedScaleFactors({ui::SCALE_FACTOR_100P, ui::SCALE_FACTOR_300P});
+
+  std::unique_ptr<WebApp> web_app = CreateWebApp();
+  const AppId app_id = web_app->app_id();
+
+  // App declares the icon which is ok for 100P but small for 300P.
+  const std::vector<int> sizes_px{icon_size::k32};
+  WriteIcons(app_id, {IconPurpose::ANY}, sizes_px, /*colors=*/{SK_ColorDKGRAY});
+  web_app->SetDownloadedIconSizes(IconPurpose::ANY, sizes_px);
+
+  controller().RegisterApp(std::move(web_app));
+
+  StartIconManagerWaitFavicon(app_id);
+
+  gfx::ImageSkia image_skia = icon_manager().GetFaviconImageSkia(app_id);
+  ASSERT_FALSE(image_skia.isNull());
+
+  EXPECT_EQ(gfx::kFaviconSize, image_skia.width());
+  EXPECT_EQ(gfx::kFaviconSize, image_skia.height());
+  {
+    SCOPED_TRACE(icon_size::k16);
+    ExpectImageSkiaRep(image_skia, /*scale=*/1.0f, /*size_px=*/icon_size::k16,
+                       SK_ColorDKGRAY);
+  }
+  EXPECT_FALSE(image_skia.HasRepresentation(2.0f));
+  EXPECT_FALSE(image_skia.HasRepresentation(3.0f));
+}
+
 }  // namespace web_app
diff --git a/chrome/browser/web_applications/web_app_tab_helper.cc b/chrome/browser/web_applications/web_app_tab_helper.cc
index 3b49c3ee..84f07a49 100644
--- a/chrome/browser/web_applications/web_app_tab_helper.cc
+++ b/chrome/browser/web_applications/web_app_tab_helper.cc
@@ -35,7 +35,7 @@
       provider_(WebAppProviderBase::GetProviderBase(
           Profile::FromBrowserContext(web_contents->GetBrowserContext()))) {
   DCHECK(provider_);
-  observer_.Add(&provider_->registrar());
+  observation_.Observe(&provider_->registrar());
   SetAppId(
       FindAppIdWithUrlInScope(web_contents->GetSiteInstance()->GetSiteURL()));
 }
@@ -159,7 +159,7 @@
 }
 
 void WebAppTabHelper::OnAppRegistrarDestroyed() {
-  observer_.RemoveAll();
+  observation_.Reset();
 }
 
 void WebAppTabHelper::ResetAppId() {
diff --git a/chrome/browser/web_applications/web_app_tab_helper.h b/chrome/browser/web_applications/web_app_tab_helper.h
index c887e75..a64dd7d6 100644
--- a/chrome/browser/web_applications/web_app_tab_helper.h
+++ b/chrome/browser/web_applications/web_app_tab_helper.h
@@ -5,7 +5,7 @@
 #ifndef CHROME_BROWSER_WEB_APPLICATIONS_WEB_APP_TAB_HELPER_H_
 #define CHROME_BROWSER_WEB_APPLICATIONS_WEB_APP_TAB_HELPER_H_
 
-#include "base/scoped_observer.h"
+#include "base/scoped_observation.h"
 #include "base/unguessable_token.h"
 #include "chrome/browser/web_applications/components/app_registrar.h"
 #include "chrome/browser/web_applications/components/app_registrar_observer.h"
@@ -91,7 +91,8 @@
 
   bool has_loaded_non_about_blank_page_ = false;
 
-  ScopedObserver<AppRegistrar, AppRegistrarObserver> observer_{this};
+  base::ScopedObservation<AppRegistrar, AppRegistrarObserver> observation_{
+      this};
   WebAppProviderBase* provider_ = nullptr;
 
 };
diff --git a/chrome/browser/window_placement/window_placement_browsertest.cc b/chrome/browser/window_placement/window_placement_browsertest.cc
index ae2a976e..8217902 100644
--- a/chrome/browser/window_placement/window_placement_browsertest.cc
+++ b/chrome/browser/window_placement/window_placement_browsertest.cc
@@ -82,7 +82,7 @@
         });
       }
       var makeScreensChangePromise = () => {
-        return promiseForEvent(screensInterface, 'change');
+        return promiseForEvent(screensInterface, 'screenschange');
       };
       var getScreenWidths = () => {
         return screensInterface.screens.map((d) => d.width).sort();
@@ -176,3 +176,247 @@
     )"));
   }
 }
+
+// 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_OnCurrentScreenChangeEvent DISABLED_OnCurrentScreenChangeEvent
+#else
+#define MAYBE_OnCurrentScreenChangeEvent OnCurrentScreenChangeEvent
+#endif
+// Test that the oncurrentscreenchange handler fires correctly for screen
+// changes and property updates.
+IN_PROC_BROWSER_TEST_F(WindowPlacementTest, MAYBE_OnCurrentScreenChangeEvent) {
+#if BUILDFLAG(IS_CHROMEOS_ASH)
+  display::test::DisplayManagerTestApi(ash::Shell::Get()->display_manager())
+      .UpdateDisplay("100+100-801x802,901+100-802x802");
+#else
+  display::ScreenBase screen;
+  screen.display_list().AddDisplay({1, gfx::Rect(100, 100, 801, 802)},
+                                   display::DisplayList::Type::PRIMARY);
+  screen.display_list().AddDisplay({2, gfx::Rect(901, 100, 802, 802)},
+                                   display::DisplayList::Type::NOT_PRIMARY);
+  display::test::ScopedScreenOverride screen_override(&screen);
+#endif  // BUILDFLAG(IS_CHROMEOS_ASH)
+  ASSERT_EQ(2, 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);
+
+  EXPECT_EQ(801, 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 makeCurrentScreenChangePromise = () => {
+        return promiseForEvent(screensInterface, 'currentscreenchange');
+      };
+      (async () => {
+          screensInterface = await self.getScreens();
+          return screensInterface.currentScreen.width;
+      })();
+  )"));
+
+  // Switch to a second display.  This should fire an event.
+  EXPECT_TRUE(ExecJs(tab, R"(var change = makeCurrentScreenChangePromise();)"));
+
+  const gfx::Rect new_bounds(1000, 150, 600, 500);
+  browser()->window()->SetBounds(new_bounds);
+
+  EXPECT_EQ(802, EvalJs(tab, R"(
+      (async () => {
+          await change;
+          return screensInterface.currentScreen.width;
+      })();
+    )"));
+
+  // Update the second display to have a height of 300.  Validate that a change
+  // event is fired when attributes of the current screen change.
+  EXPECT_TRUE(ExecJs(tab, R"(var change = makeCurrentScreenChangePromise();)"));
+
+#if BUILDFLAG(IS_CHROMEOS_ASH)
+  display::test::DisplayManagerTestApi(ash::Shell::Get()->display_manager())
+      .UpdateDisplay("100+100-801x802,901+100-802x300");
+#else
+  screen.display_list().UpdateDisplay({2, gfx::Rect(901, 100, 802, 300)},
+                                      display::DisplayList::Type::NOT_PRIMARY);
+#endif  // BUILDFLAG(IS_CHROMEOS_ASH)
+
+  EXPECT_EQ(300, EvalJs(tab, R"(
+      (async () => {
+          await change;
+          return screensInterface.currentScreen.height;
+      })();
+    )"));
+}
+
+// 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_ScreenAdvancedOnChange DISABLED_ScreenAdvancedOnChange
+#else
+#define MAYBE_ScreenAdvancedOnChange ScreenAdvancedOnChange
+#endif
+// Test that onchange events for individual screens in the screen list are
+// supported.
+IN_PROC_BROWSER_TEST_F(WindowPlacementTest, MAYBE_ScreenAdvancedOnChange) {
+#if BUILDFLAG(IS_CHROMEOS_ASH)
+  display::test::DisplayManagerTestApi(ash::Shell::Get()->display_manager())
+      .UpdateDisplay("100+100-801x802,901+100-802x802");
+#else
+  display::ScreenBase screen;
+  screen.display_list().AddDisplay({1, gfx::Rect(100, 100, 801, 802)},
+                                   display::DisplayList::Type::PRIMARY);
+  screen.display_list().AddDisplay({2, gfx::Rect(901, 100, 802, 802)},
+                                   display::DisplayList::Type::NOT_PRIMARY);
+  display::test::ScopedScreenOverride screen_override(&screen);
+#endif  // BUILDFLAG(IS_CHROMEOS_ASH)
+  ASSERT_EQ(2, 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);
+
+  EXPECT_EQ(true, 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 screenChanges0 = 0;
+      var screenChanges1 = 0;
+      (async () => {
+        screensInterface = await self.getScreens();
+        if (screensInterface.screens.length !== 2)
+          return false;
+        // Add some event listeners for individual screens.
+        screensInterface.screens[0].addEventListener('change', () => {
+          screenChanges0++;
+        });
+        screensInterface.screens[1].addEventListener('change', () => {
+          screenChanges1++;
+        });
+        return true;
+      })();
+  )"));
+
+  // Update only the first display to have a different height.
+  EXPECT_TRUE(ExecJs(tab,
+                     R"(
+    var change0 = promiseForEvent(screensInterface.screens[0], 'change');
+    )"));
+
+#if BUILDFLAG(IS_CHROMEOS_ASH)
+  display::test::DisplayManagerTestApi(ash::Shell::Get()->display_manager())
+      .UpdateDisplay("100+100-801x301,901+100-802x802");
+#else
+  screen.display_list().UpdateDisplay({1, gfx::Rect(100, 100, 801, 301)},
+                                      display::DisplayList::Type::PRIMARY);
+#endif  // BUILDFLAG(IS_CHROMEOS_ASH)
+
+  EXPECT_EQ(301, EvalJs(tab, R"(
+      (async () => {
+          await change0;
+          // Only screen[0] should have changed.
+          if (screenChanges0 !== 1)
+            return -1;
+          if (screenChanges1 !== 0)
+            return -2;
+          return screensInterface.screens[0].height;
+      })();
+    )"));
+
+  // Update only the second display to have a different height.
+  EXPECT_TRUE(ExecJs(tab,
+                     R"(
+    var change1 = promiseForEvent(screensInterface.screens[1], 'change');
+    )"));
+
+#if BUILDFLAG(IS_CHROMEOS_ASH)
+  display::test::DisplayManagerTestApi(ash::Shell::Get()->display_manager())
+      .UpdateDisplay("100+100-801x301,901+100-802x302");
+#else
+  screen.display_list().UpdateDisplay({2, gfx::Rect(901, 100, 802, 302)},
+                                      display::DisplayList::Type::NOT_PRIMARY);
+#endif  // BUILDFLAG(IS_CHROMEOS_ASH)
+
+  EXPECT_EQ(302, EvalJs(tab, R"(
+      (async () => {
+          await change1;
+          // Both screens have one change.
+          if (screenChanges0 !== 1)
+            return -1;
+          if (screenChanges1 !== 1)
+            return -2;
+          return screensInterface.screens[1].height;
+      })();
+    )"));
+
+  // Change the width of both displays at the same time.
+  EXPECT_TRUE(ExecJs(tab,
+                     R"(
+    var change0 = promiseForEvent(screensInterface.screens[0], 'change');
+    var change1 = promiseForEvent(screensInterface.screens[1], 'change');
+    )"));
+
+#if BUILDFLAG(IS_CHROMEOS_ASH)
+  display::test::DisplayManagerTestApi(ash::Shell::Get()->display_manager())
+      .UpdateDisplay("100+100-401x301,901+100-402x302");
+#else
+  screen.display_list().UpdateDisplay({1, gfx::Rect(100, 100, 401, 301)},
+                                      display::DisplayList::Type::PRIMARY);
+  screen.display_list().UpdateDisplay({2, gfx::Rect(901, 100, 402, 302)},
+                                      display::DisplayList::Type::NOT_PRIMARY);
+#endif  // BUILDFLAG(IS_CHROMEOS_ASH)
+
+  EXPECT_EQ(true, EvalJs(tab, R"(
+      (async () => {
+          await change0;
+          await change1;
+          // Both screens have two changes
+          if (screenChanges0 !== 2)
+            return false;
+          if (screenChanges1 !== 2)
+            return false;
+          if (screensInterface.screens[0].width !== 401)
+            return false;
+          if (screensInterface.screens[1].width !== 402)
+            return false;
+          return true;
+      })();
+    )"));
+}
diff --git a/chrome/build/win32.pgo.txt b/chrome/build/win32.pgo.txt
index a2a84b8a3..9c34dbb 100644
--- a/chrome/build/win32.pgo.txt
+++ b/chrome/build/win32.pgo.txt
@@ -1 +1 @@
-chrome-win32-master-1620669576-eca74dd9dc869ac7b2d0cce1b0e16344e0f93027.profdata
+chrome-win32-master-1620701975-f869911131b118afc1b5448cb8843a0118ca7edd.profdata
diff --git a/chrome/build/win64.pgo.txt b/chrome/build/win64.pgo.txt
index bee3eb50..ee51901 100644
--- a/chrome/build/win64.pgo.txt
+++ b/chrome/build/win64.pgo.txt
@@ -1 +1 @@
-chrome-win64-master-1620669576-b0b725c4136c96af46839162c5f32c69dca5b1ab.profdata
+chrome-win64-master-1620701975-e0fcd5af2f55f15f02a3562c76e18f90c8eecce2.profdata
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn
index 3d4f3dc..e9e5359 100644
--- a/chrome/test/BUILD.gn
+++ b/chrome/test/BUILD.gn
@@ -226,7 +226,9 @@
     "//chrome:strings",
 
     #"//chrome/app/theme:theme_resources",
+    "//chrome/browser:browser_process",
     "//chrome/browser:test_support",
+    "//chrome/browser/safe_browsing",
     "//chrome/child",
     "//chrome/common:non_code_constants",
     "//chrome/common:test_support",
@@ -236,6 +238,11 @@
     "//components/bookmarks/test",
     "//components/captive_portal/core:test_support",
     "//components/consent_auditor:test_support",
+    "//components/content_settings/core/browser",
+    "//components/crash/core/app",
+    "//components/domain_reliability",
+    "//components/federated_learning",
+    "//components/find_in_page",
     "//components/gcm_driver:test_support",
     "//components/gcm_driver/crypto:test_support",
     "//components/gcm_driver/instance_id:test_support",
@@ -243,26 +250,38 @@
     "//components/infobars/core",
     "//components/metrics:test_support",
     "//components/network_session_configurator/common",
+    "//components/network_time",
     "//components/network_time:network_time_test_support",
     "//components/omnibox/browser:test_support",
+    "//components/os_crypt",
     "//components/password_manager/core/browser:test_support",
     "//components/payments/core:test_support",
     "//components/performance_manager/test_support",
+    "//components/permissions",
     "//components/permissions:test_support",
+    "//components/policy/core/browser",
+    "//components/policy/core/common:test_support",
     "//components/prefs:test_support",
+    "//components/profile_metrics",
+    "//components/safe_browsing/core/db:database_manager",
     "//components/safe_browsing/core/db:v4_test_util",
     "//components/search_engines:test_support",
     "//components/sessions:test_support",
     "//components/signin/public/base:test_support",
+    "//components/startup_metric_utils/browser",
+    "//components/subresource_filter/content/browser",
     "//components/subresource_filter/content/browser:test_support",
     "//components/subresource_filter/core/common",
     "//components/sync:test_support",
+    "//components/sync_preferences",
     "//components/sync_preferences:test_support",
     "//components/sync_sessions:test_support",
     "//components/sync_user_events:test_support",
     "//components/update_client:test_support",
+    "//components/user_prefs",
     "//components/variations:test_support",
     "//components/web_resource:test_support",
+    "//components/webdata_services",
     "//content/public/app",
     "//content/public/child",
     "//content/public/common",
@@ -280,6 +299,9 @@
     "//ppapi/buildflags",
     "//printing/buildflags",
     "//services/cert_verifier:test_support",
+    "//services/data_decoder/public/cpp:test_support",
+    "//services/device/public/cpp:test_support",
+    "//services/device/public/cpp/geolocation",
     "//skia",
     "//sql",
     "//sql:test_support",
@@ -287,12 +309,17 @@
     "//testing/gtest",
     "//third_party/leveldatabase",
     "//ui/base",
+    "//ui/base:test_support",
     "//ui/events:events_base",
     "//ui/gfx:test_support",
     "//ui/gl",
   ]
 
   if (is_android) {
+    public_deps += [
+      ":test_support_ui_android",
+      "//chrome:chrome_android_core",
+    ]
     if (enable_vr) {
       public_deps += [ "//chrome/browser/android/vr:test_support" ]
 
@@ -303,10 +330,23 @@
     public_deps += [
       # Android uses //chrome:chrome_android_core instead of the //chrome/app
       # target.
+      ":test_support_ui",
       "//chrome/app:test_support",
+      "//chrome/browser/web_applications",
+      "//chrome/browser/web_applications/components",
+      "//components/crx_file",
+      "//components/keep_alive_registry",
+      "//components/pref_registry",
+      "//components/storage_monitor",
+      "//components/storage_monitor:test_support",
       "//components/ukm:test_support",
       "//components/ukm:ukm_test_helper",
+      "//components/web_modal",
+      "//components/zoom",
       "//components/zoom:test_support",
+      "//extensions/browser:test_support",
+      "//extensions/common:test_support",
+      "//ui/snapshot",
     ]
     sources += [
       "../browser/enterprise/reporting/extension_request/extension_request_report_throttler_test.cc",
@@ -329,7 +369,11 @@
       "//chrome/app/chrome_crash_reporter_client.cc",
       "//chrome/app/chrome_crash_reporter_client_mac.mm",
     ]
-    deps += [ "//build:branding_buildflags" ]
+    deps += [
+      "//build:branding_buildflags",
+      "//chrome/app_shim",
+      "//components/upload_list",
+    ]
     public_deps += [
       "//components/crash/core/app",
       "//third_party/breakpad",
@@ -360,6 +404,7 @@
       "//chrome/app/chrome_crash_reporter_client_win.cc",
     ]
     public_deps += [
+      "//chrome/chrome_elf:crash",
       "//chrome/install_static/test:test_support",
       "//components/crash/core/app",
       "//third_party/wtl",
@@ -403,11 +448,23 @@
     public_deps += [
       "//ash",
       "//ash:test_support",
+      "//ash/components/account_manager",
+      "//ash/constants",
+      "//chrome/browser/chromeos",
+      "//chromeos/dbus/session_manager",
+      "//chromeos/dbus/tpm_manager",
+      "//chromeos/dbus/userdataauth",
+      "//chromeos/login/login_state",
+      "//chromeos/settings",
+      "//chromeos/tpm",
+      "//chromeos/tpm:test_support",
+      "//components/exo",
       "//components/ownership",
       "//components/user_manager:test_support",
       "//ui/aura",
       "//ui/aura:test_support",
       "//ui/base/ime/init",
+      "//ui/chromeos/resources:resources_grit",
     ]
     deps += [
       "//chromeos/cryptohome",
@@ -417,7 +474,11 @@
   }
 
   if (is_chromeos_lacros) {
-    deps += [ "//chromeos/services/machine_learning/public/cpp:stub" ]
+    deps += [
+      "//chromeos/lacros",
+      "//chromeos/lacros:test_support",
+      "//chromeos/services/machine_learning/public/cpp:stub",
+    ]
   }
 
   if (is_win || is_mac || (is_linux || is_chromeos_lacros)) {
@@ -433,6 +494,11 @@
       "base/test_browser_window_aura.cc",
       "base/test_browser_window_aura.h",
     ]
+    deps += [
+      "//ui/aura:test_support",
+      "//ui/wm",
+      "//ui/wm/public",
+    ]
   }
 
   if (toolkit_views) {
@@ -1635,6 +1701,9 @@
       "../renderer/translate/translate_script_browsertest.cc",
       "base/chrome_render_view_test.cc",
       "base/chrome_render_view_test.h",
+      "base/devtools_listener.cc",
+      "base/devtools_listener.h",
+      "base/devtools_listener_browsertest.cc",
       "base/in_process_browser_test_browsertest.cc",
       "base/memory_tracing_browsertest.cc",
       "base/test_chrome_web_ui_controller_factory_browsertest.cc",
@@ -2744,9 +2813,6 @@
         "../browser/chromeos/extensions/wallpaper_manager_browsertest.cc",
         "../browser/chromeos/extensions/wallpaper_private_apitest.cc",
         "../browser/chromeos/file_manager/audio_player_browsertest.cc",
-        "../browser/chromeos/file_manager/devtools_listener.cc",
-        "../browser/chromeos/file_manager/devtools_listener.h",
-        "../browser/chromeos/file_manager/devtools_listener_browsertest.cc",
         "../browser/chromeos/file_manager/external_filesystem_apitest.cc",
         "../browser/chromeos/file_manager/file_manager_browsertest.cc",
         "../browser/chromeos/file_manager/file_manager_browsertest_base.cc",
@@ -4880,6 +4946,7 @@
       "../browser/feedback/system_logs/log_sources/crash_ids_source_unittest.cc",
 
       # NTP is in native code on Android.
+      "../browser/cart/cart_discount_fetcher_unittest.cc",
       "../browser/cart/cart_handler_unittest.cc",
       "../browser/cart/cart_service_unittest.cc",
       "../browser/cart/fetch_discount_worker_unittest.cc",
@@ -6708,7 +6775,6 @@
       "../browser/ui/views/content_test_utils.h",
       "../browser/ui/views/web_apps/frame_toolbar/web_app_frame_toolbar_interactive_uitest.cc",
       "../browser/ui/views/web_dialog_view_browsertest.cc",
-      "../browser/ui/web_applications/test/system_web_app_interactive_uitest.cc",
       "../browser/webauth_interactive_uitest.cc",
       "//ui/base/clipboard/clipboard_unittest.cc",
       "base/interactive_test_utils.cc",
@@ -6906,6 +6972,10 @@
           "//components/media_router/browser:test_support",
         ]
       }
+      if (!is_android && !is_chromeos_lacros) {
+        sources += [ "../browser/ui/web_applications/test/system_web_app_interactive_uitest.cc" ]
+      }
+
       if (use_aura) {
         sources += [ "../browser/ui/views/tooltip/tooltip_browsertest.cc" ]
       }
@@ -7090,11 +7160,7 @@
 }
 
 test("chrome_app_unittests") {
-  sources = [
-    "../app/chrome_main_delegate.cc",
-    "../app/chrome_main_delegate.h",
-    "../app/resources/resources_unittest.cc",
-  ]
+  sources = [ "../app/resources/resources_unittest.cc" ]
   deps = [
     ":test_support",
     "//base/test:run_all_unittests",
@@ -7118,11 +7184,7 @@
     deps += [ "//components/gwp_asan/client" ]
   }
   if (is_android) {
-    sources += [
-      "../app/android/chrome_main_delegate_android.cc",
-      "../app/android/chrome_main_delegate_android.h",
-      "../app/android/chrome_main_delegate_android_unittest.cc",
-    ]
+    sources += [ "../app/android/chrome_main_delegate_android_unittest.cc" ]
   }
 }
 
diff --git a/chrome/browser/chromeos/file_manager/devtools_listener.cc b/chrome/test/base/devtools_listener.cc
similarity index 95%
rename from chrome/browser/chromeos/file_manager/devtools_listener.cc
rename to chrome/test/base/devtools_listener.cc
index f52d085..aba5414 100644
--- a/chrome/browser/chromeos/file_manager/devtools_listener.cc
+++ b/chrome/test/base/devtools_listener.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/chromeos/file_manager/devtools_listener.h"
+#include "chrome/test/base/devtools_listener.h"
 
 #include <stddef.h>
 
@@ -24,7 +24,7 @@
 #include "base/strings/stringprintf.h"
 #include "url/url_util.h"
 
-namespace file_manager {
+namespace coverage {
 
 namespace {
 
@@ -166,16 +166,16 @@
     entries->Append(entry->CreateDeepCopy());
   }
 
-  const std::string url = host->GetURL().spec();
+  std::string url = host->GetURL().spec();
   CHECK(result->SetString("encodedHostURL", EncodeURIComponent(url)));
   CHECK(result->SetString("hostTitle", host->GetTitle()));
   CHECK(result->SetString("hostType", host->GetType()));
   CHECK(result->SetString("hostTest", test));
   CHECK(result->SetString("hostURL", url));
 
-  const std::string md5 = base::MD5String(HostString(host, test));
+  std::string md5 = base::MD5String(HostString(host, test));
   std::string coverage = base::StrCat({test, ".", md5, uuid_, ".cov.json"});
-  base::FilePath path = store.AppendASCII("tests").Append(coverage);
+  base::FilePath path = store.AppendASCII("tests").AppendASCII(coverage);
 
   CHECK(result->SetList("result", std::move(entries)));
   CHECK(base::JSONWriter::Write(*result, &coverage));
@@ -235,7 +235,7 @@
     CHECK(script->SetString("url", url));
 
     base::FilePath path =
-        store.AppendASCII("scripts").Append(hash.append(".js.json"));
+        store.AppendASCII("scripts").AppendASCII(hash.append(".js.json"));
     CHECK(base::JSONWriter::Write(*script, &text));
     if (!base::PathExists(path))  // script de-duplication
       base::WriteFile(path, text.data(), text.size());
@@ -297,4 +297,4 @@
   attached_ = false;
 }
 
-}  // namespace file_manager
+}  // namespace coverage
diff --git a/chrome/browser/chromeos/file_manager/devtools_listener.h b/chrome/test/base/devtools_listener.h
similarity index 92%
rename from chrome/browser/chromeos/file_manager/devtools_listener.h
rename to chrome/test/base/devtools_listener.h
index d81c9bd..d55fd1e 100644
--- a/chrome/browser/chromeos/file_manager/devtools_listener.h
+++ b/chrome/test/base/devtools_listener.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CHROME_BROWSER_CHROMEOS_FILE_MANAGER_DEVTOOLS_LISTENER_H_
-#define CHROME_BROWSER_CHROMEOS_FILE_MANAGER_DEVTOOLS_LISTENER_H_
+#ifndef CHROME_TEST_BASE_DEVTOOLS_LISTENER_H_
+#define CHROME_TEST_BASE_DEVTOOLS_LISTENER_H_
 
 #include <map>
 #include <memory>
@@ -14,7 +14,7 @@
 #include "content/public/browser/devtools_agent_host.h"
 #include "content/public/browser/devtools_agent_host_client.h"
 
-namespace file_manager {
+namespace coverage {
 
 // Collects code coverage from a WebContents, during a browser test
 // for example, using Chrome Devtools Protocol (CDP).
@@ -93,6 +93,6 @@
   bool attached_ = true;
 };
 
-}  // namespace file_manager
+}  // namespace coverage
 
-#endif  // CHROME_BROWSER_CHROMEOS_FILE_MANAGER_DEVTOOLS_LISTENER_H_
+#endif  // CHROME_TEST_BASE_DEVTOOLS_LISTENER_H_
diff --git a/chrome/browser/chromeos/file_manager/devtools_listener_browsertest.cc b/chrome/test/base/devtools_listener_browsertest.cc
similarity index 96%
rename from chrome/browser/chromeos/file_manager/devtools_listener_browsertest.cc
rename to chrome/test/base/devtools_listener_browsertest.cc
index 279949de..9f1eddb 100644
--- a/chrome/browser/chromeos/file_manager/devtools_listener_browsertest.cc
+++ b/chrome/test/base/devtools_listener_browsertest.cc
@@ -10,10 +10,10 @@
 #include "base/path_service.h"
 #include "base/process/process_handle.h"
 #include "base/threading/thread_restrictions.h"
-#include "chrome/browser/chromeos/file_manager/devtools_listener.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/tabs/tab_strip_model.h"
 #include "chrome/common/chrome_paths.h"
+#include "chrome/test/base/devtools_listener.h"
 #include "chrome/test/base/in_process_browser_test.h"
 #include "chrome/test/base/ui_test_utils.h"
 #include "content/public/browser/devtools_agent_host.h"
@@ -23,7 +23,7 @@
 #include "net/test/embedded_test_server/embedded_test_server.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
-namespace file_manager {
+namespace coverage {
 
 class DevToolsListenerBrowserTest : public content::DevToolsAgentHostObserver,
                                     public InProcessBrowserTest {
@@ -118,4 +118,4 @@
   content::RunAllTasksUntilIdle();
 }
 
-}  // namespace file_manager
+}  // namespace coverage
diff --git a/chrome/test/data/webui/chromeos/ash_common/BUILD.gn b/chrome/test/data/webui/chromeos/ash_common/BUILD.gn
index 2dc6d3e..3121736 100644
--- a/chrome/test/data/webui/chromeos/ash_common/BUILD.gn
+++ b/chrome/test/data/webui/chromeos/ash_common/BUILD.gn
@@ -7,6 +7,7 @@
 js_type_check("closure_compile") {
   is_polymer3 = true
   closure_flags = default_closure_args + [
+                    "browser_resolver_prefix_replacements=\"chrome://resources/ash/common/=../../ash/content/common/resources/\"",
                     "js_module_root=../../chrome/test/data/webui/",
                     "js_module_root=./gen/chrome/test/data/webui/",
                   ]
@@ -14,6 +15,7 @@
     ":ash_common_unified_test",
     ":fake_method_resolver_test",
     ":fake_observables_test",
+    ":navigation_selector_test",
   ]
 }
 
@@ -35,3 +37,11 @@
     "//ash/content/common/resources:fake_method_resolver",
   ]
 }
+
+js_library("navigation_selector_test") {
+  deps = [
+    "../..:chai_assert",
+    "//ash/content/common/resources:navigation_selector",
+    "//third_party/polymer/v3_0/components-chromium/iron-collapse:iron-collapse",
+  ]
+}
diff --git a/chrome/test/data/webui/chromeos/ash_common/ash_common_browsertest.js b/chrome/test/data/webui/chromeos/ash_common/ash_common_browsertest.js
index cedaf74d..6ca9d73 100644
--- a/chrome/test/data/webui/chromeos/ash_common/ash_common_browsertest.js
+++ b/chrome/test/data/webui/chromeos/ash_common/ash_common_browsertest.js
@@ -37,6 +37,7 @@
 const debug_suites_list = [
   'FakeObservables',
   'FakeMethodResolver',
+  'NavigationSelector',
 ];
 
 TEST_F('AshCommon', 'BrowserTest', function() {
diff --git a/chrome/test/data/webui/chromeos/ash_common/ash_common_unified_test.js b/chrome/test/data/webui/chromeos/ash_common/ash_common_unified_test.js
index 9820681..6988d26d 100644
--- a/chrome/test/data/webui/chromeos/ash_common/ash_common_unified_test.js
+++ b/chrome/test/data/webui/chromeos/ash_common/ash_common_unified_test.js
@@ -7,6 +7,7 @@
 
 import {fakeMethodResolverTestSuite} from './fake_method_resolver_test.js';
 import {fakeObservablesTestSuite} from './fake_observables_test.js';
+import {navigationSelectorTestSuite} from './navigation_selector_test.js';
 
 window.test_suites_list = [];
 
@@ -16,4 +17,5 @@
 }
 
 runSuite('FakeObservables', fakeObservablesTestSuite);
-runSuite('FakeMethodResolver', fakeMethodResolverTestSuite);
\ No newline at end of file
+runSuite('FakeMethodResolver', fakeMethodResolverTestSuite);
+runSuite('NavigationSelector', navigationSelectorTestSuite);
\ No newline at end of file
diff --git a/chrome/test/data/webui/chromeos/ash_common/navigation_selector_test.js b/chrome/test/data/webui/chromeos/ash_common/navigation_selector_test.js
new file mode 100644
index 0000000..56ae6669
--- /dev/null
+++ b/chrome/test/data/webui/chromeos/ash_common/navigation_selector_test.js
@@ -0,0 +1,118 @@
+// 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 {MenuSelectorItem, NavigationSelectorElement, SelectorItem, SelectorProperties} from 'chrome://resources/ash/common/navigation_selector.js';
+
+import {assertEquals, assertFalse, assertTrue} from '../../chai_assert.js';
+import {waitAfterNextRender} from '../../test_util.m.js';
+
+export function navigationSelectorTestSuite() {
+  /** @type {?NavigationSelectorElement} */
+  let navigationElement = null;
+
+  setup(() => {
+    navigationElement =
+        /** @type {!NavigationSelectorElement} */ (
+            document.createElement('navigation-selector'));
+    document.body.appendChild(navigationElement);
+  });
+
+  teardown(() => {
+    navigationElement.remove();
+    navigationElement = null;
+  });
+
+  /**
+   * @param {string} name
+   * @param {string} pageIs
+   * @return {!SelectorItem}
+   */
+  function createSelectorItem(name, pageIs) {
+    let item = /** @type{SelectorItem} */ ({'name': name, 'pageIs': pageIs});
+    return item;
+  }
+
+  /**
+   * @param {boolean} isCollapsible
+   * @param {boolean} isExpanded
+   * @param {!Array<?SelectorItem>} subMenuItems
+   * @return {!SelectorProperties}
+   */
+  function createProperty(isCollapsible, isExpanded, subMenuItems) {
+    let property = /** @type{SelectorProperties} */ ({
+      'isCollapsible': isCollapsible,
+      'isExpanded': isExpanded,
+      'subMenuItems': subMenuItems,
+    });
+    return property;
+  }
+
+  /**
+   * @param {SelectorItem} selectorItem
+   * @param {SelectorProperties} properties
+   * @return {!MenuSelectorItem}
+   */
+  function createMenuItem(selectorItem, properties) {
+    let menuItem = /** @type{!MenuSelectorItem} */ ({
+      'selectorItem': selectorItem,
+      'properties': properties,
+    });
+    return menuItem;
+  }
+
+  test('navigationSelectorLoadEntries', async () => {
+    const item1 = createSelectorItem('test1', 'test-page1');
+    const item2 = createSelectorItem('test2', 'test-page2');
+
+    const property1 = createProperty(false, false, []);
+    const property2 = createProperty(false, false, []);
+
+    const menuItem1 = createMenuItem(item1, property1);
+    const menuItem2 = createMenuItem(item2, property2);
+
+    const entries =
+        /** @type{!Array<!MenuSelectorItem>} */ ([menuItem1, menuItem2]);
+    navigationElement.menuItems = entries;
+
+    await waitAfterNextRender(navigationElement);
+
+    const navigationElements =
+        navigationElement.shadowRoot.querySelectorAll('.navigation-item');
+    assertEquals(2, navigationElements.length);
+    assertEquals('test1', navigationElements[0].textContent.trim());
+    assertEquals('test2', navigationElements[1].textContent.trim());
+  });
+
+  test('navigationSelectorLoadsCollapsibleEntries', async () => {
+    const item1 = createSelectorItem('test1', 'test-page1');
+    const item2 = createSelectorItem('Advanced', '');
+
+    const property = createProperty(true, false, [item1]);
+
+    const menuItem = createMenuItem(item2, property);
+
+    const entries =
+        /** @type{!Array<!MenuSelectorItem>} */ ([menuItem]);
+    navigationElement.menuItems = entries;
+
+    await waitAfterNextRender(navigationElement);
+
+    const button = navigationElement.shadowRoot.querySelector('.expand-button');
+    const ironCollapse =
+        navigationElement.shadowRoot.querySelector('iron-collapse');
+
+    assertTrue(!!button);
+    assertFalse(ironCollapse.opened);
+    assertEquals('Advanced', button.textContent.trim());
+
+    // Click on the expandable entry.
+    button.click();
+
+    let navigationElements =
+        navigationElement.shadowRoot.querySelectorAll('.navigation-item');
+    assertEquals(1, navigationElements.length);
+    assertEquals('test1', navigationElements[0].textContent.trim());
+    assertTrue(ironCollapse.opened);
+  });
+}
\ No newline at end of file
diff --git a/chrome/test/data/webui/new_tab_page/modules/drive/module_test.js b/chrome/test/data/webui/new_tab_page/modules/drive/module_test.js
index 8d2dc5d..9c4d0ed 100644
--- a/chrome/test/data/webui/new_tab_page/modules/drive/module_test.js
+++ b/chrome/test/data/webui/new_tab_page/modules/drive/module_test.js
@@ -85,6 +85,40 @@
     assertFalse(!!module);
   });
 
+  test('backend is notified when module is dismissed or restored', async () => {
+    // Arrange.
+    const data = {
+      files: [
+        {
+          justificationText: '',
+          title: '',
+          id: '',
+          mimeType: '',
+          itemUrl: {url: ''},
+        },
+      ]
+    };
+    testProxy.handler.setResultFor('getFiles', Promise.resolve(data));
+    const moduleElement = await driveDescriptor.initialize();
+    document.body.append(moduleElement);
+
+    // Act.
+    const dismiss = {event: null};
+    moduleElement.addEventListener('dismiss-module', (e) => dismiss.event = e);
+    $$(moduleElement, 'ntp-module-header')
+        .dispatchEvent(new Event('dismiss-button-click'));
+
+    // Assert.
+    assertEquals('Files hidden', dismiss.event.detail.message);
+    assertEquals(1, testProxy.handler.getCallCount('dismissModule'));
+
+    // Act.
+    dismiss.event.detail.restoreCallback();
+
+    // Assert.
+    assertEquals(1, testProxy.handler.getCallCount('restoreModule'));
+  });
+
   test('info button click opens info dialog', async () => {
     // Arrange.
     const data = {
diff --git a/chrome/test/data/webui/settings/chromeos/on_startup_page_tests.js b/chrome/test/data/webui/settings/chromeos/on_startup_page_tests.js
index 42054827..5143f83 100644
--- a/chrome/test/data/webui/settings/chromeos/on_startup_page_tests.js
+++ b/chrome/test/data/webui/settings/chromeos/on_startup_page_tests.js
@@ -76,7 +76,7 @@
     onstartupPage.setPrefValue('settings.restore_apps_and_pages', 2);
 
     const params = new URLSearchParams;
-    params.append('settingId', '1900');
+    params.append('settingId', '703');
     settings.Router.getInstance().navigateTo(
         settings.routes.ON_STARTUP, params);
 
@@ -85,6 +85,6 @@
     await test_util.waitAfterNextRender(deepLinkElement);
     assertEquals(
         deepLinkElement, getDeepActiveElement(),
-        'Restore apps and pages radio group should be focused for settingId=1900.');
+        'Restore apps and pages radio group should be focused for settingId=703.');
   });
 });
diff --git a/chrome/test/data/webui/settings/chromeos/os_settings_page_test.js b/chrome/test/data/webui/settings/chromeos/os_settings_page_test.js
index 95bd508..86f6b7ff 100644
--- a/chrome/test/data/webui/settings/chromeos/os_settings_page_test.js
+++ b/chrome/test/data/webui/settings/chromeos/os_settings_page_test.js
@@ -145,13 +145,6 @@
     assert(!!osSettingsAppsPage);
   });
 
-  test('Check settings-on-startup-page exists', async () => {
-    settingsPage.showStartup = true;
-    Polymer.dom.flush();
-    const settingsOnStartupPage = settingsPage.$$('settings-on-startup-page');
-    assert(!!settingsOnStartupPage);
-  });
-
   test('Check settings-crostini-page exists', async () => {
     settingsPage.showCrostini = true;
     const idleRender = settingsPage.$$('settings-idle-load');
diff --git a/chrome/test/data/webui/settings/chromeos/os_settings_v3_browsertest.js b/chrome/test/data/webui/settings/chromeos/os_settings_v3_browsertest.js
index fa7311fa..de949db 100644
--- a/chrome/test/data/webui/settings/chromeos/os_settings_v3_browsertest.js
+++ b/chrome/test/data/webui/settings/chromeos/os_settings_v3_browsertest.js
@@ -89,10 +89,7 @@
 
   /** @override */
   get featureList() {
-    return {
-      enabled: super.featureList.enabled,
-      disabled: ['ash::features::kFullRestore']
-    };
+    return {enabled: ['ash::features::kFullRestore']};
   }
 };
 
diff --git a/chromeos/components/diagnostics_ui/mojom/BUILD.gn b/chromeos/components/diagnostics_ui/mojom/BUILD.gn
index 56d17862..8a4aece 100644
--- a/chromeos/components/diagnostics_ui/mojom/BUILD.gn
+++ b/chromeos/components/diagnostics_ui/mojom/BUILD.gn
@@ -6,6 +6,7 @@
 
 mojom("mojom") {
   sources = [
+    "input_data_provider.mojom",
     "system_data_provider.mojom",
     "system_routine_controller.mojom",
   ]
diff --git a/chromeos/components/diagnostics_ui/mojom/input_data_provider.mojom b/chromeos/components/diagnostics_ui/mojom/input_data_provider.mojom
new file mode 100644
index 0000000..10aeed5
--- /dev/null
+++ b/chromeos/components/diagnostics_ui/mojom/input_data_provider.mojom
@@ -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.
+
+module chromeos.diagnostics.mojom;
+
+enum ConnectionType {
+  kInternal,  // Includes internal USB devices.
+  kUsb,
+  kBluetooth,
+  kUnknown,
+};
+
+// Describes a connected keyboard.
+struct KeyboardInfo {
+  // The number of the keyboard's /dev/input/event* node.
+  uint32 id;
+  ConnectionType connection_type;
+  string name;
+};
+
+enum TouchDeviceType {
+  kDirect,   // e.g. touchscreens, drawing tablets with screens.
+  kPointer,  // e.g. touchpads, drawing tablets without displays.
+};
+
+// Describes a touch surface (pad or screen).
+struct TouchDeviceInfo {
+  // The number of the touch device's /dev/input/event* node.
+  uint32 id;
+  ConnectionType connection_type;
+  TouchDeviceType type;
+  string name;
+};
+
+// Provides information about input devices connected to the system. Implemented
+// in the browser process and called by the Diagnostics SWA (a renderer
+// process).
+interface InputDataProvider {
+  // Returns a snapshot of the devices connected to the system.
+  GetConnectedDevices() =>
+    (array<KeyboardInfo> keyboards,
+     array<TouchDeviceInfo> touch_devices);
+};
diff --git a/chromeos/components/phonehub/fake_notification_manager.cc b/chromeos/components/phonehub/fake_notification_manager.cc
index 1ec3060..b7b8c99 100644
--- a/chromeos/components/phonehub/fake_notification_manager.cc
+++ b/chromeos/components/phonehub/fake_notification_manager.cc
@@ -5,6 +5,7 @@
 #include "chromeos/components/phonehub/fake_notification_manager.h"
 
 #include "base/check.h"
+#include "base/containers/contains.h"
 
 namespace chromeos {
 namespace phonehub {
diff --git a/chromeos/components/quick_answers/quick_answers_client.cc b/chromeos/components/quick_answers/quick_answers_client.cc
index 2fb5120..d3ea79f 100644
--- a/chromeos/components/quick_answers/quick_answers_client.cc
+++ b/chromeos/components/quick_answers/quick_answers_client.cc
@@ -7,6 +7,7 @@
 #include <utility>
 
 #include "ash/constants/ash_features.h"
+#include "base/containers/contains.h"
 #include "chromeos/components/quick_answers/quick_answers_model.h"
 #include "chromeos/components/quick_answers/utils/quick_answers_metrics.h"
 #include "chromeos/components/quick_answers/utils/quick_answers_utils.h"
diff --git a/chromeos/components/sync_wifi/wifi_configuration_bridge_unittest.cc b/chromeos/components/sync_wifi/wifi_configuration_bridge_unittest.cc
index a59e2bc..7abf1b2 100644
--- a/chromeos/components/sync_wifi/wifi_configuration_bridge_unittest.cc
+++ b/chromeos/components/sync_wifi/wifi_configuration_bridge_unittest.cc
@@ -12,6 +12,7 @@
 #include "ash/constants/ash_features.h"
 #include "base/bind.h"
 #include "base/callback_helpers.h"
+#include "base/containers/contains.h"
 #include "base/containers/flat_map.h"
 #include "base/run_loop.h"
 #include "base/test/bind.h"
diff --git a/chromeos/components/tether/disconnect_tethering_request_sender_impl.cc b/chromeos/components/tether/disconnect_tethering_request_sender_impl.cc
index 3d04963..9073b4cc 100644
--- a/chromeos/components/tether/disconnect_tethering_request_sender_impl.cc
+++ b/chromeos/components/tether/disconnect_tethering_request_sender_impl.cc
@@ -7,6 +7,7 @@
 #include <memory>
 
 #include "base/bind.h"
+#include "base/containers/contains.h"
 #include "base/memory/ptr_util.h"
 #include "chromeos/components/multidevice/logging/logging.h"
 #include "chromeos/components/tether/tether_host_fetcher.h"
diff --git a/chromeos/components/tether/host_scanner_operation.cc b/chromeos/components/tether/host_scanner_operation.cc
index 52871c7..66339144 100644
--- a/chromeos/components/tether/host_scanner_operation.cc
+++ b/chromeos/components/tether/host_scanner_operation.cc
@@ -7,6 +7,7 @@
 #include <memory>
 
 #include "base/bind.h"
+#include "base/containers/contains.h"
 #include "base/memory/ptr_util.h"
 #include "base/metrics/histogram_macros.h"
 #include "base/time/default_clock.h"
diff --git a/chromeos/components/tether/message_transfer_operation.cc b/chromeos/components/tether/message_transfer_operation.cc
index a144341..bf05cb70 100644
--- a/chromeos/components/tether/message_transfer_operation.cc
+++ b/chromeos/components/tether/message_transfer_operation.cc
@@ -8,6 +8,7 @@
 #include <set>
 
 #include "base/bind.h"
+#include "base/containers/contains.h"
 #include "chromeos/components/multidevice/logging/logging.h"
 #include "chromeos/components/tether/message_wrapper.h"
 #include "chromeos/components/tether/timer_factory.h"
diff --git a/chromeos/components/tether/network_host_scan_cache.cc b/chromeos/components/tether/network_host_scan_cache.cc
index 20293f4..4f62d5a 100644
--- a/chromeos/components/tether/network_host_scan_cache.cc
+++ b/chromeos/components/tether/network_host_scan_cache.cc
@@ -4,6 +4,7 @@
 
 #include "chromeos/components/tether/network_host_scan_cache.h"
 
+#include "base/containers/contains.h"
 #include "chromeos/components/multidevice/logging/logging.h"
 #include "chromeos/components/tether/device_id_tether_network_guid_map.h"
 #include "chromeos/components/tether/tether_host_response_recorder.h"
diff --git a/chromeos/components/tether/network_host_scan_cache_unittest.cc b/chromeos/components/tether/network_host_scan_cache_unittest.cc
index 14ae478..c96f961 100644
--- a/chromeos/components/tether/network_host_scan_cache_unittest.cc
+++ b/chromeos/components/tether/network_host_scan_cache_unittest.cc
@@ -7,6 +7,7 @@
 #include <memory>
 #include <vector>
 
+#include "base/containers/contains.h"
 #include "base/test/task_environment.h"
 #include "chromeos/components/tether/device_id_tether_network_guid_map.h"
 #include "chromeos/components/tether/fake_host_scan_cache.h"
diff --git a/chromeos/components/web_applications/test/sandboxed_web_ui_test_base.cc b/chromeos/components/web_applications/test/sandboxed_web_ui_test_base.cc
index 61002c3..9c7c8af 100644
--- a/chromeos/components/web_applications/test/sandboxed_web_ui_test_base.cc
+++ b/chromeos/components/web_applications/test/sandboxed_web_ui_test_base.cc
@@ -7,6 +7,7 @@
 #include <vector>
 
 #include "base/base_paths.h"
+#include "base/containers/contains.h"
 #include "base/files/file_util.h"
 #include "base/memory/ref_counted_memory.h"
 #include "base/path_service.h"
diff --git a/chromeos/dbus/kerberos/fake_kerberos_client.cc b/chromeos/dbus/kerberos/fake_kerberos_client.cc
index 4b60b9f..db7e6cd7 100644
--- a/chromeos/dbus/kerberos/fake_kerberos_client.cc
+++ b/chromeos/dbus/kerberos/fake_kerberos_client.cc
@@ -7,6 +7,7 @@
 #include <utility>
 
 #include "base/bind.h"
+#include "base/containers/contains.h"
 #include "base/files/file_util.h"
 #include "base/location.h"
 #include "base/strings/string_split.h"
diff --git a/chromeos/dbus/shill/fake_shill_manager_client.cc b/chromeos/dbus/shill/fake_shill_manager_client.cc
index 73ae313..f66d5df 100644
--- a/chromeos/dbus/shill/fake_shill_manager_client.cc
+++ b/chromeos/dbus/shill/fake_shill_manager_client.cc
@@ -12,6 +12,7 @@
 #include "base/bind.h"
 #include "base/callback_helpers.h"
 #include "base/command_line.h"
+#include "base/containers/contains.h"
 #include "base/location.h"
 #include "base/logging.h"
 #include "base/single_thread_task_runner.h"
diff --git a/chromeos/dbus/shill/fake_shill_service_client.cc b/chromeos/dbus/shill/fake_shill_service_client.cc
index 493036f..1a634873 100644
--- a/chromeos/dbus/shill/fake_shill_service_client.cc
+++ b/chromeos/dbus/shill/fake_shill_service_client.cc
@@ -9,6 +9,7 @@
 
 #include "base/bind.h"
 #include "base/callback_helpers.h"
+#include "base/containers/contains.h"
 #include "base/location.h"
 #include "base/logging.h"
 #include "base/memory/ptr_util.h"
diff --git a/chromeos/lacros/lacros_chrome_service_impl.cc b/chromeos/lacros/lacros_chrome_service_impl.cc
index 0dcfa89..ee2df48a 100644
--- a/chromeos/lacros/lacros_chrome_service_impl.cc
+++ b/chromeos/lacros/lacros_chrome_service_impl.cc
@@ -9,6 +9,7 @@
 
 #include "base/bind_post_task.h"
 #include "base/command_line.h"
+#include "base/containers/contains.h"
 #include "base/logging.h"
 #include "base/task/task_traits.h"
 #include "base/task/thread_pool.h"
diff --git a/chromeos/network/cellular_esim_profile_handler_impl.cc b/chromeos/network/cellular_esim_profile_handler_impl.cc
index 5ab7df1..104a23d8 100644
--- a/chromeos/network/cellular_esim_profile_handler_impl.cc
+++ b/chromeos/network/cellular_esim_profile_handler_impl.cc
@@ -9,6 +9,7 @@
 
 #include "ash/constants/ash_pref_names.h"
 #include "base/callback_helpers.h"
+#include "base/containers/contains.h"
 #include "base/values.h"
 #include "chromeos/dbus/hermes/hermes_euicc_client.h"
 #include "chromeos/network/cellular_utils.h"
diff --git a/chromeos/network/managed_network_configuration_handler_impl.cc b/chromeos/network/managed_network_configuration_handler_impl.cc
index e18228a..fb3964be 100644
--- a/chromeos/network/managed_network_configuration_handler_impl.cc
+++ b/chromeos/network/managed_network_configuration_handler_impl.cc
@@ -10,6 +10,7 @@
 
 #include "base/bind.h"
 #include "base/callback_helpers.h"
+#include "base/containers/contains.h"
 #include "base/guid.h"
 #include "base/location.h"
 #include "base/logging.h"
diff --git a/chromeos/network/network_connection_handler_impl.cc b/chromeos/network/network_connection_handler_impl.cc
index 94c34b1..6133d006 100644
--- a/chromeos/network/network_connection_handler_impl.cc
+++ b/chromeos/network/network_connection_handler_impl.cc
@@ -8,6 +8,7 @@
 
 #include "base/bind.h"
 #include "base/callback_helpers.h"
+#include "base/containers/contains.h"
 #include "base/json/json_reader.h"
 #include "base/location.h"
 #include "base/single_thread_task_runner.h"
diff --git a/chromeos/network/network_state_handler.cc b/chromeos/network/network_state_handler.cc
index 7b3a504..f049f105 100644
--- a/chromeos/network/network_state_handler.cc
+++ b/chromeos/network/network_state_handler.cc
@@ -12,6 +12,7 @@
 
 #include "base/bind.h"
 #include "base/command_line.h"
+#include "base/containers/contains.h"
 #include "base/format_macros.h"
 #include "base/guid.h"
 #include "base/location.h"
diff --git a/chromeos/network/onc/onc_certificate_pattern.cc b/chromeos/network/onc/onc_certificate_pattern.cc
index b6f9797d..5a115b4 100644
--- a/chromeos/network/onc/onc_certificate_pattern.cc
+++ b/chromeos/network/onc/onc_certificate_pattern.cc
@@ -8,6 +8,7 @@
 
 #include <utility>
 
+#include "base/containers/contains.h"
 #include "base/values.h"
 #include "components/onc/onc_constants.h"
 #include "net/cert/x509_certificate.h"
diff --git a/chromeos/network/shill_property_handler.cc b/chromeos/network/shill_property_handler.cc
index 1e32fa69..1a05265 100644
--- a/chromeos/network/shill_property_handler.cc
+++ b/chromeos/network/shill_property_handler.cc
@@ -11,6 +11,7 @@
 
 #include "base/bind.h"
 #include "base/callback_helpers.h"
+#include "base/containers/contains.h"
 #include "base/format_macros.h"
 #include "base/macros.h"
 #include "base/strings/string_split.h"
diff --git a/chromeos/network/stub_cellular_networks_provider.cc b/chromeos/network/stub_cellular_networks_provider.cc
index a4b8e65..e74288c 100644
--- a/chromeos/network/stub_cellular_networks_provider.cc
+++ b/chromeos/network/stub_cellular_networks_provider.cc
@@ -4,6 +4,7 @@
 
 #include "chromeos/network/stub_cellular_networks_provider.h"
 
+#include "base/containers/contains.h"
 #include "base/guid.h"
 #include "chromeos/network/cellular_esim_profile.h"
 #include "chromeos/network/cellular_esim_profile_handler.h"
diff --git a/chromeos/network/test_cellular_esim_profile_handler.cc b/chromeos/network/test_cellular_esim_profile_handler.cc
index fc5360b..2931e16 100644
--- a/chromeos/network/test_cellular_esim_profile_handler.cc
+++ b/chromeos/network/test_cellular_esim_profile_handler.cc
@@ -4,6 +4,7 @@
 
 #include "chromeos/network/test_cellular_esim_profile_handler.h"
 
+#include "base/containers/contains.h"
 #include "chromeos/network/cellular_utils.h"
 #include "chromeos/network/network_state_handler.h"
 
@@ -48,4 +49,4 @@
   NotifyESimProfileListUpdated();
 }
 
-}  // namespace chromeos
\ No newline at end of file
+}  // namespace chromeos
diff --git a/chromeos/printing/ppd_provider.cc b/chromeos/printing/ppd_provider.cc
index e93b75a5..7af91b78 100644
--- a/chromeos/printing/ppd_provider.cc
+++ b/chromeos/printing/ppd_provider.cc
@@ -15,6 +15,7 @@
 #include "base/bind.h"
 #include "base/callback_helpers.h"
 #include "base/containers/circular_deque.h"
+#include "base/containers/contains.h"
 #include "base/files/file.h"
 #include "base/files/file_util.h"
 #include "base/json/json_reader.h"
diff --git a/chromeos/printing/ppd_provider_unittest.cc b/chromeos/printing/ppd_provider_unittest.cc
index b7b0ee0..4172c35 100644
--- a/chromeos/printing/ppd_provider_unittest.cc
+++ b/chromeos/printing/ppd_provider_unittest.cc
@@ -11,6 +11,7 @@
 
 #include "base/bind.h"
 #include "base/callback_helpers.h"
+#include "base/containers/contains.h"
 #include "base/files/file_util.h"
 #include "base/files/scoped_temp_dir.h"
 #include "base/run_loop.h"
diff --git a/chromeos/profiles/orderfile.newest.txt b/chromeos/profiles/orderfile.newest.txt
index 2adde4c..51ffda85 100644
--- a/chromeos/profiles/orderfile.newest.txt
+++ b/chromeos/profiles/orderfile.newest.txt
@@ -1 +1 @@
-chromeos-chrome-orderfile-field-92-4484.0-1620039320-benchmark-92.0.4497.0-r1.orderfile.xz
+chromeos-chrome-orderfile-field-92-4484.0-1620039320-benchmark-92.0.4502.0-r1.orderfile.xz
diff --git a/chromeos/services/cellular_setup/ota_activator_impl.cc b/chromeos/services/cellular_setup/ota_activator_impl.cc
index 8c86a125..40a22602 100644
--- a/chromeos/services/cellular_setup/ota_activator_impl.cc
+++ b/chromeos/services/cellular_setup/ota_activator_impl.cc
@@ -276,7 +276,7 @@
         cellular_network->path(), base::DoNothing(),
         base::BindOnce(&OtaActivatorImpl::OnNetworkConnectionError,
                        weak_ptr_factory_.GetWeakPtr()),
-        false /* check_error_state */, ConnectCallbackMode::ON_STARTED);
+        false /* check_error_state */, ConnectCallbackMode::ON_COMPLETED);
     return;
   }
 
diff --git a/chromeos/services/cellular_setup/ota_activator_impl_unittest.cc b/chromeos/services/cellular_setup/ota_activator_impl_unittest.cc
index e313276..cabc4f4 100644
--- a/chromeos/services/cellular_setup/ota_activator_impl_unittest.cc
+++ b/chromeos/services/cellular_setup/ota_activator_impl_unittest.cc
@@ -12,6 +12,7 @@
 #include "base/test/test_simple_task_runner.h"
 #include "chromeos/network/fake_network_activation_handler.h"
 #include "chromeos/network/fake_network_connection_handler.h"
+#include "chromeos/network/network_connection_handler.h"
 #include "chromeos/network/network_state_handler.h"
 #include "chromeos/network/network_state_test_helper.h"
 #include "chromeos/services/cellular_setup/public/cpp/fake_activation_delegate.h"
@@ -202,6 +203,8 @@
 
     // A connection should have been requested by |ota_activator_|.
     EXPECT_EQ(kTestCellularServicePath, connect_calls.back().service_path());
+    EXPECT_EQ(ConnectCallbackMode::ON_COMPLETED,
+              connect_calls.back().connect_callback_mode());
     connect_calls.back().InvokeErrorCallback("fake_error", nullptr);
     base::RunLoop().RunUntilIdle();
   }
diff --git a/chromeos/services/device_sync/cryptauth_device_manager_impl.cc b/chromeos/services/device_sync/cryptauth_device_manager_impl.cc
index cfb10ca..93dc278 100644
--- a/chromeos/services/device_sync/cryptauth_device_manager_impl.cc
+++ b/chromeos/services/device_sync/cryptauth_device_manager_impl.cc
@@ -12,6 +12,7 @@
 
 #include "base/base64url.h"
 #include "base/bind.h"
+#include "base/containers/contains.h"
 #include "base/memory/ptr_util.h"
 #include "base/metrics/histogram_macros.h"
 #include "base/strings/string_number_conversions.h"
diff --git a/chromeos/services/device_sync/cryptauth_device_manager_impl_unittest.cc b/chromeos/services/device_sync/cryptauth_device_manager_impl_unittest.cc
index d4e2877..c7cc1df 100644
--- a/chromeos/services/device_sync/cryptauth_device_manager_impl_unittest.cc
+++ b/chromeos/services/device_sync/cryptauth_device_manager_impl_unittest.cc
@@ -10,6 +10,7 @@
 #include <utility>
 
 #include "base/base64url.h"
+#include "base/containers/contains.h"
 #include "base/macros.h"
 #include "base/memory/ptr_util.h"
 #include "base/memory/weak_ptr.h"
diff --git a/chromeos/services/device_sync/cryptauth_device_syncer_impl.cc b/chromeos/services/device_sync/cryptauth_device_syncer_impl.cc
index 72930e7..7868ad3 100644
--- a/chromeos/services/device_sync/cryptauth_device_syncer_impl.cc
+++ b/chromeos/services/device_sync/cryptauth_device_syncer_impl.cc
@@ -7,6 +7,7 @@
 #include <utility>
 
 #include "base/bind.h"
+#include "base/containers/contains.h"
 #include "base/containers/flat_set.h"
 #include "base/memory/ptr_util.h"
 #include "base/metrics/histogram_functions.h"
diff --git a/chromeos/services/device_sync/cryptauth_device_syncer_impl_unittest.cc b/chromeos/services/device_sync/cryptauth_device_syncer_impl_unittest.cc
index b776d05b..13520ac 100644
--- a/chromeos/services/device_sync/cryptauth_device_syncer_impl_unittest.cc
+++ b/chromeos/services/device_sync/cryptauth_device_syncer_impl_unittest.cc
@@ -8,6 +8,7 @@
 #include <string>
 #include <utility>
 
+#include "base/containers/contains.h"
 #include "base/macros.h"
 #include "base/no_destructor.h"
 #include "base/optional.h"
diff --git a/chromeos/services/device_sync/cryptauth_feature_status_getter_impl_unittest.cc b/chromeos/services/device_sync/cryptauth_feature_status_getter_impl_unittest.cc
index 2f2588a..8e6135e 100644
--- a/chromeos/services/device_sync/cryptauth_feature_status_getter_impl_unittest.cc
+++ b/chromeos/services/device_sync/cryptauth_feature_status_getter_impl_unittest.cc
@@ -8,6 +8,7 @@
 #include <string>
 #include <utility>
 
+#include "base/containers/contains.h"
 #include "base/containers/flat_set.h"
 #include "base/macros.h"
 #include "base/no_destructor.h"
diff --git a/chromeos/services/device_sync/cryptauth_group_private_key_sharer_impl_unittest.cc b/chromeos/services/device_sync/cryptauth_group_private_key_sharer_impl_unittest.cc
index ba26d6d..740781db 100644
--- a/chromeos/services/device_sync/cryptauth_group_private_key_sharer_impl_unittest.cc
+++ b/chromeos/services/device_sync/cryptauth_group_private_key_sharer_impl_unittest.cc
@@ -8,6 +8,7 @@
 #include <string>
 #include <utility>
 
+#include "base/containers/contains.h"
 #include "base/containers/flat_set.h"
 #include "base/macros.h"
 #include "base/no_destructor.h"
diff --git a/chromeos/services/network_config/cros_network_config.cc b/chromeos/services/network_config/cros_network_config.cc
index 9cdf65d2..4bff109 100644
--- a/chromeos/services/network_config/cros_network_config.cc
+++ b/chromeos/services/network_config/cros_network_config.cc
@@ -7,6 +7,7 @@
 #include <vector>
 
 #include "ash/constants/ash_features.h"
+#include "base/containers/contains.h"
 #include "base/containers/flat_map.h"
 #include "base/guid.h"
 #include "base/optional.h"
diff --git a/chromeos/services/network_config/cros_network_config_unittest.cc b/chromeos/services/network_config/cros_network_config_unittest.cc
index 5caac9e..15762a9 100644
--- a/chromeos/services/network_config/cros_network_config_unittest.cc
+++ b/chromeos/services/network_config/cros_network_config_unittest.cc
@@ -7,6 +7,7 @@
 #include "ash/constants/ash_features.h"
 #include "base/bind.h"
 #include "base/callback_helpers.h"
+#include "base/containers/contains.h"
 #include "base/json/json_reader.h"
 #include "base/memory/ptr_util.h"
 #include "base/strings/string_number_conversions.h"
diff --git a/chromeos/services/secure_channel/active_connection_manager_impl_unittest.cc b/chromeos/services/secure_channel/active_connection_manager_impl_unittest.cc
index 919a7871..66e49b4b 100644
--- a/chromeos/services/secure_channel/active_connection_manager_impl_unittest.cc
+++ b/chromeos/services/secure_channel/active_connection_manager_impl_unittest.cc
@@ -7,6 +7,7 @@
 #include <memory>
 
 #include "base/bind.h"
+#include "base/containers/contains.h"
 #include "base/containers/flat_map.h"
 #include "base/macros.h"
 #include "base/test/gtest_util.h"
diff --git a/chromeos/services/secure_channel/authenticated_channel_impl.cc b/chromeos/services/secure_channel/authenticated_channel_impl.cc
index 041047ed..688180f 100644
--- a/chromeos/services/secure_channel/authenticated_channel_impl.cc
+++ b/chromeos/services/secure_channel/authenticated_channel_impl.cc
@@ -5,6 +5,7 @@
 #include "chromeos/services/secure_channel/authenticated_channel_impl.h"
 
 #include "base/bind.h"
+#include "base/containers/contains.h"
 #include "base/logging.h"
 #include "base/memory/ptr_util.h"
 #include "chromeos/components/multidevice/logging/logging.h"
diff --git a/chromeos/services/secure_channel/fake_connection_attempt.h b/chromeos/services/secure_channel/fake_connection_attempt.h
index 06776e0..110440f 100644
--- a/chromeos/services/secure_channel/fake_connection_attempt.h
+++ b/chromeos/services/secure_channel/fake_connection_attempt.h
@@ -8,6 +8,7 @@
 #include <unordered_map>
 
 #include "base/callback.h"
+#include "base/containers/contains.h"
 #include "base/macros.h"
 #include "base/time/default_clock.h"
 #include "base/unguessable_token.h"
diff --git a/chromeos/services/secure_channel/nearby_connection_manager_impl.cc b/chromeos/services/secure_channel/nearby_connection_manager_impl.cc
index 061d99d..73d78c1 100644
--- a/chromeos/services/secure_channel/nearby_connection_manager_impl.cc
+++ b/chromeos/services/secure_channel/nearby_connection_manager_impl.cc
@@ -4,6 +4,7 @@
 
 #include "chromeos/services/secure_channel/nearby_connection_manager_impl.h"
 
+#include "base/containers/contains.h"
 #include "base/memory/ptr_util.h"
 #include "chromeos/components/multidevice/logging/logging.h"
 #include "chromeos/services/secure_channel/authenticated_channel_impl.h"
diff --git a/chromeos/services/secure_channel/pending_connection_manager_impl_unittest.cc b/chromeos/services/secure_channel/pending_connection_manager_impl_unittest.cc
index e57f3d6..d74e8608 100644
--- a/chromeos/services/secure_channel/pending_connection_manager_impl_unittest.cc
+++ b/chromeos/services/secure_channel/pending_connection_manager_impl_unittest.cc
@@ -8,6 +8,7 @@
 #include <sstream>
 
 #include "base/bind.h"
+#include "base/containers/contains.h"
 #include "base/containers/flat_map.h"
 #include "base/macros.h"
 #include "base/optional.h"
diff --git a/chromeos/services/secure_channel/public/cpp/client/client_channel_impl_unittest.cc b/chromeos/services/secure_channel/public/cpp/client/client_channel_impl_unittest.cc
index 36dd950..0f170025 100644
--- a/chromeos/services/secure_channel/public/cpp/client/client_channel_impl_unittest.cc
+++ b/chromeos/services/secure_channel/public/cpp/client/client_channel_impl_unittest.cc
@@ -7,6 +7,7 @@
 #include "base/bind.h"
 #include "base/callback.h"
 #include "base/callback_helpers.h"
+#include "base/containers/contains.h"
 #include "base/memory/scoped_refptr.h"
 #include "base/no_destructor.h"
 #include "base/optional.h"
diff --git a/chromeos/services/secure_channel/public/cpp/client/fake_secure_channel_client.h b/chromeos/services/secure_channel/public/cpp/client/fake_secure_channel_client.h
index da2de80..c1d23bcf 100644
--- a/chromeos/services/secure_channel/public/cpp/client/fake_secure_channel_client.h
+++ b/chromeos/services/secure_channel/public/cpp/client/fake_secure_channel_client.h
@@ -10,6 +10,7 @@
 #include <vector>
 
 #include "base/callback.h"
+#include "base/containers/contains.h"
 #include "base/macros.h"
 #include "chromeos/services/secure_channel/public/cpp/client/connection_attempt.h"
 #include "chromeos/services/secure_channel/public/cpp/client/secure_channel_client.h"
diff --git a/chromeos/services/secure_channel/wire_message.cc b/chromeos/services/secure_channel/wire_message.cc
index a0a31af92..f03be85 100644
--- a/chromeos/services/secure_channel/wire_message.cc
+++ b/chromeos/services/secure_channel/wire_message.cc
@@ -11,6 +11,7 @@
 
 #include "base/base64url.h"
 #include "base/big_endian.h"
+#include "base/containers/contains.h"
 #include "base/json/json_reader.h"
 #include "base/json/json_writer.h"
 #include "base/macros.h"
diff --git a/components/arc/arc_util.cc b/components/arc/arc_util.cc
index 860dd80..a8c0440 100644
--- a/components/arc/arc_util.cc
+++ b/components/arc/arc_util.cc
@@ -382,6 +382,8 @@
     return 213;  // TVDPI
   if (std::abs(device_scale_factor - display::kDsf_1_777) < kEpsilon)
     return 240;  // HDPI
+  if (std::abs(device_scale_factor - display::kDsf_1_8) < kEpsilon)
+    return 240;  // HDPI
   if (std::abs(device_scale_factor - display::kDsf_2_666) < kEpsilon)
     return 320;  // XHDPI
 
diff --git a/components/arc/arc_util_unittest.cc b/components/arc/arc_util_unittest.cc
index 851599e..9819a38 100644
--- a/components/arc/arc_util_unittest.cc
+++ b/components/arc/arc_util_unittest.cc
@@ -406,6 +406,7 @@
   EXPECT_EQ(160, GetLcdDensityForDeviceScaleFactor(1.25f));
   EXPECT_EQ(213, GetLcdDensityForDeviceScaleFactor(1.6f));
   EXPECT_EQ(240, GetLcdDensityForDeviceScaleFactor(display::kDsf_1_777));
+  EXPECT_EQ(240, GetLcdDensityForDeviceScaleFactor(display::kDsf_1_8));
   EXPECT_EQ(240, GetLcdDensityForDeviceScaleFactor(2.0f));
   EXPECT_EQ(280, GetLcdDensityForDeviceScaleFactor(display::kDsf_2_252));
   EXPECT_EQ(280, GetLcdDensityForDeviceScaleFactor(2.4f));
diff --git a/components/autofill_assistant/browser/action_value.proto b/components/autofill_assistant/browser/action_value.proto
index 4ea533fd..80ea145 100644
--- a/components/autofill_assistant/browser/action_value.proto
+++ b/components/autofill_assistant/browser/action_value.proto
@@ -10,37 +10,57 @@
 
 package autofill_assistant;
 
-message AutofillValue {
-  message Profile { optional string identifier = 1; }
+// An Autofill profile stored on the client. This requires to be selected by
+// the user in a CollectUserData step first.
+message AutofillProfile {
+  optional string identifier = 1;
+}
 
+// A value expression.
+message ValueExpression {
+  message Chunk {
+    oneof chunk {
+      // An integer representation to resolve a piece of Autofill information.
+      // The key is an integer corresponding to entries from field_types.h or
+      // |AutofillFormatProto::AutofillAssistantCustomField|.
+      // Note that the set of actually available fields are outside of our
+      // control and are retrieved automatically from the provided profile.
+      int32 key = 1;
+      // A plain text.
+      string text = 2;
+    }
+  }
+  repeated Chunk chunk = 1;
+}
+
+// A value expression to be used as a regular expression.
+message ValueExpressionRegexp {
+  optional ValueExpression value_expression = 1;
+
+  // If true, the |value_expression| will be checked case sensitively.
+  // Default is case insensitive.
+  optional bool case_sensitive = 4;
+}
+
+message AutofillValue {
   // The profile to be used. This has to be requested with a
   // |CollectUserDataAction| first.
-  optional Profile profile = 1;
+  optional AutofillProfile profile = 1;
 
-  // A string containing any number of "${key}" placeholders, where the key is
-  // an integer corresponding to entries from field_types.h or
-  // |AutofillFormatProto::AutofillAssistantCustomField|.
-  // Note that the set of actually available fields are outside of our
-  // control and are retrieved automatically from the provided profile.
-  optional string value_expression = 2;
+  // The value expression.
+  optional ValueExpression value_expression = 5;
+
+  reserved 2 to 4;
 }
 
 message AutofillValueRegexp {
-  message Profile { optional string identifier = 1; }
-
   // The profile to be used. This has to be requested with a
   // |CollectUserDataAction| first.
-  optional Profile profile = 1;
+  optional AutofillProfile profile = 1;
 
-  // A string containing any number of "${key}" placeholders, where the key is
-  // an integer corresponding to entries from field_types.h or
-  // |AutofillFormatProto::AutofillAssistantCustomField|.
-  // Note that the set of actually available fields are outside of our
-  // control and are retrieved automatically from the provided profile.
-  // The |value_expression| needs to escape all text outside of placeholders,
-  // e.g. a pattern like "+${12}" needs to escape the "+". The pattern keys
-  // ("${12}") should not be escaped, this will be handled during replacement.
-  optional TextFilter value_expression = 2;
+  optional ValueExpressionRegexp value_expression_re2 = 3;
+
+  reserved 2;
 }
 
 // A wrapper for regular expressions, e.g. used for filtering elements by their
diff --git a/components/autofill_assistant/browser/actions/action_delegate_util_unittest.cc b/components/autofill_assistant/browser/actions/action_delegate_util_unittest.cc
index 1238dbc..682349f 100644
--- a/components/autofill_assistant/browser/actions/action_delegate_util_unittest.cc
+++ b/components/autofill_assistant/browser/actions/action_delegate_util_unittest.cc
@@ -323,13 +323,12 @@
                                          &user_data_);
 
   TextValue text_value;
-  auto* autofill_value = text_value.mutable_autofill_value();
-  autofill_value->mutable_profile()->set_identifier("contact");
-  autofill_value->set_value_expression(
-      base::StrCat({"${",
-                    base::NumberToString(static_cast<int>(
-                        autofill::ServerFieldType::NAME_FIRST)),
-                    "}"}));
+  text_value.mutable_autofill_value()->mutable_profile()->set_identifier(
+      "contact");
+  text_value.mutable_autofill_value()
+      ->mutable_value_expression()
+      ->add_chunk()
+      ->set_key(static_cast<int>(autofill::ServerFieldType::NAME_FIRST));
 
   PerformWithTextValue(&mock_action_delegate_, text_value,
                        base::BindOnce(&ActionDelegateUtilTest::MockValueAction,
diff --git a/components/autofill_assistant/browser/actions/action_test_utils.cc b/components/autofill_assistant/browser/actions/action_test_utils.cc
index 15332843..7151cb12 100644
--- a/components/autofill_assistant/browser/actions/action_test_utils.cc
+++ b/components/autofill_assistant/browser/actions/action_test_utils.cc
@@ -69,5 +69,28 @@
   return expected_result;
 }
 
+ValueExpressionBuilder::ValueExpressionBuilder() = default;
+
+ValueExpressionBuilder& ValueExpressionBuilder::addChunk(
+    const std::string& text) {
+  value_expression.add_chunk()->set_text(text);
+  return *this;
+}
+
+ValueExpressionBuilder& ValueExpressionBuilder::addChunk(int key) {
+  value_expression.add_chunk()->set_key(key);
+  return *this;
+}
+
+ValueExpressionBuilder& ValueExpressionBuilder::addChunk(
+    autofill::ServerFieldType field) {
+  value_expression.add_chunk()->set_key(static_cast<int>(field));
+  return *this;
+}
+
+ValueExpression ValueExpressionBuilder::toProto() {
+  return value_expression;
+}
+
 }  // namespace test_util
 }  // namespace autofill_assistant
diff --git a/components/autofill_assistant/browser/actions/action_test_utils.h b/components/autofill_assistant/browser/actions/action_test_utils.h
index 998a3ff..d9fc6b1 100644
--- a/components/autofill_assistant/browser/actions/action_test_utils.h
+++ b/components/autofill_assistant/browser/actions/action_test_utils.h
@@ -5,6 +5,8 @@
 #ifndef COMPONENTS_AUTOFILL_ASSISTANT_BROWSER_ACTIONS_ACTION_TEST_UTILS_H_
 #define COMPONENTS_AUTOFILL_ASSISTANT_BROWSER_ACTIONS_ACTION_TEST_UTILS_H_
 
+#include "components/autofill/core/browser/field_types.h"
+#include "components/autofill_assistant/browser/action_value.pb.h"
 #include "components/autofill_assistant/browser/actions/mock_action_delegate.h"
 #include "components/autofill_assistant/browser/selector.h"
 #include "components/autofill_assistant/browser/web/element_finder.h"
@@ -43,6 +45,23 @@
                                       const Selector& selector,
                                       int times = 1);
 
+struct ValueExpressionBuilder {
+ public:
+  ValueExpressionBuilder();
+
+  ValueExpressionBuilder(const ValueExpressionBuilder&) = delete;
+  ValueExpressionBuilder& operator=(const ValueExpressionBuilder&) = delete;
+
+  ValueExpressionBuilder& addChunk(const std::string& text);
+  ValueExpressionBuilder& addChunk(int key);
+  ValueExpressionBuilder& addChunk(autofill::ServerFieldType field);
+
+  ValueExpression toProto();
+
+ private:
+  ValueExpression value_expression;
+};
+
 }  // namespace test_util
 }  // namespace autofill_assistant
 
diff --git a/components/autofill_assistant/browser/actions/fallback_handler/required_field.cc b/components/autofill_assistant/browser/actions/fallback_handler/required_field.cc
index 7b57d43..734428f 100644
--- a/components/autofill_assistant/browser/actions/fallback_handler/required_field.cc
+++ b/components/autofill_assistant/browser/actions/fallback_handler/required_field.cc
@@ -29,10 +29,10 @@
 }
 
 bool RequiredField::ShouldFallback(bool apply_fallback) const {
-  return (status == EMPTY && !value_expression.empty() &&
+  return (status == EMPTY && !value_expression.chunk().empty() &&
           !fallback_click_element.has_value() &&
           !(optional && !apply_fallback)) ||
-         (status != EMPTY && value_expression.empty() &&
+         (status != EMPTY && value_expression.chunk().empty() &&
           !fallback_click_element.has_value()) ||
          (forced && apply_fallback) ||
          (fallback_click_element.has_value() && apply_fallback);
diff --git a/components/autofill_assistant/browser/actions/fallback_handler/required_field.h b/components/autofill_assistant/browser/actions/fallback_handler/required_field.h
index 7a8a836b..34acdb4 100644
--- a/components/autofill_assistant/browser/actions/fallback_handler/required_field.h
+++ b/components/autofill_assistant/browser/actions/fallback_handler/required_field.h
@@ -31,7 +31,7 @@
 
   // The value expression to be filled into the field. This gets evaluated with
   // the provided data.
-  std::string value_expression;
+  ValueExpression value_expression;
 
   // Defines whether the field is currently considered to be filled or not.
   FieldValueStatus status = UNKNOWN;
diff --git a/components/autofill_assistant/browser/actions/fallback_handler/required_field_unittest.cc b/components/autofill_assistant/browser/actions/fallback_handler/required_field_unittest.cc
index fd76bdd..9c77b7eb 100644
--- a/components/autofill_assistant/browser/actions/fallback_handler/required_field_unittest.cc
+++ b/components/autofill_assistant/browser/actions/fallback_handler/required_field_unittest.cc
@@ -18,7 +18,7 @@
 TEST_F(RequiredFieldTest, ShouldFallbackForNotEmpty) {
   RequiredField required_field;
   required_field.status = RequiredField::NOT_EMPTY;
-  required_field.value_expression = "value";
+  required_field.value_expression.add_chunk()->set_text("value");
 
   EXPECT_FALSE(required_field.ShouldFallback(true));
   EXPECT_FALSE(required_field.ShouldFallback(false));
@@ -27,7 +27,6 @@
 TEST_F(RequiredFieldTest, ShouldFallbackForNotEmptyToBeCleared) {
   RequiredField required_field;
   required_field.status = RequiredField::NOT_EMPTY;
-  required_field.value_expression = std::string();
 
   EXPECT_TRUE(required_field.ShouldFallback(true));
   EXPECT_TRUE(required_field.ShouldFallback(false));
@@ -36,7 +35,7 @@
 TEST_F(RequiredFieldTest, ShouldFallbackForEmpty) {
   RequiredField required_field;
   required_field.status = RequiredField::EMPTY;
-  required_field.value_expression = "value";
+  required_field.value_expression.add_chunk()->set_text("value");
 
   EXPECT_TRUE(required_field.ShouldFallback(true));
   EXPECT_TRUE(required_field.ShouldFallback(false));
@@ -46,7 +45,7 @@
   RequiredField required_field;
   required_field.forced = true;
   required_field.status = RequiredField::NOT_EMPTY;
-  required_field.value_expression = "value";
+  required_field.value_expression.add_chunk()->set_text("value");
 
   EXPECT_TRUE(required_field.ShouldFallback(true));
   EXPECT_FALSE(required_field.ShouldFallback(false));
@@ -65,7 +64,7 @@
   RequiredField required_field;
   required_field.optional = true;
   required_field.status = RequiredField::EMPTY;
-  required_field.value_expression = "value";
+  required_field.value_expression.add_chunk()->set_text("value");
 
   EXPECT_TRUE(required_field.ShouldFallback(true));
   EXPECT_FALSE(required_field.ShouldFallback(false));
diff --git a/components/autofill_assistant/browser/actions/fallback_handler/required_fields_fallback_handler.cc b/components/autofill_assistant/browser/actions/fallback_handler/required_fields_fallback_handler.cc
index 1368c06e..33207df 100644
--- a/components/autofill_assistant/browser/actions/fallback_handler/required_fields_fallback_handler.cc
+++ b/components/autofill_assistant/browser/actions/fallback_handler/required_fields_fallback_handler.cc
@@ -24,6 +24,27 @@
 
 const char kSelectElementTag[] = "SELECT";
 
+// Returns a human-readable string representation of |value_expression| for
+// use in logging and error reporting.
+std::string GetHumanReadableValueExpression(
+    const ValueExpression& value_expression) {
+  std::string out;
+  for (const auto& chunk : value_expression.chunk()) {
+    switch (chunk.chunk_case()) {
+      case ValueExpression::Chunk::kText:
+        out += chunk.text();
+        break;
+      case ValueExpression::Chunk::kKey:
+        out += "${" + base::NumberToString(chunk.key()) + "}";
+        break;
+      case ValueExpression::Chunk::CHUNK_NOT_SET:
+        out += "<CHUNK_NOT_SET>";
+        break;
+    }
+  }
+  return out;
+}
+
 AutofillErrorInfoProto::AutofillFieldError* AddAutofillError(
     const RequiredField& required_field,
     ClientStatus* client_status) {
@@ -31,7 +52,8 @@
                           ->mutable_autofill_error_info()
                           ->add_autofill_field_error();
   *field_error->mutable_field() = required_field.selector.proto;
-  field_error->set_value_expression(required_field.value_expression);
+  field_error->set_value_expression(
+      GetHumanReadableValueExpression(required_field.value_expression));
   return field_error;
 }
 
@@ -156,7 +178,7 @@
     if (required_field.ShouldFallback(apply_fallback)) {
       should_fallback = true;
       if (!apply_fallback) {
-        if (required_field.value_expression.empty()) {
+        if (required_field.value_expression.chunk().empty()) {
           VLOG(1) << "Field was filled after attempting to clear it: "
                   << required_field.selector;
           FillStatusDetailsWithNotClearedField(required_field, &client_status_);
@@ -182,16 +204,20 @@
   }
 
   for (const RequiredField& required_field : required_fields_) {
-    if (required_field.value_expression.empty() ||
+    if (required_field.value_expression.chunk().empty() ||
         !required_field.ShouldFallback(/* apply_fallback= */ true)) {
       continue;
     }
 
-    if (!field_formatter::FormatString(required_field.value_expression,
-                                       fallback_values_)
-             .has_value()) {
+    std::string tmp;
+    if (!field_formatter::FormatExpression(required_field.value_expression,
+                                           fallback_values_,
+                                           /* quote_meta= */ false, &tmp)
+             .ok()) {
       DVLOG(3) << "Field has no fallback data: " << required_field.selector
-               << " " << required_field.value_expression;
+               << " "
+               << GetHumanReadableValueExpression(
+                      required_field.value_expression);
       FillStatusDetailsWithMissingFallbackData(required_field, &client_status_);
     }
   }
@@ -224,9 +250,11 @@
       &RequiredFieldsFallbackHandler::SetFallbackFieldValuesSequentially,
       weak_ptr_factory_.GetWeakPtr(), required_fields_index + 1);
 
-  auto fallback_value = field_formatter::FormatString(
-      required_field.value_expression, fallback_values_);
-  if (!fallback_value.has_value()) {
+  std::string fallback_value;
+  ClientStatus format_status = field_formatter::FormatExpression(
+      required_field.value_expression, fallback_values_,
+      /* quote_meta= */ false, &fallback_value);
+  if (!format_status.ok()) {
     // Skip optional field, fail otherwise.
     if (required_field.optional) {
       std::move(set_next_field).Run();
@@ -238,10 +266,10 @@
   }
 
   if (required_field.fallback_click_element.has_value()) {
-    FillJsDrivenDropdown(*fallback_value, required_field,
+    FillJsDrivenDropdown(fallback_value, required_field,
                          std::move(set_next_field));
   } else {
-    FillFormField(*fallback_value, required_field, std::move(set_next_field));
+    FillFormField(fallback_value, required_field, std::move(set_next_field));
   }
 }
 
diff --git a/components/autofill_assistant/browser/actions/fallback_handler/required_fields_fallback_handler_unittest.cc b/components/autofill_assistant/browser/actions/fallback_handler/required_fields_fallback_handler_unittest.cc
index a39f0616..eee21ab 100644
--- a/components/autofill_assistant/browser/actions/fallback_handler/required_fields_fallback_handler_unittest.cc
+++ b/components/autofill_assistant/browser/actions/fallback_handler/required_fields_fallback_handler_unittest.cc
@@ -31,7 +31,16 @@
 using ::testing::Invoke;
 using ::testing::Return;
 
-RequiredField CreateRequiredField(const std::string& value_expression,
+RequiredField CreateRequiredField(int key,
+                                  const std::vector<std::string>& selector) {
+  RequiredField required_field;
+  required_field.value_expression.add_chunk()->set_key(key);
+  required_field.selector = Selector(selector);
+  required_field.status = RequiredField::EMPTY;
+  return required_field;
+}
+
+RequiredField CreateRequiredField(const ValueExpression& value_expression,
                                   const std::vector<std::string>& selector) {
   RequiredField required_field;
   required_field.value_expression = value_expression;
@@ -90,7 +99,7 @@
       .WillByDefault(RunOnceCallback<1>(OkClientStatus(), "value"));
 
   std::vector<RequiredField> required_fields = {
-      CreateRequiredField("${51}", {"#card_name"})};
+      CreateRequiredField(51, {"#card_name"})};
 
   RequiredFieldsFallbackHandler fallback_handler(required_fields, {},
                                                  &mock_action_delegate_);
@@ -111,7 +120,7 @@
       .WillByDefault(RunOnceCallback<1>(OkClientStatus(), std::string()));
 
   std::vector<RequiredField> required_fields = {
-      CreateRequiredField("${51}", {"#card_name"})};
+      CreateRequiredField(51, {"#card_name"})};
 
   RequiredFieldsFallbackHandler fallback_handler(required_fields, {},
                                                  &mock_action_delegate_);
@@ -149,9 +158,9 @@
       .WillOnce(RunOnceCallback<1>(OkClientStatus(), std::string()));
 
   std::vector<RequiredField> required_fields = {
-      CreateRequiredField("${51}", {"#card_name"}),
-      CreateRequiredField("${52}", {"#card_number"}),
-      CreateRequiredField("${-3}", {"#card_network"})};
+      CreateRequiredField(51, {"#card_name"}),
+      CreateRequiredField(52, {"#card_number"}),
+      CreateRequiredField(-3, {"#card_network"})};
 
   std::map<std::string, std::string> fallback_values = {
       {base::NumberToString(
@@ -201,8 +210,8 @@
       .WillByDefault(RunOnceCallback<2>(ClientStatus(OTHER_ACTION_STATUS)));
 
   std::vector<RequiredField> required_fields = {
-      CreateRequiredField("${51}", {"#card_name"}),
-      CreateRequiredField("${52}", {"#card_number"})};
+      CreateRequiredField(51, {"#card_name"}),
+      CreateRequiredField(52, {"#card_number"})};
 
   std::map<std::string, std::string> fallback_values = {
       {base::NumberToString(
@@ -243,8 +252,8 @@
       .WillByDefault(RunOnceCallback<1>(OkClientStatus(), ""));
 
   std::vector<RequiredField> required_fields = {
-      CreateRequiredField("${51}", {"#card_name"}),
-      CreateRequiredField("${52}", {"#card_number"})};
+      CreateRequiredField(51, {"#card_name"}),
+      CreateRequiredField(52, {"#card_number"})};
 
   std::map<std::string, std::string> fallback_values = {
       {base::NumberToString(
@@ -284,7 +293,7 @@
   EXPECT_CALL(mock_web_controller_, SetValueAttribute(_, _, _)).Times(0);
 
   std::vector<RequiredField> required_fields = {
-      CreateRequiredField("${51}", {"#card_name"})};
+      CreateRequiredField(51, {"#card_name"})};
 
   RequiredFieldsFallbackHandler fallback_handler(required_fields, {},
                                                  &mock_action_delegate_);
@@ -311,7 +320,7 @@
       .WillOnce(RunOnceCallback<1>(OkClientStatus(), "John Doe"));
 
   std::vector<RequiredField> required_fields = {
-      CreateRequiredField("${51}", {"#card_name"})};
+      CreateRequiredField(51, {"#card_name"})};
 
   std::map<std::string, std::string> fallback_values = {
       {base::NumberToString(
@@ -338,7 +347,7 @@
       .WillOnce(RunOnceCallback<2>(OkClientStatus()));
 
   std::vector<RequiredField> required_fields = {
-      CreateRequiredField("${51}", {"#card_name"})};
+      CreateRequiredField(51, {"#card_name"})};
   required_fields[0].forced = true;
 
   std::map<std::string, std::string> fallback_values = {
@@ -360,7 +369,7 @@
   EXPECT_CALL(mock_web_controller_, SetValueAttribute(_, _, _)).Times(0);
 
   std::vector<RequiredField> required_fields = {
-      CreateRequiredField("${51}", {"#card_name"})};
+      CreateRequiredField(51, {"#card_name"})};
   required_fields[0].forced = true;
 
   RequiredFieldsFallbackHandler fallback_handler(required_fields, {},
@@ -403,8 +412,12 @@
       .After(set_value)
       .WillOnce(RunOnceCallback<1>(OkClientStatus(), "not empty"));
 
+  ValueExpression value_expression;
+  value_expression.add_chunk()->set_key(53);
+  value_expression.add_chunk()->set_text("/");
+  value_expression.add_chunk()->set_key(55);
   std::vector<RequiredField> required_fields = {
-      CreateRequiredField("${53}/${55}", {"#card_expiry"})};
+      CreateRequiredField(value_expression, {"#card_expiry"})};
 
   std::map<std::string, std::string> fallback_values = {
       {base::NumberToString(
@@ -430,8 +443,8 @@
   EXPECT_CALL(mock_web_controller_, SetValueAttribute(_, _, _)).Times(0);
 
   std::vector<RequiredField> required_fields = {
-      CreateRequiredField("${53}", {"#card_expiry"}),
-      CreateRequiredField("${-3}", {"#card_network"})};
+      CreateRequiredField(53, {"#card_expiry"}),
+      CreateRequiredField(-3, {"#card_network"})};
 
   std::map<std::string, std::string> fallback_values;
   fallback_values.emplace(base::NumberToString(static_cast<int>(
@@ -502,7 +515,7 @@
       .WillOnce(RunOnceCallback<1>(OkClientStatus(), "2050"));
 
   std::vector<RequiredField> required_fields = {
-      CreateRequiredField("${55}", {"#year"})};
+      CreateRequiredField(55, {"#year"})};
 
   std::map<std::string, std::string> fallback_values = {
       {base::NumberToString(static_cast<int>(
@@ -543,7 +556,7 @@
       .WillOnce(RunOnceCallback<2>(OkClientStatus()));
 
   std::vector<RequiredField> required_fields = {
-      CreateRequiredField("${53}", {"#card_expiry"})};
+      CreateRequiredField(53, {"#card_expiry"})};
   required_fields[0].fallback_click_element = Selector({".option"});
 
   std::map<std::string, std::string> fallback_values = {
@@ -585,7 +598,7 @@
       .After(main_click);
 
   std::vector<RequiredField> required_fields = {
-      CreateRequiredField("${53}", {"#card_expiry"})};
+      CreateRequiredField(53, {"#card_expiry"})};
   required_fields[0].fallback_click_element = Selector({".option"});
 
   std::map<std::string, std::string> fallback_values = {
@@ -629,7 +642,7 @@
       .WillRepeatedly(RunOnceCallback<1>(OkClientStatus(), std::string()));
 
   std::vector<RequiredField> required_fields = {
-      CreateRequiredField(std::string(), {"#field"})};
+      CreateRequiredField(ValueExpression(), {"#field"})};
   std::map<std::string, std::string> fallback_values;
 
   RequiredFieldsFallbackHandler fallback_handler(
@@ -664,7 +677,9 @@
                             _))
       .WillRepeatedly(RunOnceCallback<1>(OkClientStatus(), "value"));
 
-  auto forced_field = CreateRequiredField("value", {"#forced_field"});
+  ValueExpression value_expression;
+  value_expression.add_chunk()->set_text("value");
+  auto forced_field = CreateRequiredField(value_expression, {"#forced_field"});
   forced_field.forced = true;
   std::vector<RequiredField> required_fields = {forced_field};
 
@@ -702,8 +717,8 @@
       .WillOnce(RunOnceCallback<2>(OkClientStatus()));
 
   std::vector<RequiredField> required_fields = {
-      CreateRequiredField("${51}", {"#card_name"}),
-      CreateRequiredField("${52}", {"#card_number"})};
+      CreateRequiredField(51, {"#card_name"}),
+      CreateRequiredField(52, {"#card_number"})};
 
   std::map<std::string, std::string> fallback_values = {
       {base::NumberToString(
@@ -751,8 +766,8 @@
       .WillOnce(RunOnceCallback<2>(OkClientStatus()));
 
   std::vector<RequiredField> required_fields = {
-      CreateRequiredField("${51}", {"#card_name"}),
-      CreateRequiredField("${52}", {"#card_number"})};
+      CreateRequiredField(51, {"#card_name"}),
+      CreateRequiredField(52, {"#card_number"})};
   required_fields[0].optional = true;
 
   std::map<std::string, std::string> fallback_values = {
@@ -810,8 +825,8 @@
       .WillOnce(RunOnceCallback<2>(OkClientStatus()));
 
   std::vector<RequiredField> required_fields = {
-      CreateRequiredField("${51}", {"#card_name"}),
-      CreateRequiredField("${52}", {"#card_number"})};
+      CreateRequiredField(51, {"#card_name"}),
+      CreateRequiredField(52, {"#card_number"})};
   required_fields[0].optional = true;
 
   std::map<std::string, std::string> fallback_values = {
diff --git a/components/autofill_assistant/browser/actions/get_element_status_action_unittest.cc b/components/autofill_assistant/browser/actions/get_element_status_action_unittest.cc
index ea92301..f7d596c 100644
--- a/components/autofill_assistant/browser/actions/get_element_status_action_unittest.cc
+++ b/components/autofill_assistant/browser/actions/get_element_status_action_unittest.cc
@@ -605,14 +605,11 @@
 
   AutofillValue autofill_value;
   autofill_value.mutable_profile()->set_identifier("contact");
-  autofill_value.set_value_expression(
-      base::StrCat({"${",
-                    base::NumberToString(static_cast<int>(
-                        autofill::ServerFieldType::NAME_FIRST)),
-                    "} ${",
-                    base::NumberToString(
-                        static_cast<int>(autofill::ServerFieldType::NAME_LAST)),
-                    "}"}));
+  autofill_value.mutable_value_expression()->add_chunk()->set_key(
+      static_cast<int>(autofill::ServerFieldType::NAME_FIRST));
+  autofill_value.mutable_value_expression()->add_chunk()->set_text(" ");
+  autofill_value.mutable_value_expression()->add_chunk()->set_key(
+      static_cast<int>(autofill::ServerFieldType::NAME_LAST));
 
   Selector selector({"#element"});
   *proto_.mutable_element() = selector.proto;
diff --git a/components/autofill_assistant/browser/actions/select_option_action.cc b/components/autofill_assistant/browser/actions/select_option_action.cc
index e194a08f..3bf4a27 100644
--- a/components/autofill_assistant/browser/actions/select_option_action.cc
+++ b/components/autofill_assistant/browser/actions/select_option_action.cc
@@ -53,15 +53,17 @@
       value_ = select_option.text_filter_value().re2();
       case_sensitive_ = select_option.text_filter_value().case_sensitive();
       break;
-    case SelectOptionProto::kAutofillValue: {
-      ClientStatus autofill_status = GetFormattedAutofillValue(
-          select_option.autofill_value(), delegate_->GetUserData(), &value_);
+    case SelectOptionProto::kAutofillRegexpValue: {
+      ClientStatus autofill_status =
+          GetFormattedAutofillValue(select_option.autofill_regexp_value(),
+                                    delegate_->GetUserData(), &value_);
       if (!autofill_status.ok()) {
         EndAction(autofill_status);
         return;
       }
-      case_sensitive_ =
-          select_option.autofill_value().value_expression().case_sensitive();
+      case_sensitive_ = select_option.autofill_regexp_value()
+                            .value_expression_re2()
+                            .case_sensitive();
       break;
     }
     default:
diff --git a/components/autofill_assistant/browser/actions/select_option_action_unittest.cc b/components/autofill_assistant/browser/actions/select_option_action_unittest.cc
index 217454c..f25b7a5 100644
--- a/components/autofill_assistant/browser/actions/select_option_action_unittest.cc
+++ b/components/autofill_assistant/browser/actions/select_option_action_unittest.cc
@@ -82,7 +82,7 @@
 TEST_F(SelectOptionActionTest, EmptyAutofillValueFails) {
   Selector selector({"#select"});
   *proto_.mutable_element() = selector.proto;
-  proto_.mutable_autofill_value();
+  proto_.mutable_autofill_regexp_value();
   EXPECT_CALL(
       callback_,
       Run(Pointee(Property(&ProcessedActionProto::status, INVALID_ACTION))));
@@ -136,9 +136,12 @@
 TEST_F(SelectOptionActionTest, RequestDataFromUnknownProfile) {
   Selector selector({"#select"});
   *proto_.mutable_element() = selector.proto;
-  auto* value = proto_.mutable_autofill_value();
+  auto* value = proto_.mutable_autofill_regexp_value();
   value->mutable_profile()->set_identifier("none");
-  value->mutable_value_expression()->set_re2("value");
+  value->mutable_value_expression_re2()
+      ->mutable_value_expression()
+      ->add_chunk()
+      ->set_text("value");
   EXPECT_CALL(callback_, Run(Pointee(Property(&ProcessedActionProto::status,
                                               PRECONDITION_FAILED))));
   Run();
@@ -157,13 +160,12 @@
 
   Selector selector({"#select"});
   *proto_.mutable_element() = selector.proto;
-  auto* value = proto_.mutable_autofill_value();
+  auto* value = proto_.mutable_autofill_regexp_value();
   value->mutable_profile()->set_identifier("contact");
-  value->mutable_value_expression()->set_re2(
-      base::StrCat({"${",
-                    base::NumberToString(static_cast<int>(
-                        autofill::ServerFieldType::NAME_MIDDLE)),
-                    "}"}));
+  value->mutable_value_expression_re2()
+      ->mutable_value_expression()
+      ->add_chunk()
+      ->set_key(static_cast<int>(autofill::ServerFieldType::NAME_MIDDLE));
 
   EXPECT_CALL(callback_, Run(Pointee(Property(&ProcessedActionProto::status,
                                               AUTOFILL_INFO_NOT_AVAILABLE))));
@@ -183,13 +185,12 @@
 
   Selector selector({"#select"});
   *proto_.mutable_element() = selector.proto;
-  auto* value = proto_.mutable_autofill_value();
+  auto* value = proto_.mutable_autofill_regexp_value();
   value->mutable_profile()->set_identifier("contact");
-  value->mutable_value_expression()->set_re2(
-      base::StrCat({"${",
-                    base::NumberToString(static_cast<int>(
-                        autofill::ServerFieldType::NAME_FIRST)),
-                    "}"}));
+  value->mutable_value_expression_re2()
+      ->mutable_value_expression()
+      ->add_chunk()
+      ->set_key(static_cast<int>(autofill::ServerFieldType::NAME_FIRST));
 
   Selector expected_selector = selector;
   EXPECT_CALL(mock_action_delegate_,
@@ -248,14 +249,15 @@
 
   Selector selector({"#select"});
   *proto_.mutable_element() = selector.proto;
-  auto* value = proto_.mutable_autofill_value();
+  auto* value = proto_.mutable_autofill_regexp_value();
   value->mutable_profile()->set_identifier("contact");
-  value->mutable_value_expression()->set_re2(
-      base::StrCat({"^${",
-                    base::NumberToString(static_cast<int>(
-                        autofill::ServerFieldType::PHONE_HOME_WHOLE_NUMBER)),
-                    "}$"}));
-  value->mutable_value_expression()->set_case_sensitive(true);
+  *value->mutable_value_expression_re2()->mutable_value_expression() =
+      test_util::ValueExpressionBuilder()
+          .addChunk("^")
+          .addChunk(autofill::ServerFieldType::PHONE_HOME_WHOLE_NUMBER)
+          .addChunk("$")
+          .toProto();
+  value->mutable_value_expression_re2()->set_case_sensitive(true);
 
   Selector expected_selector = selector;
   EXPECT_CALL(mock_action_delegate_,
diff --git a/components/autofill_assistant/browser/actions/set_form_field_value_action_unittest.cc b/components/autofill_assistant/browser/actions/set_form_field_value_action_unittest.cc
index 400c4e5..7473361 100644
--- a/components/autofill_assistant/browser/actions/set_form_field_value_action_unittest.cc
+++ b/components/autofill_assistant/browser/actions/set_form_field_value_action_unittest.cc
@@ -709,7 +709,7 @@
 TEST_F(SetFormFieldValueActionTest, RequestDataFromUnknownProfile) {
   auto* value = set_form_field_proto_->add_value()->mutable_autofill_value();
   value->mutable_profile()->set_identifier("none");
-  value->set_value_expression("value");
+  value->mutable_value_expression()->add_chunk()->set_text("value");
   SetFormFieldValueAction action(&mock_action_delegate_, proto_);
 
   EXPECT_CALL(callback_, Run(Pointee(Property(&ProcessedActionProto::status,
@@ -729,11 +729,8 @@
 
   auto* value = set_form_field_proto_->add_value()->mutable_autofill_value();
   value->mutable_profile()->set_identifier("contact");
-  value->set_value_expression(
-      base::StrCat({"${",
-                    base::NumberToString(static_cast<int>(
-                        autofill::ServerFieldType::NAME_MIDDLE)),
-                    "}"}));
+  value->mutable_value_expression()->add_chunk()->set_key(
+      static_cast<int>(autofill::ServerFieldType::NAME_MIDDLE));
   SetFormFieldValueAction action(&mock_action_delegate_, proto_);
 
   EXPECT_CALL(callback_, Run(Pointee(Property(&ProcessedActionProto::status,
@@ -752,11 +749,8 @@
 
   auto* value = set_form_field_proto_->add_value()->mutable_autofill_value();
   value->mutable_profile()->set_identifier("contact");
-  value->set_value_expression(
-      base::StrCat({"${",
-                    base::NumberToString(static_cast<int>(
-                        autofill::ServerFieldType::NAME_FIRST)),
-                    "}"}));
+  value->mutable_value_expression()->add_chunk()->set_key(
+      static_cast<int>(autofill::ServerFieldType::NAME_FIRST));
   SetFormFieldValueAction action(&mock_action_delegate_, proto_);
 
   const ElementFinder::Result& expected_element =
diff --git a/components/autofill_assistant/browser/actions/use_address_action_unittest.cc b/components/autofill_assistant/browser/actions/use_address_action_unittest.cc
index 1e1b10e..3410f16 100644
--- a/components/autofill_assistant/browser/actions/use_address_action_unittest.cc
+++ b/components/autofill_assistant/browser/actions/use_address_action_unittest.cc
@@ -90,10 +90,11 @@
   }
 
   RequiredFieldProto* AddRequiredField(ActionProto* action,
-                                       std::string value_expression,
-                                       std::string selector) {
+                                       autofill::ServerFieldType field,
+                                       const std::string& selector) {
     auto* required_field = action->mutable_use_address()->add_required_fields();
-    required_field->set_value_expression(value_expression);
+    required_field->mutable_value_expression()->add_chunk()->set_key(
+        static_cast<int>(field));
     *required_field->mutable_element() = ToSelectorProto(selector);
     return required_field;
   }
@@ -263,23 +264,11 @@
   InSequence seq;
 
   ActionProto action_proto = CreateUseAddressAction();
-  AddRequiredField(&action_proto,
-                   base::StrCat({"${",
-                                 base::NumberToString(static_cast<int>(
-                                     autofill::ServerFieldType::NAME_FIRST)),
-                                 "}"}),
+  AddRequiredField(&action_proto, autofill::ServerFieldType::NAME_FIRST,
                    "#first_name");
-  AddRequiredField(&action_proto,
-                   base::StrCat({"${",
-                                 base::NumberToString(static_cast<int>(
-                                     autofill::ServerFieldType::NAME_LAST)),
-                                 "}"}),
+  AddRequiredField(&action_proto, autofill::ServerFieldType::NAME_LAST,
                    "#last_name");
-  AddRequiredField(&action_proto,
-                   base::StrCat({"${",
-                                 base::NumberToString(static_cast<int>(
-                                     autofill::ServerFieldType::EMAIL_ADDRESS)),
-                                 "}"}),
+  AddRequiredField(&action_proto, autofill::ServerFieldType::EMAIL_ADDRESS,
                    "#email");
 
   // Autofill succeeds.
@@ -300,23 +289,11 @@
       .WillByDefault(RunOnceCallback<1>(OkClientStatus(), "INPUT"));
 
   ActionProto action_proto = CreateUseAddressAction();
-  AddRequiredField(&action_proto,
-                   base::StrCat({"${",
-                                 base::NumberToString(static_cast<int>(
-                                     autofill::ServerFieldType::NAME_FIRST)),
-                                 "}"}),
+  AddRequiredField(&action_proto, autofill::ServerFieldType::NAME_FIRST,
                    "#first_name");
-  AddRequiredField(&action_proto,
-                   base::StrCat({"${",
-                                 base::NumberToString(static_cast<int>(
-                                     autofill::ServerFieldType::NAME_LAST)),
-                                 "}"}),
+  AddRequiredField(&action_proto, autofill::ServerFieldType::NAME_LAST,
                    "#last_name");
-  AddRequiredField(&action_proto,
-                   base::StrCat({"${",
-                                 base::NumberToString(static_cast<int>(
-                                     autofill::ServerFieldType::EMAIL_ADDRESS)),
-                                 "}"}),
+  AddRequiredField(&action_proto, autofill::ServerFieldType::EMAIL_ADDRESS,
                    "#email");
 
   Selector email_selector({"#email"});
@@ -380,23 +357,11 @@
       .WillByDefault(RunOnceCallback<1>(OkClientStatus(), "INPUT"));
 
   ActionProto action_proto = CreateUseAddressAction();
-  AddRequiredField(&action_proto,
-                   base::StrCat({"${",
-                                 base::NumberToString(static_cast<int>(
-                                     autofill::ServerFieldType::NAME_FIRST)),
-                                 "}"}),
+  AddRequiredField(&action_proto, autofill::ServerFieldType::NAME_FIRST,
                    "#first_name");
-  AddRequiredField(&action_proto,
-                   base::StrCat({"${",
-                                 base::NumberToString(static_cast<int>(
-                                     autofill::ServerFieldType::NAME_LAST)),
-                                 "}"}),
+  AddRequiredField(&action_proto, autofill::ServerFieldType::NAME_LAST,
                    "#last_name");
-  AddRequiredField(&action_proto,
-                   base::StrCat({"${",
-                                 base::NumberToString(static_cast<int>(
-                                     autofill::ServerFieldType::EMAIL_ADDRESS)),
-                                 "}"}),
+  AddRequiredField(&action_proto, autofill::ServerFieldType::EMAIL_ADDRESS,
                    "#email");
 
   Selector first_name_selector({"#first_name"});
@@ -469,11 +434,7 @@
       .WillByDefault(RunOnceCallback<1>(OkClientStatus(), "INPUT"));
 
   ActionProto action_proto = CreateUseAddressAction();
-  AddRequiredField(&action_proto,
-                   base::StrCat({"${",
-                                 base::NumberToString(static_cast<int>(
-                                     autofill::ServerFieldType::NAME_FIRST)),
-                                 "}"}),
+  AddRequiredField(&action_proto, autofill::ServerFieldType::NAME_FIRST,
                    "#first_name");
 
   Selector first_name_selector({"#first_name"});
@@ -523,7 +484,18 @@
       .WillByDefault(RunOnceCallback<1>(OkClientStatus(), "INPUT"));
 
   ActionProto action_proto = CreateUseAddressAction();
-  AddRequiredField(&action_proto, "(+${12}) (${11}) ${10}", "#phone_number");
+  auto* required_field =
+      action_proto.mutable_use_address()->add_required_fields();
+  *required_field->mutable_value_expression() =
+      test_util::ValueExpressionBuilder()
+          .addChunk("(+")
+          .addChunk(autofill::ServerFieldType::PHONE_HOME_COUNTRY_CODE)
+          .addChunk(") (")
+          .addChunk(autofill::ServerFieldType::PHONE_HOME_CITY_CODE)
+          .addChunk(") ")
+          .addChunk(autofill::ServerFieldType::PHONE_HOME_NUMBER)
+          .toProto();
+  *required_field->mutable_element() = ToSelectorProto("#phone_number");
 
   Selector phone_number_selector({"#phone_number"});
 
@@ -567,12 +539,7 @@
 
   ActionProto action_proto = CreateUseAddressAction();
   auto* name_required = AddRequiredField(
-      &action_proto,
-      base::StrCat({"${",
-                    base::NumberToString(static_cast<int>(
-                        autofill::ServerFieldType::NAME_FIRST)),
-                    "}"}),
-      "#first_name");
+      &action_proto, autofill::ServerFieldType::NAME_FIRST, "#first_name");
   name_required->set_forced(true);
   name_required->set_fill_strategy(SIMULATE_KEY_PRESSES);
   name_required->set_delay_in_millisecond(1000);
@@ -632,11 +599,7 @@
 
   ActionProto action_proto;
   action_proto.mutable_use_address()->set_name(kAddressName);
-  AddRequiredField(&action_proto,
-                   base::StrCat({"${",
-                                 base::NumberToString(static_cast<int>(
-                                     autofill::ServerFieldType::NAME_FIRST)),
-                                 "}"}),
+  AddRequiredField(&action_proto, autofill::ServerFieldType::NAME_FIRST,
                    "#first_name");
   action_proto.mutable_use_address()->set_skip_autofill(true);
 
diff --git a/components/autofill_assistant/browser/actions/use_credit_card_action_unittest.cc b/components/autofill_assistant/browser/actions/use_credit_card_action_unittest.cc
index c6a0aeb1..f80308b5 100644
--- a/components/autofill_assistant/browser/actions/use_credit_card_action_unittest.cc
+++ b/components/autofill_assistant/browser/actions/use_credit_card_action_unittest.cc
@@ -101,10 +101,10 @@
   }
 
   RequiredFieldProto* AddRequiredField(ActionProto* action,
-                                       std::string value_expression,
-                                       std::string selector) {
+                                       int key,
+                                       const std::string& selector) {
     auto* required_field = action->mutable_use_card()->add_required_fields();
-    required_field->set_value_expression(value_expression);
+    required_field->mutable_value_expression()->add_chunk()->set_key(key);
     *required_field->mutable_element() = ToSelectorProto(selector);
     return required_field;
   }
@@ -228,14 +228,14 @@
       .WillByDefault(RunOnceCallback<1>(OkClientStatus(), "not empty"));
 
   ActionProto action = CreateUseCreditCardAction();
-  AddRequiredField(&action,
-                   base::NumberToString(static_cast<int>(
-                       AutofillFormatProto::CREDIT_CARD_VERIFICATION_CODE)),
-                   "#cvc");
-  AddRequiredField(&action,
-                   base::NumberToString(static_cast<int>(
-                       autofill::ServerFieldType::CREDIT_CARD_EXP_MONTH)),
-                   "#expmonth");
+  AddRequiredField(
+      &action,
+      static_cast<int>(AutofillFormatProto::CREDIT_CARD_VERIFICATION_CODE),
+      "#cvc");
+  AddRequiredField(
+      &action,
+      static_cast<int>(autofill::ServerFieldType::CREDIT_CARD_EXP_MONTH),
+      "#expmonth");
 
   user_data_.selected_card_ = std::make_unique<autofill::CreditCard>();
   EXPECT_CALL(mock_action_delegate_,
@@ -255,53 +255,29 @@
   ActionProto action = CreateUseCreditCardAction();
   AddRequiredField(
       &action,
-      base::StrCat({"${",
-                    base::NumberToString(static_cast<int>(
-                        AutofillFormatProto::CREDIT_CARD_VERIFICATION_CODE)),
-                    "}"}),
+      static_cast<int>(AutofillFormatProto::CREDIT_CARD_VERIFICATION_CODE),
       "#cvc");
   AddRequiredField(
       &action,
-      base::StrCat({"${",
-                    base::NumberToString(static_cast<int>(
-                        autofill::ServerFieldType::CREDIT_CARD_EXP_MONTH)),
-                    "}"}),
+      static_cast<int>(autofill::ServerFieldType::CREDIT_CARD_EXP_MONTH),
       "#expmonth");
   AddRequiredField(
       &action,
-      base::StrCat(
-          {"${",
-           base::NumberToString(static_cast<int>(
-               autofill::ServerFieldType::CREDIT_CARD_EXP_2_DIGIT_YEAR)),
-           "}"}),
+      static_cast<int>(autofill::ServerFieldType::CREDIT_CARD_EXP_2_DIGIT_YEAR),
       "#expyear2");
   AddRequiredField(
       &action,
-      base::StrCat(
-          {"${",
-           base::NumberToString(static_cast<int>(
-               autofill::ServerFieldType::CREDIT_CARD_EXP_4_DIGIT_YEAR)),
-           "}"}),
+      static_cast<int>(autofill::ServerFieldType::CREDIT_CARD_EXP_4_DIGIT_YEAR),
       "#expyear4");
   AddRequiredField(
       &action,
-      base::StrCat({"${",
-                    base::NumberToString(static_cast<int>(
-                        autofill::ServerFieldType::CREDIT_CARD_NAME_FULL)),
-                    "}"}),
+      static_cast<int>(autofill::ServerFieldType::CREDIT_CARD_NAME_FULL),
       "#card_name");
   AddRequiredField(
-      &action,
-      base::StrCat({"${",
-                    base::NumberToString(static_cast<int>(
-                        autofill::ServerFieldType::CREDIT_CARD_NUMBER)),
-                    "}"}),
+      &action, static_cast<int>(autofill::ServerFieldType::CREDIT_CARD_NUMBER),
       "#card_number");
   AddRequiredField(&action,
-                   base::StrCat({"${",
-                                 base::NumberToString(static_cast<int>(
-                                     AutofillFormatProto::CREDIT_CARD_NETWORK)),
-                                 "}"}),
+                   static_cast<int>(AutofillFormatProto::CREDIT_CARD_NETWORK),
                    "#network");
 
   EXPECT_CALL(mock_action_delegate_,
@@ -387,10 +363,7 @@
   ActionProto action = CreateUseCreditCardAction();
   auto* cvc_required = AddRequiredField(
       &action,
-      base::StrCat({"${",
-                    base::NumberToString(static_cast<int>(
-                        AutofillFormatProto::CREDIT_CARD_VERIFICATION_CODE)),
-                    "}"}),
+      static_cast<int>(AutofillFormatProto::CREDIT_CARD_VERIFICATION_CODE),
       "#cvc");
   cvc_required->set_forced(true);
   cvc_required->set_fill_strategy(SIMULATE_KEY_PRESSES);
@@ -451,10 +424,7 @@
   ActionProto action;
   AddRequiredField(
       &action,
-      base::StrCat({"${",
-                    base::NumberToString(static_cast<int>(
-                        AutofillFormatProto::CREDIT_CARD_VERIFICATION_CODE)),
-                    "}"}),
+      static_cast<int>(AutofillFormatProto::CREDIT_CARD_VERIFICATION_CODE),
       "#cvc");
   action.mutable_use_card()->set_skip_autofill(true);
 
@@ -516,10 +486,7 @@
   ActionProto action_proto = CreateUseCreditCardAction();
   AddRequiredField(
       &action_proto,
-      base::StrCat({"${",
-                    base::NumberToString(static_cast<int>(
-                        AutofillFormatProto::CREDIT_CARD_VERIFICATION_CODE)),
-                    "}"}),
+      static_cast<int>(AutofillFormatProto::CREDIT_CARD_VERIFICATION_CODE),
       "#cvc");
 
   Selector cvc_selector({"#cvc"});
@@ -570,7 +537,14 @@
       .WillByDefault(RunOnceCallback<1>(OkClientStatus(), "INPUT"));
 
   ActionProto action_proto = CreateUseCreditCardAction();
-  AddRequiredField(&action_proto, "${53} - ${55}", "#expiration_date");
+  auto* required_field = action_proto.mutable_use_card()->add_required_fields();
+  *required_field->mutable_value_expression() =
+      test_util::ValueExpressionBuilder()
+          .addChunk(autofill::ServerFieldType::CREDIT_CARD_EXP_MONTH)
+          .addChunk(" - ")
+          .addChunk(autofill::ServerFieldType::CREDIT_CARD_EXP_4_DIGIT_YEAR)
+          .toProto();
+  *required_field->mutable_element() = ToSelectorProto("#expiration_date");
 
   Selector expiration_date_selector({"#expiration_date"});
 
@@ -614,7 +588,11 @@
       .WillByDefault(RunOnceCallback<1>(OkClientStatus(), "INPUT"));
 
   ActionProto action_proto = CreateUseCreditCardAction();
-  AddRequiredField(&action_proto, "${57}", "#expiration_date");
+  AddRequiredField(
+      &action_proto,
+      static_cast<int>(
+          autofill::ServerFieldType::CREDIT_CARD_EXP_DATE_4_DIGIT_YEAR),
+      "#expiration_date");
 
   Selector expiration_date_selector({"#expiration_date"});
 
diff --git a/components/autofill_assistant/browser/field_formatter.cc b/components/autofill_assistant/browser/field_formatter.cc
index cb47cf53..c7fcfc5 100644
--- a/components/autofill_assistant/browser/field_formatter.cc
+++ b/components/autofill_assistant/browser/field_formatter.cc
@@ -84,6 +84,38 @@
   return out;
 }
 
+ClientStatus FormatExpression(
+    const ValueExpression& value_expression,
+    const std::map<std::string, std::string>& mappings,
+    bool quote_meta,
+    std::string* out_value) {
+  out_value->clear();
+  for (const auto& chunk : value_expression.chunk()) {
+    switch (chunk.chunk_case()) {
+      case ValueExpression::Chunk::kText:
+        out_value->append(chunk.text());
+        break;
+      case ValueExpression::Chunk::kKey: {
+        auto rewrite_value =
+            GetFieldValue(mappings, base::NumberToString(chunk.key()));
+        if (!rewrite_value.has_value()) {
+          return ClientStatus(AUTOFILL_INFO_NOT_AVAILABLE);
+        }
+        if (quote_meta) {
+          out_value->append(re2::RE2::QuoteMeta(*rewrite_value));
+        } else {
+          out_value->append(*rewrite_value);
+        }
+        break;
+      }
+      case ValueExpression::Chunk::CHUNK_NOT_SET:
+        return ClientStatus(INVALID_ACTION);
+    }
+  }
+
+  return OkClientStatus();
+}
+
 template <>
 std::map<std::string, std::string>
 CreateAutofillMappings<autofill::AutofillProfile>(
diff --git a/components/autofill_assistant/browser/field_formatter.h b/components/autofill_assistant/browser/field_formatter.h
index 74a9516..920fbfc 100644
--- a/components/autofill_assistant/browser/field_formatter.h
+++ b/components/autofill_assistant/browser/field_formatter.h
@@ -10,6 +10,8 @@
 #include "base/optional.h"
 #include "components/autofill/core/browser/data_model/autofill_profile.h"
 #include "components/autofill/core/browser/data_model/credit_card.h"
+#include "components/autofill_assistant/browser/action_value.pb.h"
+#include "components/autofill_assistant/browser/client_status.h"
 
 namespace autofill_assistant {
 namespace field_formatter {
@@ -24,6 +26,15 @@
     const std::map<std::string, std::string>& mappings,
     bool strict = true);
 
+// Turns a |value_expression| into a string, replacing |key| chunks with
+// corresponding values in |mappings|. This will fail if any of the keys are
+// not in |mappings|. If |quote_meta| the replacement pieces will be quoted.
+ClientStatus FormatExpression(
+    const ValueExpression& value_expression,
+    const std::map<std::string, std::string>& mappings,
+    bool quote_meta,
+    std::string* out_value);
+
 // Creates a lookup map for all non-empty autofill and custom
 // AutofillFormatProto::AutofillAssistantCustomField field types in
 // |autofill_data_model|.
diff --git a/components/autofill_assistant/browser/field_formatter_unittest.cc b/components/autofill_assistant/browser/field_formatter_unittest.cc
index f1afeda0..c717d0c 100644
--- a/components/autofill_assistant/browser/field_formatter_unittest.cc
+++ b/components/autofill_assistant/browser/field_formatter_unittest.cc
@@ -41,6 +41,47 @@
             "${keyD}valueA");
 }
 
+TEST(FieldFormatterTest, FormatExpression) {
+  std::map<std::string, std::string> mappings = {{"1", "valueA"},
+                                                 {"2", "val.ueB"}};
+  std::string result;
+
+  ValueExpression value_expression_1;
+  value_expression_1.add_chunk()->set_text("text");
+  value_expression_1.add_chunk()->set_text(" ");
+  value_expression_1.add_chunk()->set_key(1);
+  EXPECT_EQ(ACTION_APPLIED, FormatExpression(value_expression_1, mappings,
+                                             /* quote_meta=*/false, &result)
+                                .proto_status());
+  EXPECT_EQ("text valueA", result);
+
+  ValueExpression value_expression_2;
+  value_expression_2.add_chunk()->set_text("^");
+  value_expression_2.add_chunk()->set_key(2);
+  value_expression_2.add_chunk()->set_text("$");
+  EXPECT_EQ(ACTION_APPLIED, FormatExpression(value_expression_2, mappings,
+                                             /* quote_meta= */ false, &result)
+                                .proto_status());
+  EXPECT_EQ("^val.ueB$", result);
+  EXPECT_EQ(ACTION_APPLIED, FormatExpression(value_expression_2, mappings,
+                                             /* quote_meta= */ true, &result)
+                                .proto_status());
+  EXPECT_EQ("^val\\.ueB$", result);
+
+  ValueExpression value_expression_3;
+  value_expression_3.add_chunk()->set_key(3);
+  EXPECT_EQ(AUTOFILL_INFO_NOT_AVAILABLE,
+            FormatExpression(value_expression_3, mappings,
+                             /* quote_meta= */ false, &result)
+                .proto_status());
+
+  ValueExpression value_expression_4;
+  EXPECT_EQ(ACTION_APPLIED, FormatExpression(value_expression_4, mappings,
+                                             /* quote_meta= */ false, &result)
+                                .proto_status());
+  EXPECT_EQ(std::string(), result);
+}
+
 TEST(FieldFormatterTest, AutofillProfile) {
   autofill::AutofillProfile profile(base::GenerateGUID(), kFakeUrl);
   autofill::test::SetProfileInfo(
diff --git a/components/autofill_assistant/browser/service.proto b/components/autofill_assistant/browser/service.proto
index d2cf94c..4243ca8 100644
--- a/components/autofill_assistant/browser/service.proto
+++ b/components/autofill_assistant/browser/service.proto
@@ -1379,7 +1379,7 @@
     TextFilter text_filter_value = 7;
     // A value from an Autofill source. Note that this must be preceded by a
     // |CollectUserDataAction|.
-    AutofillValueRegexp autofill_value = 8;
+    AutofillValueRegexp autofill_regexp_value = 8;
   }
 
   // Defines which attribute to use for comparing the option to the expected
@@ -1490,19 +1490,16 @@
 // Message used to indicate what form fields should be filled with what
 // information coming from either the address or the credit card.
 message RequiredFieldProto {
-  // A string containing any number of "${key}" placeholders, where the key
-  // is an integer corresponding to entries from field_types.h or
+  // A value expression containing any number of |key| placeholders, where the
+  // |key| is an integer corresponding to entries from field_types.h or
   // |AutofillFormatProto::AutofillAssistantCustomField|.
   // Example:
-  // * "${3}" -> First name.
-  // * "(+${12}) (${11}) ${10}" -> phone country code, city code, number,
-  //   e.g., (+41) (79) (1234567)
-  // * "${51}" -> Full card name.
-  // * "${53}/${55}" -> expiration month / expiration year, e.g., 04/2021
+  // * 3 -> First name.
+  // * 51 -> Full card name.
   // Note that the set of actually available fields are outside of our
   // control and are retrieved automatically.
-  // A value expression set to an empty string will clear the field.
-  optional string value_expression = 6;
+  // An empty value expression will clear the field.
+  optional ValueExpression value_expression = 12;
 
   // The element to fill.
   optional SelectorProto element = 2;
@@ -1534,7 +1531,7 @@
   optional SelectorProto option_element_to_click = 9;
   optional ClickType click_type = 10;
 
-  reserved 1, 3;
+  reserved 1, 3, 6;
 }
 
 // Fill a form with an address if there is, otherwise fail this action.
diff --git a/components/autofill_assistant/browser/user_data_util.cc b/components/autofill_assistant/browser/user_data_util.cc
index e4a4e8be..196d5bcf 100644
--- a/components/autofill_assistant/browser/user_data_util.cc
+++ b/components/autofill_assistant/browser/user_data_util.cc
@@ -159,15 +159,14 @@
   return true;
 }
 
-template <typename T>
 ClientStatus ExtractProfileAndFormatAutofillValue(
-    const T& profile,
-    const std::string& value_expression,
+    const AutofillProfile& profile,
+    const ValueExpression& value_expression,
     const UserData* user_data,
     bool quote_meta,
     std::string* out_value) {
-  if (profile.identifier().empty() || value_expression.empty()) {
-    VLOG(1) << "|autofill_value| with empty "
+  if (profile.identifier().empty() || value_expression.chunk().empty()) {
+    VLOG(1) << "|value_expression| with empty "
                "|profile.identifier| or |value_expression|";
     return ClientStatus(INVALID_ACTION);
   }
@@ -182,18 +181,12 @@
   auto mappings =
       field_formatter::CreateAutofillMappings(*address,
                                               /* locale= */ "en-US");
-  if (quote_meta) {
-    for (const auto& it : mappings) {
-      mappings[it.first] = re2::RE2::QuoteMeta(it.second);
-    }
-  }
-  auto value = field_formatter::FormatString(value_expression, mappings,
-                                             /* strict= */ true);
-  if (!value.has_value()) {
-    return ClientStatus(AUTOFILL_INFO_NOT_AVAILABLE);
+  ClientStatus format_status = field_formatter::FormatExpression(
+      value_expression, mappings, quote_meta, out_value);
+  if (!format_status.ok()) {
+    return format_status;
   }
 
-  out_value->assign(*value);
   return OkClientStatus();
 }
 
@@ -421,18 +414,20 @@
 ClientStatus GetFormattedAutofillValue(const AutofillValue& autofill_value,
                                        const UserData* user_data,
                                        std::string* out_value) {
-  return ExtractProfileAndFormatAutofillValue<AutofillValue::Profile>(
+  return ExtractProfileAndFormatAutofillValue(
       autofill_value.profile(), autofill_value.value_expression(), user_data,
       /* quote_meta= */ false, out_value);
 }
 
 ClientStatus GetFormattedAutofillValue(
-    const AutofillValueRegexp& autofill_value,
+    const AutofillValueRegexp& autofill_value_regexp,
     const UserData* user_data,
     std::string* out_value) {
-  return ExtractProfileAndFormatAutofillValue<AutofillValueRegexp::Profile>(
-      autofill_value.profile(), autofill_value.value_expression().re2(),
-      user_data, /* quote_meta= */ true, out_value);
+  return ExtractProfileAndFormatAutofillValue(
+      autofill_value_regexp.profile(),
+      autofill_value_regexp.value_expression_re2().value_expression(),
+      user_data,
+      /* quote_meta= */ true, out_value);
 }
 
 void GetPasswordManagerValue(
diff --git a/components/autofill_assistant/browser/user_data_util.h b/components/autofill_assistant/browser/user_data_util.h
index e363576..246cee4 100644
--- a/components/autofill_assistant/browser/user_data_util.h
+++ b/components/autofill_assistant/browser/user_data_util.h
@@ -90,7 +90,7 @@
                                        const UserData* user_data,
                                        std::string* out_value);
 ClientStatus GetFormattedAutofillValue(
-    const AutofillValueRegexp& autofill_value,
+    const AutofillValueRegexp& autofill_value_regexp,
     const UserData* user_data,
     std::string* out_value);
 
diff --git a/components/autofill_assistant/browser/user_data_util_unittest.cc b/components/autofill_assistant/browser/user_data_util_unittest.cc
index 4db0083..a7dfe63 100644
--- a/components/autofill_assistant/browser/user_data_util_unittest.cc
+++ b/components/autofill_assistant/browser/user_data_util_unittest.cc
@@ -731,7 +731,8 @@
 TEST_F(UserDataUtilTextValueTest, RequestDataFromUnknownProfile) {
   AutofillValue autofill_value;
   autofill_value.mutable_profile()->set_identifier("none");
-  autofill_value.set_value_expression("value");
+  autofill_value.mutable_value_expression()->add_chunk()->set_text("text");
+
   std::string result;
 
   EXPECT_EQ(GetFormattedAutofillValue(autofill_value, &user_data_, &result)
@@ -752,11 +753,8 @@
 
   AutofillValue autofill_value;
   autofill_value.mutable_profile()->set_identifier("contact");
-  autofill_value.set_value_expression(
-      base::StrCat({"${",
-                    base::NumberToString(static_cast<int>(
-                        autofill::ServerFieldType::NAME_MIDDLE)),
-                    "}"}));
+  autofill_value.mutable_value_expression()->add_chunk()->set_key(
+      static_cast<int>(autofill::ServerFieldType::NAME_MIDDLE));
 
   std::string result;
 
@@ -777,11 +775,8 @@
 
   AutofillValue autofill_value;
   autofill_value.mutable_profile()->set_identifier("contact");
-  autofill_value.set_value_expression(
-      base::StrCat({"${",
-                    base::NumberToString(static_cast<int>(
-                        autofill::ServerFieldType::NAME_FIRST)),
-                    "}"}));
+  autofill_value.mutable_value_expression()->add_chunk()->set_key(
+      static_cast<int>(autofill::ServerFieldType::NAME_FIRST));
 
   std::string result;
 
@@ -801,11 +796,12 @@
 
   AutofillValueRegexp autofill_value;
   autofill_value.mutable_profile()->set_identifier("contact");
-  autofill_value.mutable_value_expression()->set_re2(
-      base::StrCat({"^${",
-                    base::NumberToString(static_cast<int>(
-                        autofill::ServerFieldType::NAME_FIRST)),
-                    "}$"}));
+  *autofill_value.mutable_value_expression_re2()->mutable_value_expression() =
+      test_util::ValueExpressionBuilder()
+          .addChunk("^")
+          .addChunk(autofill::ServerFieldType::NAME_FIRST)
+          .addChunk("$")
+          .toProto();
 
   std::string result;
 
@@ -959,13 +955,12 @@
       &user_data_);
 
   TextValue text_value;
-  AutofillValue* autofill_value = text_value.mutable_autofill_value();
-  autofill_value->mutable_profile()->set_identifier("contact");
-  autofill_value->set_value_expression(
-      base::StrCat({"${",
-                    base::NumberToString(static_cast<int>(
-                        autofill::ServerFieldType::NAME_FIRST)),
-                    "}"}));
+  text_value.mutable_autofill_value()->mutable_profile()->set_identifier(
+      "contact");
+  text_value.mutable_autofill_value()
+      ->mutable_value_expression()
+      ->add_chunk()
+      ->set_key(static_cast<int>(autofill::ServerFieldType::NAME_FIRST));
 
   EXPECT_CALL(*this, OnResult(EqualsStatus(OkClientStatus()), "John"));
 
diff --git a/components/exo/client_controlled_shell_surface.cc b/components/exo/client_controlled_shell_surface.cc
index 5c340c1..dab4714 100644
--- a/components/exo/client_controlled_shell_surface.cc
+++ b/components/exo/client_controlled_shell_surface.cc
@@ -1036,7 +1036,8 @@
 gfx::Rect ClientControlledShellSurface::GetShadowBounds() const {
   gfx::Rect shadow_bounds = ShellSurfaceBase::GetShadowBounds();
   const ash::NonClientFrameViewAsh* frame_view = GetFrameView();
-  if (frame_view->GetFrameEnabled()) {
+  if (frame_view->GetFrameEnabled() && !shadow_bounds_->IsEmpty() &&
+      !geometry_.IsEmpty()) {
     // The client controlled geometry is only for the client
     // area. When the chrome side frame is enabled, the shadow height
     // has to include the height of the frame, and the total height is
diff --git a/components/exo/client_controlled_shell_surface.h b/components/exo/client_controlled_shell_surface.h
index b10f6bd8..9b3f5b6 100644
--- a/components/exo/client_controlled_shell_surface.h
+++ b/components/exo/client_controlled_shell_surface.h
@@ -252,6 +252,8 @@
   float GetScale() const override;
 
  private:
+  FRIEND_TEST_ALL_PREFIXES(ClientControlledShellSurfaceTest,
+                           OverlayShadowBounds);
   class ScopedSetBoundsLocally;
   class ScopedLockedToRoot;
 
diff --git a/components/exo/client_controlled_shell_surface_unittest.cc b/components/exo/client_controlled_shell_surface_unittest.cc
index fa76475..182a4cdf 100644
--- a/components/exo/client_controlled_shell_surface_unittest.cc
+++ b/components/exo/client_controlled_shell_surface_unittest.cc
@@ -2704,4 +2704,36 @@
   }
 }
 
+TEST_F(ClientControlledShellSurfaceTest, OverlayShadowBounds) {
+  gfx::Size buffer_size(1, 1);
+  std::unique_ptr<Buffer> buffer(
+      new Buffer(exo_test_helper()->CreateGpuMemoryBuffer(buffer_size)));
+  std::unique_ptr<Surface> surface(new Surface);
+  auto shell_surface =
+      exo_test_helper()->CreateClientControlledShellSurface(surface.get());
+  surface->Attach(buffer.get());
+  surface->Commit();
+
+  display::Display primary_display =
+      display::Screen::GetScreen()->GetPrimaryDisplay();
+  gfx::Rect initial_bounds(150, 10, 200, 200);
+  shell_surface->SetBounds(primary_display.id(), initial_bounds);
+  shell_surface->OnSetFrame(SurfaceFrameType::NORMAL);
+  surface->Commit();
+
+  EXPECT_FALSE(shell_surface->HasOverlay());
+
+  ShellSurfaceBase::OverlayParams params(std::make_unique<views::View>());
+  params.overlaps_frame = false;
+  shell_surface->AddOverlay(std::move(params));
+  EXPECT_TRUE(shell_surface->HasOverlay());
+
+  {
+    gfx::Size overlay_size =
+        shell_surface->GetWidget()->GetWindowBoundsInScreen().size();
+    gfx::Size shadow_size = shell_surface->GetShadowBounds().size();
+    EXPECT_EQ(shadow_size, overlay_size);
+  }
+}
+
 }  // namespace exo
diff --git a/components/exo/shell_surface_util.cc b/components/exo/shell_surface_util.cc
index 5fdac05..e38d6dd2 100644
--- a/components/exo/shell_surface_util.cc
+++ b/components/exo/shell_surface_util.cc
@@ -260,9 +260,7 @@
 Surface* GetTargetSurfaceForKeyboardFocus(aura::Window* window) {
   if (!window)
     return nullptr;
-  Surface* const surface = Surface::AsSurface(window);
-  if (surface)
-    return surface;
+  // The keyboard focus should be set to the root surface.
   ShellSurfaceBase* shell_surface_base = nullptr;
   for (auto* current = window; current && !shell_surface_base;
        current = current->parent()) {
@@ -275,7 +273,10 @@
        shell_surface_base->host_window()->Contains(window))) {
     return shell_surface_base->root_surface();
   }
-  return nullptr;
+
+  // Fallback to the window's surface if any. This is used for
+  // notifications.
+  return Surface::AsSurface(window);
 }
 
 void GrantPermissionToActivate(aura::Window* window, base::TimeDelta timeout) {
diff --git a/components/exo/shell_surface_util_unittest.cc b/components/exo/shell_surface_util_unittest.cc
index 34ca098..8329554 100644
--- a/components/exo/shell_surface_util_unittest.cc
+++ b/components/exo/shell_surface_util_unittest.cc
@@ -68,7 +68,7 @@
   auto* child_surface = test::ShellSurfaceBuilder::AddChildSurface(
       root_surface, {10, 10, 10, 10});
 
-  EXPECT_EQ(child_surface,
+  EXPECT_EQ(root_surface,
             GetTargetSurfaceForKeyboardFocus(child_surface->window()));
   EXPECT_EQ(root_surface,
             GetTargetSurfaceForKeyboardFocus(root_surface->window()));
diff --git a/components/history/core/browser/top_sites_impl.cc b/components/history/core/browser/top_sites_impl.cc
index f604963..587d9a9 100644
--- a/components/history/core/browser/top_sites_impl.cc
+++ b/components/history/core/browser/top_sites_impl.cc
@@ -145,7 +145,7 @@
 bool TopSitesImpl::HasBlockedUrls() const {
   const base::DictionaryValue* blocked_urls =
       pref_service_->GetDictionary(kBlockedUrlsPrefsKey);
-  return blocked_urls && !blocked_urls->empty();
+  return blocked_urls && !blocked_urls->DictEmpty();
 }
 
 void TopSitesImpl::AddBlockedUrl(const GURL& url) {
diff --git a/components/metrics/structured/event_base.cc b/components/metrics/structured/event_base.cc
index f1ec5364..0084541b 100644
--- a/components/metrics/structured/event_base.cc
+++ b/components/metrics/structured/event_base.cc
@@ -28,7 +28,7 @@
   metrics_.push_back(metric);
 }
 
-void EventBase::AddIntMetric(uint64_t name_hash, int value) {
+void EventBase::AddIntMetric(uint64_t name_hash, int64_t value) {
   Metric metric(name_hash, MetricType::kInt);
   metric.int_value = value;
   metrics_.push_back(metric);
diff --git a/components/metrics/structured/event_base.h b/components/metrics/structured/event_base.h
index 2c80adb..2d5ac75f 100644
--- a/components/metrics/structured/event_base.h
+++ b/components/metrics/structured/event_base.h
@@ -53,7 +53,7 @@
     // only the HMAC digest will be reported, so it is safe to put any value
     // here.
     std::string string_value;
-    int int_value;
+    int64_t int_value;
   };
 
   // Finalizes the event and sends it for recording. After this call, the event
@@ -75,7 +75,7 @@
 
   void AddStringMetric(uint64_t name_hash, const std::string& value);
 
-  void AddIntMetric(uint64_t name_hash, int value);
+  void AddIntMetric(uint64_t name_hash, int64_t value);
 
  private:
   // First 8 bytes of the MD5 hash of the event name, as defined in
diff --git a/components/metrics/structured/structured_metrics_provider_unittest.cc b/components/metrics/structured/structured_metrics_provider_unittest.cc
index 90bf1bc..99f906a 100644
--- a/components/metrics/structured/structured_metrics_provider_unittest.cc
+++ b/components/metrics/structured/structured_metrics_provider_unittest.cc
@@ -417,6 +417,20 @@
   histogram_tester_.ExpectTotalCount("UMA.StructuredMetrics.InternalError", 0);
 }
 
+// Check that a full int64 can be recorded, and is not truncated to an int32.
+TEST_F(StructuredMetricsProviderTest, Int64MetricsNotTruncated) {
+  Init();
+  const int64_t big = 1ll << 60;
+  events::test_project_one::TestEventOne().SetTestMetricTwo(big).Record();
+
+  const auto data = GetIndependentMetrics();
+  ASSERT_EQ(data.events_size(), 1);
+  const auto& event = data.events(0);
+  ASSERT_EQ(event.metrics_size(), 1);
+  const auto& metric = event.metrics(0);
+  EXPECT_EQ(metric.value_int64(), big);
+}
+
 TEST_F(StructuredMetricsProviderTest, EventsWithinProjectReportedWithSameID) {
   WriteTestingKeys();
   Init();
diff --git a/components/policy/core/common/cloud/user_cloud_policy_store_base.cc b/components/policy/core/common/cloud/user_cloud_policy_store_base.cc
index c69107bb..e5d19e1 100644
--- a/components/policy/core/common/cloud/user_cloud_policy_store_base.cc
+++ b/components/policy/core/common/cloud/user_cloud_policy_store_base.cc
@@ -60,8 +60,8 @@
 #else
   PolicyPerProfileFilter filter = PolicyPerProfileFilter::kAny;
 #endif
-  DecodeProtoFieldsPerProfile(*payload, external_data_manager(), policy_source_,
-                              policy_scope_, &policy_map_, filter);
+  DecodeProtoFields(*payload, external_data_manager(), policy_source_,
+                    policy_scope_, &policy_map_, filter);
 
   if (policy_data->user_affiliation_ids_size() > 0) {
     policy_map_.SetUserAffiliationIds(
diff --git a/components/policy/core/common/policy_loader_lacros.cc b/components/policy/core/common/policy_loader_lacros.cc
index 6a5fdc3..d5f1613 100644
--- a/components/policy/core/common/policy_loader_lacros.cc
+++ b/components/policy/core/common/policy_loader_lacros.cc
@@ -82,10 +82,10 @@
 
   PolicyMap policy_map;
   base::WeakPtr<CloudExternalDataManager> external_data_manager;
-  DecodeProtoFieldsPerProfile(*(validator.payload()), external_data_manager,
-                              PolicySource::POLICY_SOURCE_CLOUD,
-                              PolicyScope::POLICY_SCOPE_USER, &policy_map,
-                              PolicyPerProfileFilter::kFalse);
+  DecodeProtoFields(*(validator.payload()), external_data_manager,
+                    PolicySource::POLICY_SOURCE_CLOUD,
+                    PolicyScope::POLICY_SCOPE_USER, &policy_map,
+                    PolicyPerProfileFilter::kFalse);
   bundle->Get(PolicyNamespace(POLICY_DOMAIN_CHROME, std::string()))
       .MergeFrom(policy_map);
   return bundle;
diff --git a/components/policy/core/common/policy_proto_decoders.cc b/components/policy/core/common/policy_proto_decoders.cc
index 27cac6d..9b3bf03 100644
--- a/components/policy/core/common/policy_proto_decoders.cc
+++ b/components/policy/core/common/policy_proto_decoders.cc
@@ -124,16 +124,6 @@
     base::WeakPtr<CloudExternalDataManager> external_data_manager,
     PolicySource source,
     PolicyScope scope,
-    PolicyMap* map) {
-  DecodeProtoFieldsPerProfile(policy, external_data_manager, source, scope, map,
-                              PolicyPerProfileFilter::kAny);
-}
-
-void DecodeProtoFieldsPerProfile(
-    const em::CloudPolicySettings& policy,
-    base::WeakPtr<CloudExternalDataManager> external_data_manager,
-    PolicySource source,
-    PolicyScope scope,
     PolicyMap* map,
     PolicyPerProfileFilter per_profile) {
   PolicyLevel level;
diff --git a/components/policy/core/common/policy_proto_decoders.h b/components/policy/core/common/policy_proto_decoders.h
index 7e2a04b..4a47256f 100644
--- a/components/policy/core/common/policy_proto_decoders.h
+++ b/components/policy/core/common/policy_proto_decoders.h
@@ -27,22 +27,12 @@
   kAny
 };
 
-// Decode all of the fields in |policy| which are recognized (see the metadata
-// in policy_constants.cc) and store them in the given |map|, with the given
-// |source| and |scope|. Deprecated: Use DecodeProtoFieldsPerProfile instead.
-POLICY_EXPORT void DecodeProtoFields(
-    const enterprise_management::CloudPolicySettings& policy,
-    base::WeakPtr<CloudExternalDataManager> external_data_manager,
-    PolicySource source,
-    PolicyScope scope,
-    PolicyMap* map);
-
 // Decode all the fields in |policy| that match the needed |per_profile| flag
 // which are recognized (see the metadata in policy_constants.cc) and store them
-// in the given |map|, with the given |source| and |scope|. In case
-// |per_profile| is nullopt, the flag is ignored and all the policies are
-// included.
-POLICY_EXPORT void DecodeProtoFieldsPerProfile(
+// in the given |map|, with the given |source| and |scope|. The value of
+// |per_profile| parameter specifies which fields have to be included based on
+// per_profile flag.
+POLICY_EXPORT void DecodeProtoFields(
     const enterprise_management::CloudPolicySettings& policy,
     base::WeakPtr<CloudExternalDataManager> external_data_manager,
     PolicySource source,
diff --git a/components/printing/renderer/print_render_frame_helper.cc b/components/printing/renderer/print_render_frame_helper.cc
index e688c3134..fb3cbc5 100644
--- a/components/printing/renderer/print_render_frame_helper.cc
+++ b/components/printing/renderer/print_render_frame_helper.cc
@@ -2192,7 +2192,7 @@
     blink::WebLocalFrame* frame,
     const blink::WebNode& node,
     const base::DictionaryValue& passed_job_settings) {
-  if (passed_job_settings.empty()) {
+  if (passed_job_settings.DictEmpty()) {
     // TODO(thestig): Remove this block in the future, when we are certain this
     // is not reachable.
     NOTREACHED();
diff --git a/components/search/ntp_features.cc b/components/search/ntp_features.cc
index 8657556..65f22d93 100644
--- a/components/search/ntp_features.cc
+++ b/components/search/ntp_features.cc
@@ -109,9 +109,9 @@
 const char kNtpStatefulTasksModuleDataParam[] =
     "NtpStatefulTasksModuleDataParam";
 const char kNtpChromeCartModuleDataParam[] = "NtpChromeCartModuleDataParam";
-
 const char kNtpChromeCartModuleAbandonedCartDiscountParam[] =
     "NtpChromeCartModuleAbandonedCartDiscountParam";
+const char kNtpDriveModuleDataParam[] = "NtpDriveModuleDataParam";
 
 base::Time GetLocalHistoryRepeatableQueriesAgeThreshold() {
   const base::TimeDelta kLocalHistoryRepeatableQueriesAgeThreshold =
diff --git a/components/search/ntp_features.h b/components/search/ntp_features.h
index 823eb16..afb256cb 100644
--- a/components/search/ntp_features.h
+++ b/components/search/ntp_features.h
@@ -68,6 +68,8 @@
 extern const char kNtpChromeCartModuleDataParam[];
 // Parameter for enabling the abandoned cart discount.
 extern const char kNtpChromeCartModuleAbandonedCartDiscountParam[];
+// Parameter determining the type of Drive data to render.
+extern const char kNtpDriveModuleDataParam[];
 
 // Returns the age threshold for local history repeatable queries.
 base::Time GetLocalHistoryRepeatableQueriesAgeThreshold();
diff --git a/components/services/app_service/public/cpp/instance.cc b/components/services/app_service/public/cpp/instance.cc
index 0eba9b740..2efb8d9 100644
--- a/components/services/app_service/public/cpp/instance.cc
+++ b/components/services/app_service/public/cpp/instance.cc
@@ -8,15 +8,24 @@
 
 namespace apps {
 
-Instance::Instance(const std::string& app_id, aura::Window* window)
-    : app_id_(app_id), window_(window) {
+Instance::InstanceKey::InstanceKey(aura::Window* window) : window_(window) {}
+
+bool Instance::InstanceKey::operator<(const InstanceKey& other) {
+  return this->Window() < other.Window();
+}
+
+Instance::Instance(const std::string& app_id,
+                   std::unique_ptr<InstanceKey> instance_key)
+    : app_id_(app_id), instance_key_(std::move(instance_key)) {
+  DCHECK(instance_key_);
   state_ = InstanceState::kUnknown;
 }
 
 Instance::~Instance() = default;
 
 std::unique_ptr<Instance> Instance::Clone() {
-  auto instance = std::make_unique<Instance>(this->AppId(), this->Window());
+  auto instance = std::make_unique<Instance>(
+      this->AppId(), std::make_unique<InstanceKey>(this->Window()));
   instance->SetLaunchId(this->LaunchId());
   instance->UpdateState(this->State(), this->LastUpdatedTime());
   instance->SetBrowserContext(this->BrowserContext());
diff --git a/components/services/app_service/public/cpp/instance.h b/components/services/app_service/public/cpp/instance.h
index 45c1a5c..83b7772 100644
--- a/components/services/app_service/public/cpp/instance.h
+++ b/components/services/app_service/public/cpp/instance.h
@@ -7,6 +7,7 @@
 
 #include <memory>
 #include <string>
+#include <utility>
 
 #include "base/time/time.h"
 #include "content/public/browser/browser_context.h"
@@ -27,7 +28,24 @@
 // Instance is used to represent an App Instance, or a running app.
 class Instance {
  public:
-  Instance(const std::string& app_id, aura::Window* window);
+  // InstanceKey is the unique key for the instance.
+  class InstanceKey {
+   public:
+    explicit InstanceKey(aura::Window* window);
+    ~InstanceKey() = default;
+    aura::Window* Window() const { return window_; }
+    bool operator<(const InstanceKey& other);
+
+   private:
+    // window_ is owned by ash and will be deleted when the user closes the
+    // window. Instance itself doesn't observe the window. The window's observer
+    // is responsible to delete Instance from InstanceRegistry when the window
+    // is destroyed.
+    aura::Window* window_;
+  };
+
+  Instance(const std::string& app_id,
+           std::unique_ptr<InstanceKey> instance_key);
   ~Instance();
 
   Instance(const Instance&) = delete;
@@ -40,7 +58,8 @@
   void SetBrowserContext(content::BrowserContext* browser_context);
 
   const std::string& AppId() const { return app_id_; }
-  aura::Window* Window() const { return window_; }
+  const InstanceKey& GetInstanceKey() const { return *instance_key_; }
+  aura::Window* Window() const { return instance_key_->Window(); }
   const std::string& LaunchId() const { return launch_id_; }
   InstanceState State() const { return state_; }
   const base::Time& LastUpdatedTime() const { return last_updated_time_; }
@@ -48,12 +67,7 @@
 
  private:
   std::string app_id_;
-
-  // window_ is owned by ash and will be deleted when the user closes the
-  // window. Instance itself doesn't observe the window. The window's observer
-  // is responsible to delete Instance from InstanceRegistry when the window is
-  // destroyed.
-  aura::Window* window_;
+  std::unique_ptr<InstanceKey> instance_key_;
   std::string launch_id_;
   InstanceState state_;
   base::Time last_updated_time_;
diff --git a/components/services/app_service/public/cpp/instance_registry_unittest.cc b/components/services/app_service/public/cpp/instance_registry_unittest.cc
index 49120032..7f2c17f 100644
--- a/components/services/app_service/public/cpp/instance_registry_unittest.cc
+++ b/components/services/app_service/public/cpp/instance_registry_unittest.cc
@@ -20,8 +20,8 @@
       aura::Window* window,
       apps::InstanceState state = apps::InstanceState::kUnknown,
       base::Time time = base::Time()) {
-    std::unique_ptr<apps::Instance> instance =
-        std::make_unique<apps::Instance>(app_id, window);
+    std::unique_ptr<apps::Instance> instance = std::make_unique<apps::Instance>(
+        app_id, std::make_unique<apps::Instance::InstanceKey>(window));
     instance->UpdateState(state, time);
     return instance;
   }
diff --git a/components/services/app_service/public/cpp/instance_update_unittest.cc b/components/services/app_service/public/cpp/instance_update_unittest.cc
index 5eb5802..57e307a 100644
--- a/components/services/app_service/public/cpp/instance_update_unittest.cc
+++ b/components/services/app_service/public/cpp/instance_update_unittest.cc
@@ -109,8 +109,8 @@
 TEST_F(InstanceUpdateTest, StateIsNonNull) {
   aura::Window window(nullptr);
   window.Init(ui::LAYER_NOT_DRAWN);
-  std::unique_ptr<apps::Instance> state =
-      std::make_unique<apps::Instance>(app_id, &window);
+  std::unique_ptr<apps::Instance> state = std::make_unique<apps::Instance>(
+      app_id, std::make_unique<apps::Instance::InstanceKey>(&window));
   EXPECT_TRUE(apps::InstanceUpdate::Equals(state.get(), nullptr));
   TestInstanceUpdate(state.get(), nullptr);
 }
@@ -118,8 +118,8 @@
 TEST_F(InstanceUpdateTest, DeltaIsNonNull) {
   aura::Window window(nullptr);
   window.Init(ui::LAYER_NOT_DRAWN);
-  std::unique_ptr<apps::Instance> delta =
-      std::make_unique<apps::Instance>(app_id, &window);
+  std::unique_ptr<apps::Instance> delta = std::make_unique<apps::Instance>(
+      app_id, std::make_unique<apps::Instance::InstanceKey>(&window));
   EXPECT_FALSE(apps::InstanceUpdate::Equals(nullptr, delta.get()));
   TestInstanceUpdate(nullptr, delta.get());
 }
@@ -127,10 +127,10 @@
 TEST_F(InstanceUpdateTest, BothAreNonNull) {
   aura::Window window(nullptr);
   window.Init(ui::LAYER_NOT_DRAWN);
-  std::unique_ptr<apps::Instance> state =
-      std::make_unique<apps::Instance>(app_id, &window);
-  std::unique_ptr<apps::Instance> delta =
-      std::make_unique<apps::Instance>(app_id, &window);
+  std::unique_ptr<apps::Instance> state = std::make_unique<apps::Instance>(
+      app_id, std::make_unique<apps::Instance::InstanceKey>(&window));
+  std::unique_ptr<apps::Instance> delta = std::make_unique<apps::Instance>(
+      app_id, std::make_unique<apps::Instance::InstanceKey>(&window));
   EXPECT_TRUE(apps::InstanceUpdate::Equals(state.get(), delta.get()));
   TestInstanceUpdate(state.get(), delta.get());
 }
@@ -138,10 +138,10 @@
 TEST_F(InstanceUpdateTest, LaunchIdIsUpdated) {
   aura::Window window(nullptr);
   window.Init(ui::LAYER_NOT_DRAWN);
-  std::unique_ptr<apps::Instance> state =
-      std::make_unique<apps::Instance>(app_id, &window);
-  std::unique_ptr<apps::Instance> delta =
-      std::make_unique<apps::Instance>(app_id, &window);
+  std::unique_ptr<apps::Instance> state = std::make_unique<apps::Instance>(
+      app_id, std::make_unique<apps::Instance::InstanceKey>(&window));
+  std::unique_ptr<apps::Instance> delta = std::make_unique<apps::Instance>(
+      app_id, std::make_unique<apps::Instance::InstanceKey>(&window));
   delta->SetLaunchId("abc");
   EXPECT_FALSE(apps::InstanceUpdate::Equals(state.get(), delta.get()));
 }
@@ -149,21 +149,21 @@
 TEST_F(InstanceUpdateTest, LaunchIdIsNotUpdated) {
   aura::Window window(nullptr);
   window.Init(ui::LAYER_NOT_DRAWN);
-  std::unique_ptr<apps::Instance> state =
-      std::make_unique<apps::Instance>(app_id, &window);
+  std::unique_ptr<apps::Instance> state = std::make_unique<apps::Instance>(
+      app_id, std::make_unique<apps::Instance::InstanceKey>(&window));
   state->SetLaunchId("abc");
-  std::unique_ptr<apps::Instance> delta =
-      std::make_unique<apps::Instance>(app_id, &window);
+  std::unique_ptr<apps::Instance> delta = std::make_unique<apps::Instance>(
+      app_id, std::make_unique<apps::Instance::InstanceKey>(&window));
   EXPECT_TRUE(apps::InstanceUpdate::Equals(state.get(), delta.get()));
 }
 
 TEST_F(InstanceUpdateTest, StateIsUpdated) {
   aura::Window window(nullptr);
   window.Init(ui::LAYER_NOT_DRAWN);
-  std::unique_ptr<apps::Instance> state =
-      std::make_unique<apps::Instance>(app_id, &window);
-  std::unique_ptr<apps::Instance> delta =
-      std::make_unique<apps::Instance>(app_id, &window);
+  std::unique_ptr<apps::Instance> state = std::make_unique<apps::Instance>(
+      app_id, std::make_unique<apps::Instance::InstanceKey>(&window));
+  std::unique_ptr<apps::Instance> delta = std::make_unique<apps::Instance>(
+      app_id, std::make_unique<apps::Instance::InstanceKey>(&window));
   delta->UpdateState(apps::InstanceState::kStarted, base::Time::Now());
   EXPECT_FALSE(apps::InstanceUpdate::Equals(state.get(), delta.get()));
 }
@@ -171,23 +171,23 @@
 TEST_F(InstanceUpdateTest, StateIsNotUpdated) {
   aura::Window window(nullptr);
   window.Init(ui::LAYER_NOT_DRAWN);
-  std::unique_ptr<apps::Instance> state =
-      std::make_unique<apps::Instance>(app_id, &window);
+  std::unique_ptr<apps::Instance> state = std::make_unique<apps::Instance>(
+      app_id, std::make_unique<apps::Instance::InstanceKey>(&window));
   state->UpdateState(apps::InstanceState::kStarted, base::Time::Now());
-  std::unique_ptr<apps::Instance> delta =
-      std::make_unique<apps::Instance>(app_id, &window);
+  std::unique_ptr<apps::Instance> delta = std::make_unique<apps::Instance>(
+      app_id, std::make_unique<apps::Instance::InstanceKey>(&window));
   EXPECT_TRUE(apps::InstanceUpdate::Equals(state.get(), delta.get()));
 }
 
 TEST_F(InstanceUpdateTest, BothLaunchAndStateIsUpdated) {
   aura::Window window(nullptr);
   window.Init(ui::LAYER_NOT_DRAWN);
-  std::unique_ptr<apps::Instance> state =
-      std::make_unique<apps::Instance>(app_id, &window);
+  std::unique_ptr<apps::Instance> state = std::make_unique<apps::Instance>(
+      app_id, std::make_unique<apps::Instance::InstanceKey>(&window));
   state->SetLaunchId("aaa");
   state->UpdateState(apps::InstanceState::kStarted, base::Time::Now());
-  std::unique_ptr<apps::Instance> delta =
-      std::make_unique<apps::Instance>(app_id, &window);
+  std::unique_ptr<apps::Instance> delta = std::make_unique<apps::Instance>(
+      app_id, std::make_unique<apps::Instance::InstanceKey>(&window));
   delta->SetLaunchId("bbb");
   delta->UpdateState(apps::InstanceState::kRunning, base::Time::Now());
   EXPECT_FALSE(apps::InstanceUpdate::Equals(state.get(), delta.get()));
@@ -196,10 +196,10 @@
 TEST_F(InstanceUpdateTest, BrowserContextIsUpdated) {
   aura::Window window(nullptr);
   window.Init(ui::LAYER_NOT_DRAWN);
-  std::unique_ptr<apps::Instance> state =
-      std::make_unique<apps::Instance>(app_id, &window);
-  std::unique_ptr<apps::Instance> delta =
-      std::make_unique<apps::Instance>(app_id, &window);
+  std::unique_ptr<apps::Instance> state = std::make_unique<apps::Instance>(
+      app_id, std::make_unique<apps::Instance::InstanceKey>(&window));
+  std::unique_ptr<apps::Instance> delta = std::make_unique<apps::Instance>(
+      app_id, std::make_unique<apps::Instance::InstanceKey>(&window));
   delta->SetBrowserContext(&profile_);
   EXPECT_FALSE(apps::InstanceUpdate::Equals(state.get(), delta.get()));
 }
@@ -207,10 +207,10 @@
 TEST_F(InstanceUpdateTest, BrowserContextIsNotUpdated) {
   aura::Window window(nullptr);
   window.Init(ui::LAYER_NOT_DRAWN);
-  std::unique_ptr<apps::Instance> state =
-      std::make_unique<apps::Instance>(app_id, &window);
+  std::unique_ptr<apps::Instance> state = std::make_unique<apps::Instance>(
+      app_id, std::make_unique<apps::Instance::InstanceKey>(&window));
   state->SetBrowserContext(&profile_);
-  std::unique_ptr<apps::Instance> delta =
-      std::make_unique<apps::Instance>(app_id, &window);
+  std::unique_ptr<apps::Instance> delta = std::make_unique<apps::Instance>(
+      app_id, std::make_unique<apps::Instance::InstanceKey>(&window));
   EXPECT_TRUE(apps::InstanceUpdate::Equals(state.get(), delta.get()));
 }
diff --git a/components/sync/base/sync_prefs.h b/components/sync/base/sync_prefs.h
index 86fdec0..1b67a12 100644
--- a/components/sync/base/sync_prefs.h
+++ b/components/sync/base/sync_prefs.h
@@ -21,7 +21,6 @@
 #include "components/prefs/pref_member.h"
 #include "components/sync/base/model_type.h"
 #include "components/sync/base/user_selectable_type.h"
-#include "components/sync/protocol/sync.pb.h"
 
 class PrefRegistrySimple;
 class PrefService;
diff --git a/components/sync/driver/backend_migrator.cc b/components/sync/driver/backend_migrator.cc
index 4d53e1a..5809f5a 100644
--- a/components/sync/driver/backend_migrator.cc
+++ b/components/sync/driver/backend_migrator.cc
@@ -10,7 +10,6 @@
 #include "base/sequenced_task_runner.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/threading/sequenced_task_runner_handle.h"
-#include "components/sync/protocol/sync.pb.h"
 
 namespace syncer {
 
diff --git a/components/sync/driver/backend_migrator_unittest.cc b/components/sync/driver/backend_migrator_unittest.cc
index f34b8e3..16d2609f 100644
--- a/components/sync/driver/backend_migrator_unittest.cc
+++ b/components/sync/driver/backend_migrator_unittest.cc
@@ -11,7 +11,6 @@
 #include "base/test/task_environment.h"
 #include "components/sync/base/model_type_test_util.h"
 #include "components/sync/driver/data_type_manager_mock.h"
-#include "components/sync/protocol/sync.pb.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
diff --git a/components/sync/engine/commit_processor.cc b/components/sync/engine/commit_processor.cc
index 96a1dc6..aa4f559 100644
--- a/components/sync/engine/commit_processor.cc
+++ b/components/sync/engine/commit_processor.cc
@@ -4,6 +4,7 @@
 
 #include "components/sync/engine/commit_processor.h"
 
+#include <map>
 #include <memory>
 #include <utility>
 
@@ -12,7 +13,6 @@
 #include "base/notreached.h"
 #include "components/sync/engine/commit_contribution.h"
 #include "components/sync/engine/commit_contributor.h"
-#include "components/sync/protocol/sync.pb.h"
 
 namespace syncer {
 
diff --git a/components/sync/engine/cycle/model_neutral_state.h b/components/sync/engine/cycle/model_neutral_state.h
index c1888e7..8afc7eb 100644
--- a/components/sync/engine/cycle/model_neutral_state.h
+++ b/components/sync/engine/cycle/model_neutral_state.h
@@ -7,7 +7,6 @@
 
 #include "components/sync/base/model_type.h"
 #include "components/sync/base/syncer_error.h"
-#include "components/sync/protocol/sync.pb.h"
 
 namespace syncer {
 
diff --git a/components/sync/engine/cycle/sync_cycle_snapshot.h b/components/sync/engine/cycle/sync_cycle_snapshot.h
index 64c1861..e7bc7a7 100644
--- a/components/sync/engine/cycle/sync_cycle_snapshot.h
+++ b/components/sync/engine/cycle/sync_cycle_snapshot.h
@@ -15,6 +15,7 @@
 #include "components/sync/base/model_type.h"
 #include "components/sync/base/progress_marker_map.h"
 #include "components/sync/engine/cycle/model_neutral_state.h"
+#include "components/sync/protocol/sync.pb.h"
 
 namespace base {
 class DictionaryValue;
diff --git a/components/sync/engine/net/server_connection_manager.cc b/components/sync/engine/net/server_connection_manager.cc
index 1b20fbda..ce25d01 100644
--- a/components/sync/engine/net/server_connection_manager.cc
+++ b/components/sync/engine/net/server_connection_manager.cc
@@ -13,7 +13,6 @@
 #include "components/sync/engine/cancelation_signal.h"
 #include "components/sync/engine/net/url_translator.h"
 #include "components/sync/engine/syncer.h"
-#include "components/sync/protocol/sync.pb.h"
 #include "net/http/http_status_code.h"
 #include "url/gurl.h"
 
diff --git a/components/sync/model/sync_change.cc b/components/sync/model/sync_change.cc
index f28edd0..910bdc4 100644
--- a/components/sync/model/sync_change.cc
+++ b/components/sync/model/sync_change.cc
@@ -7,7 +7,6 @@
 #include <ostream>
 
 #include "base/notreached.h"
-#include "components/sync/protocol/sync.pb.h"
 
 namespace syncer {
 
diff --git a/components/sync_sessions/tab_node_pool.cc b/components/sync_sessions/tab_node_pool.cc
index 0eb692e..6e180a0 100644
--- a/components/sync_sessions/tab_node_pool.cc
+++ b/components/sync_sessions/tab_node_pool.cc
@@ -11,7 +11,6 @@
 #include "base/logging.h"
 #include "components/sync/base/model_type.h"
 #include "components/sync/protocol/session_specifics.pb.h"
-#include "components/sync/protocol/sync.pb.h"
 #include "components/sync_sessions/synced_tab_delegate.h"
 
 namespace sync_sessions {
diff --git a/components/webapps/browser/banners/app_banner_settings_helper.cc b/components/webapps/browser/banners/app_banner_settings_helper.cc
index 8454e20..82dd13e5 100644
--- a/components/webapps/browser/banners/app_banner_settings_helper.cc
+++ b/components/webapps/browser/banners/app_banner_settings_helper.cc
@@ -101,7 +101,7 @@
                                         base::Value::Type::DICTIONARY);
     if (!dict_) {
       // Don't allow more than kMaxAppsPerSite dictionaries.
-      if (origin_dict_->size() < kMaxAppsPerSite) {
+      if (origin_dict_->DictSize() < kMaxAppsPerSite) {
         dict_ =
             origin_dict_->SetKey(package_name_or_start_url,
                                  base::Value(base::Value::Type::DICTIONARY));
diff --git a/content/browser/accessibility/accessibility_tree_formatter_android.cc b/content/browser/accessibility/accessibility_tree_formatter_android.cc
index a4f27c6..114539f0 100644
--- a/content/browser/accessibility/accessibility_tree_formatter_android.cc
+++ b/content/browser/accessibility/accessibility_tree_formatter_android.cc
@@ -141,7 +141,12 @@
 void AccessibilityTreeFormatterAndroid::RecursiveBuildTree(
     const BrowserAccessibility& node,
     base::DictionaryValue* dict) const {
+  if (!ShouldDumpNode(node))
+    return;
+
   AddProperties(node, dict);
+  if (!ShouldDumpChildren(node))
+    return;
 
   auto children = std::make_unique<base::ListValue>();
 
diff --git a/content/browser/accessibility/accessibility_tree_formatter_auralinux.cc b/content/browser/accessibility/accessibility_tree_formatter_auralinux.cc
index a7489de..88df7e5 100644
--- a/content/browser/accessibility/accessibility_tree_formatter_auralinux.cc
+++ b/content/browser/accessibility/accessibility_tree_formatter_auralinux.cc
@@ -176,7 +176,20 @@
 void AccessibilityTreeFormatterAuraLinux::RecursiveBuildTree(
     AtkObject* atk_node,
     base::DictionaryValue* dict) const {
+  ui::AXPlatformNodeAuraLinux* platform_node =
+      ui::AXPlatformNodeAuraLinux::FromAtkObject(atk_node);
+  DCHECK(platform_node);
+
+  BrowserAccessibility* node = BrowserAccessibility::FromAXPlatformNodeDelegate(
+      platform_node->GetDelegate());
+  DCHECK(node);
+
+  if (!ShouldDumpNode(*node))
+    return;
+
   AddProperties(atk_node, dict);
+  if (!ShouldDumpChildren(*node))
+    return;
 
   auto child_count = atk_object_get_n_accessible_children(atk_node);
   if (child_count <= 0)
diff --git a/content/browser/accessibility/accessibility_tree_formatter_blink.cc b/content/browser/accessibility/accessibility_tree_formatter_blink.cc
index ac022f9e..d62410f 100644
--- a/content/browser/accessibility/accessibility_tree_formatter_blink.cc
+++ b/content/browser/accessibility/accessibility_tree_formatter_blink.cc
@@ -287,7 +287,12 @@
 void AccessibilityTreeFormatterBlink::RecursiveBuildTree(
     const BrowserAccessibility& node,
     base::Value* dict) const {
+  if (!ShouldDumpNode(node))
+    return;
+
   AddProperties(node, static_cast<base::DictionaryValue*>(dict));
+  if (!ShouldDumpChildren(node))
+    return;
 
   base::Value children(base::Value::Type::LIST);
   for (size_t i = 0; i < ChildCount(node); ++i) {
diff --git a/content/browser/accessibility/accessibility_tree_formatter_mac.mm b/content/browser/accessibility/accessibility_tree_formatter_mac.mm
index 4336e1d0..65adc77 100644
--- a/content/browser/accessibility/accessibility_tree_formatter_mac.mm
+++ b/content/browser/accessibility/accessibility_tree_formatter_mac.mm
@@ -170,7 +170,16 @@
     const NSRect& root_rect,
     const LineIndexer* line_indexer,
     base::Value* dict) const {
+  BrowserAccessibility* platform_node =
+      [static_cast<BrowserAccessibilityCocoa*>(node) owner];
+  DCHECK(platform_node);
+
+  if (!ShouldDumpNode(*platform_node))
+    return;
+
   AddProperties(node, root_rect, line_indexer, dict);
+  if (!ShouldDumpChildren(*platform_node))
+    return;
 
   NSArray* children = ChildrenOf(node);
   base::Value child_dict_list(base::Value::Type::LIST);
diff --git a/content/browser/accessibility/accessibility_tree_formatter_win.cc b/content/browser/accessibility/accessibility_tree_formatter_win.cc
index a4e0d1f..19202eab 100644
--- a/content/browser/accessibility/accessibility_tree_formatter_win.cc
+++ b/content/browser/accessibility/accessibility_tree_formatter_win.cc
@@ -274,7 +274,19 @@
     base::Value* dict,
     LONG root_x,
     LONG root_y) const {
+  ui::AXPlatformNode* platform_node =
+      ui::AXPlatformNode::FromNativeViewAccessible(node.Get());
+  DCHECK(platform_node);
+
+  ui::AXPlatformNodeDelegate* delegate = platform_node->GetDelegate();
+  DCHECK(delegate);
+
+  if (!ShouldDumpNode(*delegate))
+    return;
+
   AddProperties(node, dict, root_x, root_y);
+  if (!ShouldDumpChildren(*delegate))
+    return;
 
   base::Value child_list(base::Value::Type::LIST);
   for (const ui::MSAAChild& msaa_child : ui::MSAAChildren(node)) {
diff --git a/content/browser/back_forward_cache_browsertest.cc b/content/browser/back_forward_cache_browsertest.cc
index cd537fb8..69da766 100644
--- a/content/browser/back_forward_cache_browsertest.cc
+++ b/content/browser/back_forward_cache_browsertest.cc
@@ -1454,8 +1454,9 @@
   ExpectCached(*delete_observers[3], /*cached=*/true, /*backgrounded=*/false);
 }
 
-// Tests that RenderFrameHost::ForEachFrame behaves correctly when bfcached.
-IN_PROC_BROWSER_TEST_F(BackForwardCacheBrowserTest, ForEachFrame) {
+// Tests that RenderFrameHost::ForEachRenderFrameHost behaves correctly when
+// bfcached.
+IN_PROC_BROWSER_TEST_F(BackForwardCacheBrowserTest, ForEachRenderFrameHost) {
   ASSERT_TRUE(embedded_test_server()->Start());
   GURL url_a(embedded_test_server()->GetURL(
       "a.com", "/cross_site_iframe_factory.html?a(b(c),d)"));
@@ -1476,7 +1477,7 @@
 
   // Ensure the visited frames are what we would expect for the page before
   // entering bfcache.
-  EXPECT_THAT(CollectAllFrames(rfh_a),
+  EXPECT_THAT(CollectAllRenderFrameHosts(rfh_a),
               testing::ElementsAre(rfh_a, rfh_b, rfh_d, rfh_c));
 
   // 2) Navigate to e.
@@ -1491,23 +1492,24 @@
 
   // When starting iteration from the primary frame, we shouldn't see any of the
   // frames in bfcache.
-  EXPECT_THAT(CollectAllFrames(rfh_e), testing::ElementsAre(rfh_e));
+  EXPECT_THAT(CollectAllRenderFrameHosts(rfh_e), testing::ElementsAre(rfh_e));
 
   // When starting iteration from a bfcached RFH, we should see the frame itself
   // and its descendants in breadth first order.
-  EXPECT_THAT(CollectAllFrames(rfh_a),
+  EXPECT_THAT(CollectAllRenderFrameHosts(rfh_a),
               testing::ElementsAre(rfh_a, rfh_b, rfh_d, rfh_c));
 
   // Ensure that starting iteration from a subframe of a bfcached frame also
   // works.
-  EXPECT_THAT(CollectAllFrames(rfh_b), testing::ElementsAre(rfh_b, rfh_c));
+  EXPECT_THAT(CollectAllRenderFrameHosts(rfh_b),
+              testing::ElementsAre(rfh_b, rfh_c));
 }
 
-// Tests that RenderFrameHostImpl::ForEachFrameIncludingSpeculative behaves
-// correctly when a FrameTreeNode has both a speculative RFH and a bfcached
-// RFH.
+// Tests that RenderFrameHostImpl::ForEachRenderFrameHostIncludingSpeculative
+// behaves correctly when a FrameTreeNode has both a speculative RFH and a
+// bfcached RFH.
 IN_PROC_BROWSER_TEST_F(BackForwardCacheBrowserTest,
-                       ForEachFrameWithSpeculative) {
+                       ForEachRenderFrameHostWithSpeculative) {
   IsolateAllSitesForTesting(base::CommandLine::ForCurrentProcess());
   ASSERT_TRUE(embedded_test_server()->Start());
   GURL url_a(embedded_test_server()->GetURL("a.com", "/title1.html"));
@@ -1546,17 +1548,17 @@
 
   // When starting iteration from the bfcached RFH, we should not see the
   // speculative RFH.
-  EXPECT_THAT(CollectAllFramesIncludingSpeculative(rfh_a),
+  EXPECT_THAT(CollectAllRenderFrameHostsIncludingSpeculative(rfh_a),
               testing::ElementsAre(rfh_a));
 
   // When starting iteration from the primary frame, we shouldn't see the
   // bfcached RFH, but we should see the speculative RFH.
-  EXPECT_THAT(CollectAllFramesIncludingSpeculative(rfh_b),
+  EXPECT_THAT(CollectAllRenderFrameHostsIncludingSpeculative(rfh_b),
               testing::UnorderedElementsAre(rfh_b, rfh_c));
 
   // When starting iteration from the speculative RFH, we should only see
   // the speculative RFH. In particular, we should not see the bfcached RFH.
-  EXPECT_THAT(CollectAllFramesIncludingSpeculative(rfh_c),
+  EXPECT_THAT(CollectAllRenderFrameHostsIncludingSpeculative(rfh_c),
               testing::ElementsAre(rfh_c));
 }
 
diff --git a/content/browser/browser_context.cc b/content/browser/browser_context.cc
index 967334f3..51860ca0 100644
--- a/content/browser/browser_context.cc
+++ b/content/browser/browser_context.cc
@@ -226,9 +226,7 @@
   return ChromeBlobStorageContext::GetBlobRemote(this, uuid);
 }
 
-// static
 void BrowserContext::DeliverPushMessage(
-    BrowserContext* self,
     const GURL& origin,
     int64_t service_worker_registration_id,
     const std::string& message_id,
@@ -236,13 +234,11 @@
     base::OnceCallback<void(blink::mojom::PushEventStatus)> callback) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
   PushMessagingRouter::DeliverMessage(
-      self, origin, service_worker_registration_id, message_id,
+      this, origin, service_worker_registration_id, message_id,
       std::move(payload), std::move(callback));
 }
 
-// static
 void BrowserContext::FirePushSubscriptionChangeEvent(
-    BrowserContext* self,
     const GURL& origin,
     int64_t service_worker_registration_id,
     blink::mojom::PushSubscriptionPtr new_subscription,
@@ -250,7 +246,7 @@
     base::OnceCallback<void(blink::mojom::PushEventStatus)> callback) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
   PushMessagingRouter::FireSubscriptionChangeEvent(
-      self, origin, service_worker_registration_id, std::move(new_subscription),
+      this, origin, service_worker_registration_id, std::move(new_subscription),
       std::move(old_subscription), std::move(callback));
 }
 
diff --git a/content/browser/devtools/protocol/service_worker_handler.cc b/content/browser/devtools/protocol/service_worker_handler.cc
index 76568357..9d8b5ed 100644
--- a/content/browser/devtools/protocol/service_worker_handler.cc
+++ b/content/browser/devtools/protocol/service_worker_handler.cc
@@ -351,9 +351,9 @@
   base::Optional<std::string> payload;
   if (data.size() > 0)
     payload = data;
-  BrowserContext::DeliverPushMessage(browser_context_, GURL(origin), id,
-                                     /* push_message_id= */ std::string(),
-                                     std::move(payload), base::DoNothing());
+  browser_context_->DeliverPushMessage(GURL(origin), id,
+                                       /* message_id= */ std::string(),
+                                       std::move(payload), base::DoNothing());
 
   return Response::Success();
 }
diff --git a/content/browser/prerender/prerender_browsertest.cc b/content/browser/prerender/prerender_browsertest.cc
index 9348f86..4f7199a1 100644
--- a/content/browser/prerender/prerender_browsertest.cc
+++ b/content/browser/prerender/prerender_browsertest.cc
@@ -808,8 +808,9 @@
   TestHostPrerenderingState(GetUrl("/page_with_blank_iframe.html"));
 }
 
-// Tests that RenderFrameHost::ForEachFrame behaves correctly when prerendering.
-IN_PROC_BROWSER_TEST_F(PrerenderBrowserTest, ForEachFrame) {
+// Tests that RenderFrameHost::ForEachRenderFrameHost behaves correctly when
+// prerendering.
+IN_PROC_BROWSER_TEST_F(PrerenderBrowserTest, ForEachRenderFrameHost) {
   const GURL kInitialUrl = GetUrl("/prerender/add_prerender.html");
   // All frames are same-origin due to prerendering restrictions for
   // cross-origin.
@@ -827,7 +828,7 @@
   RenderFrameHostImpl* rfh_sub_2 =
       prerendered_render_frame_host->child_at(1)->current_frame_host();
 
-  EXPECT_THAT(CollectAllFrames(prerendered_render_frame_host),
+  EXPECT_THAT(CollectAllRenderFrameHosts(prerendered_render_frame_host),
               testing::ElementsAre(prerendered_render_frame_host, rfh_sub_1,
                                    rfh_sub_2, rfh_sub_1_1));
 }
diff --git a/content/browser/prerender/prerender_host.cc b/content/browser/prerender/prerender_host.cc
index 77dc6f8..41d8cbc 100644
--- a/content/browser/prerender/prerender_host.cc
+++ b/content/browser/prerender/prerender_host.cc
@@ -43,7 +43,7 @@
 // because the |rfh| root node will not have a current_frame_host value. The
 // root node is set to null in MPArch prerender activation when generating a
 // BackForwardCacheImpl::Entry.
-// TODO(mcnee): Implement in terms of RenderFrameHost::ForEachFrame.
+// TODO(mcnee): Implement in terms of RenderFrameHost::ForEachRenderFrameHost.
 std::vector<RenderFrameHostImpl*> AllDescendantActiveRenderFrameHosts(
     RenderFrameHostImpl& rfh) {
   std::vector<RenderFrameHostImpl*> result;
diff --git a/content/browser/renderer_host/render_frame_host_impl.cc b/content/browser/renderer_host/render_frame_host_impl.cc
index 719f86e..a192ba52 100644
--- a/content/browser/renderer_host/render_frame_host_impl.cc
+++ b/content/browser/renderer_host/render_frame_host_impl.cc
@@ -1717,7 +1717,8 @@
 
 }  // namespace
 
-void RenderFrameHostImpl::ForEachFrame(FrameIterationCallback on_frame) {
+void RenderFrameHostImpl::ForEachRenderFrameHost(
+    FrameIterationCallback on_frame) {
   // There's no automatic conversion from FrameIterationCallback to
   // FrameIterationCallbackImpl, so this wrapper just forwards the
   // RenderFrameHost argument.
@@ -1726,35 +1727,38 @@
         return on_frame.Run(rfh);
       },
       on_frame);
-  ForEachFrame(on_frame_impl);
+  ForEachRenderFrameHost(on_frame_impl);
 }
 
-void RenderFrameHostImpl::ForEachFrame(
+void RenderFrameHostImpl::ForEachRenderFrameHost(
     FrameIterationAlwaysContinueCallback on_frame) {
-  ForEachFrame(ContinueIterationWrapper(on_frame));
+  ForEachRenderFrameHost(ContinueIterationWrapper(on_frame));
 }
 
-void RenderFrameHostImpl::ForEachFrame(FrameIterationCallbackImpl on_frame) {
-  ForEachFrameImpl(on_frame, /* include_speculative */ false);
-}
-
-void RenderFrameHostImpl::ForEachFrame(
-    FrameIterationAlwaysContinueCallbackImpl on_frame) {
-  ForEachFrame(ContinueIterationWrapper(on_frame));
-}
-
-void RenderFrameHostImpl::ForEachFrameIncludingSpeculative(
+void RenderFrameHostImpl::ForEachRenderFrameHost(
     FrameIterationCallbackImpl on_frame) {
-  ForEachFrameImpl(on_frame, /* include_speculative */ true);
+  ForEachRenderFrameHostImpl(on_frame, /* include_speculative */ false);
 }
 
-void RenderFrameHostImpl::ForEachFrameIncludingSpeculative(
+void RenderFrameHostImpl::ForEachRenderFrameHost(
     FrameIterationAlwaysContinueCallbackImpl on_frame) {
-  ForEachFrameIncludingSpeculative(ContinueIterationWrapper(on_frame));
+  ForEachRenderFrameHost(ContinueIterationWrapper(on_frame));
 }
 
-void RenderFrameHostImpl::ForEachFrameImpl(FrameIterationCallbackImpl on_frame,
-                                           bool include_speculative) {
+void RenderFrameHostImpl::ForEachRenderFrameHostIncludingSpeculative(
+    FrameIterationCallbackImpl on_frame) {
+  ForEachRenderFrameHostImpl(on_frame, /* include_speculative */ true);
+}
+
+void RenderFrameHostImpl::ForEachRenderFrameHostIncludingSpeculative(
+    FrameIterationAlwaysContinueCallbackImpl on_frame) {
+  ForEachRenderFrameHostIncludingSpeculative(
+      ContinueIterationWrapper(on_frame));
+}
+
+void RenderFrameHostImpl::ForEachRenderFrameHostImpl(
+    FrameIterationCallbackImpl on_frame,
+    bool include_speculative) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
 
   if (!include_speculative &&
@@ -2921,7 +2925,7 @@
 
   // TODO(danakj): We only blocked the main frame, so we should only need to
   // resume that?
-  ForEachFrameIncludingSpeculative(base::BindRepeating(
+  ForEachRenderFrameHostIncludingSpeculative(base::BindRepeating(
       [](RenderFrameHostImpl* main_rfh,
          RenderFrameHostImpl* render_frame_host) {
         // Inner frame trees shouldn't be possible here.
diff --git a/content/browser/renderer_host/render_frame_host_impl.h b/content/browser/renderer_host/render_frame_host_impl.h
index d7f2075..05ca058e 100644
--- a/content/browser/renderer_host/render_frame_host_impl.h
+++ b/content/browser/renderer_host/render_frame_host_impl.h
@@ -326,8 +326,9 @@
   RenderFrameHostImpl* GetMainFrame() override;
   std::vector<RenderFrameHost*> GetFramesInSubtree() override;
   bool IsDescendantOf(RenderFrameHost*) override;
-  void ForEachFrame(FrameIterationCallback on_frame) override;
-  void ForEachFrame(FrameIterationAlwaysContinueCallback on_frame) override;
+  void ForEachRenderFrameHost(FrameIterationCallback on_frame) override;
+  void ForEachRenderFrameHost(
+      FrameIterationAlwaysContinueCallback on_frame) override;
   int GetFrameTreeNodeId() override;
   base::UnguessableToken GetDevToolsFrameToken() override;
   base::Optional<base::UnguessableToken> GetEmbeddingToken() override;
@@ -1744,17 +1745,20 @@
   bool IsOuterDelegateFrame() { return is_outer_delegate_frame_; }
 
   // These are the content internal equivalents of
-  // |RenderFrameHost::ForEachFrame| whose comment can be referred to for
-  // details. Content internals can also access speculative RenderFrameHostImpls
-  // if necessary by using the |ForEachFrameIncludingSpeculative| variations.
+  // |RenderFrameHost::ForEachRenderFrameHost| whose comment can be referred to
+  // for details. Content internals can also access speculative
+  // RenderFrameHostImpls if necessary by using the
+  // |ForEachRenderFrameHostIncludingSpeculative| variations.
   using FrameIterationCallbackImpl =
       base::RepeatingCallback<FrameIterationAction(RenderFrameHostImpl*)>;
   using FrameIterationAlwaysContinueCallbackImpl =
       base::RepeatingCallback<void(RenderFrameHostImpl*)>;
-  void ForEachFrame(FrameIterationCallbackImpl on_frame);
-  void ForEachFrame(FrameIterationAlwaysContinueCallbackImpl on_frame);
-  void ForEachFrameIncludingSpeculative(FrameIterationCallbackImpl on_frame);
-  void ForEachFrameIncludingSpeculative(
+  void ForEachRenderFrameHost(FrameIterationCallbackImpl on_frame);
+  void ForEachRenderFrameHost(
+      FrameIterationAlwaysContinueCallbackImpl on_frame);
+  void ForEachRenderFrameHostIncludingSpeculative(
+      FrameIterationCallbackImpl on_frame);
+  void ForEachRenderFrameHostIncludingSpeculative(
       FrameIterationAlwaysContinueCallbackImpl on_frame);
 
   bool DocumentUsedWebOTP() override;
@@ -2677,9 +2681,9 @@
       const base::RepeatingCallback<void(RenderFrameHostImpl*)>& callback);
 
   // This is the actual implementation of the various overloads of
-  // |ForEachFrame|.
-  void ForEachFrameImpl(FrameIterationCallbackImpl on_frame,
-                        bool include_speculative);
+  // |ForEachRenderFrameHost|.
+  void ForEachRenderFrameHostImpl(FrameIterationCallbackImpl on_frame,
+                                  bool include_speculative);
 
   // Returns the mojom::Frame interface for this frame in the renderer process.
   // May be overridden by friend subclasses for e.g. tests which wish to
diff --git a/content/browser/renderer_host/render_frame_host_impl_browsertest.cc b/content/browser/renderer_host/render_frame_host_impl_browsertest.cc
index 33681aa..7104882 100644
--- a/content/browser/renderer_host/render_frame_host_impl_browsertest.cc
+++ b/content/browser/renderer_host/render_frame_host_impl_browsertest.cc
@@ -5330,9 +5330,9 @@
                    ->IsProcessShutdownDelayedForTesting());
 }
 
-// Tests that RenderFrameHost::ForEachFrame visits the correct frames in the
-// correct order.
-IN_PROC_BROWSER_TEST_F(RenderFrameHostImplBrowserTest, ForEachFrame) {
+// Tests that RenderFrameHost::ForEachRenderFrameHost visits the correct frames
+// in the correct order.
+IN_PROC_BROWSER_TEST_F(RenderFrameHostImplBrowserTest, ForEachRenderFrameHost) {
   GURL url_a(embedded_test_server()->GetURL(
       "a.com", "/cross_site_iframe_factory.html?a(b(c),d)"));
 
@@ -5344,17 +5344,18 @@
 
   // When starting iteration from the primary frame, we should see the frame
   // itself and its descendants in breadth first order.
-  EXPECT_THAT(CollectAllFrames(rfh_a),
+  EXPECT_THAT(CollectAllRenderFrameHosts(rfh_a),
               testing::ElementsAre(rfh_a, rfh_b, rfh_d, rfh_c));
 
   // When starting iteration from a subframe, only it and its descendants should
   // be seen.
-  EXPECT_THAT(CollectAllFrames(rfh_b), testing::ElementsAre(rfh_b, rfh_c));
+  EXPECT_THAT(CollectAllRenderFrameHosts(rfh_b),
+              testing::ElementsAre(rfh_b, rfh_c));
 
   // Test that iteration stops when requested.
   {
     std::vector<RenderFrameHostImpl*> visited_frames;
-    rfh_a->ForEachFrame(
+    rfh_a->ForEachRenderFrameHost(
         base::BindLambdaForTesting([&](RenderFrameHostImpl* rfh) {
           visited_frames.push_back(rfh);
           return RenderFrameHost::FrameIterationAction::kStop;
@@ -5363,7 +5364,7 @@
   }
   {
     std::vector<RenderFrameHostImpl*> visited_frames;
-    rfh_a->ForEachFrame(
+    rfh_a->ForEachRenderFrameHost(
         base::BindLambdaForTesting([&](RenderFrameHostImpl* rfh) {
           visited_frames.push_back(rfh);
           return RenderFrameHost::FrameIterationAction::kSkipChildren;
@@ -5376,7 +5377,7 @@
   // |rfh_c| and |rfh_d|.
   {
     std::vector<RenderFrameHostImpl*> visited_frames;
-    rfh_a->ForEachFrame(
+    rfh_a->ForEachRenderFrameHost(
         base::BindLambdaForTesting([&](RenderFrameHostImpl* rfh) {
           visited_frames.push_back(rfh);
           return rfh == rfh_b
@@ -5387,7 +5388,7 @@
   }
   {
     std::vector<RenderFrameHostImpl*> visited_frames;
-    rfh_a->ForEachFrame(
+    rfh_a->ForEachRenderFrameHost(
         base::BindLambdaForTesting([&](RenderFrameHostImpl* rfh) {
           visited_frames.push_back(rfh);
           return rfh == rfh_b
@@ -5398,10 +5399,10 @@
   }
 }
 
-// Tests that RenderFrameHost::ForEachFrame does not expose speculative RFHs,
-// unless content internal code requests them.
+// Tests that RenderFrameHost::ForEachRenderFrameHost does not expose
+// speculative RFHs, unless content internal code requests them.
 IN_PROC_BROWSER_TEST_F(RenderFrameHostImplBrowserTest,
-                       ForEachFrameSpeculative) {
+                       ForEachRenderFrameHostSpeculative) {
   IsolateAllSitesForTesting(base::CommandLine::ForCurrentProcess());
   GURL url_a(embedded_test_server()->GetURL("a.com", "/title1.html"));
   GURL url_b(embedded_test_server()->GetURL("b.com", "/title1.html"));
@@ -5419,29 +5420,31 @@
   ASSERT_TRUE(rfh_b);
   EXPECT_EQ(LifecycleStateImpl::kSpeculative, rfh_b->lifecycle_state());
 
-  // ForEachFrame does not expose the speculative RFH.
-  EXPECT_THAT(CollectAllFrames(rfh_a), testing::ElementsAre(rfh_a));
+  // ForEachRenderFrameHost does not expose the speculative RFH.
+  EXPECT_THAT(CollectAllRenderFrameHosts(rfh_a), testing::ElementsAre(rfh_a));
 
   // When we request the speculative RFH, we visit it.
-  EXPECT_THAT(CollectAllFramesIncludingSpeculative(rfh_a),
+  EXPECT_THAT(CollectAllRenderFrameHostsIncludingSpeculative(rfh_a),
               testing::UnorderedElementsAre(rfh_a, rfh_b));
 
-  // If ForEachFrame is called on a speculative RFH directly, do nothing.
-  rfh_b->ForEachFrame(base::BindRepeating([](RenderFrameHostImpl* rfh) {
-    ADD_FAILURE() << "Visited speculative RFH";
-    return RenderFrameHost::FrameIterationAction::kStop;
-  }));
+  // If ForEachRenderFrameHost is called on a speculative RFH directly, do
+  // nothing.
+  rfh_b->ForEachRenderFrameHost(
+      base::BindRepeating([](RenderFrameHostImpl* rfh) {
+        ADD_FAILURE() << "Visited speculative RFH";
+        return RenderFrameHost::FrameIterationAction::kStop;
+      }));
 
   // If we request speculative RFHs and directly call this on a speculative RFH,
   // just visit the given speculative RFH.
-  EXPECT_THAT(CollectAllFramesIncludingSpeculative(rfh_b),
+  EXPECT_THAT(CollectAllRenderFrameHostsIncludingSpeculative(rfh_b),
               testing::ElementsAre(rfh_b));
 }
 
-// Like ForEachFrameSpeculative, but for a speculative RFH for a subframe
-// navigation.
+// Like ForEachRenderFrameHostSpeculative, but for a speculative RFH for a
+// subframe navigation.
 IN_PROC_BROWSER_TEST_F(RenderFrameHostImplBrowserTest,
-                       ForEachFrameSpeculativeWithSubframes) {
+                       ForEachRenderFrameHostSpeculativeWithSubframes) {
   IsolateAllSitesForTesting(base::CommandLine::ForCurrentProcess());
   GURL url_a(embedded_test_server()->GetURL(
       "a.com", "/cross_site_iframe_factory.html?a(b(c))"));
@@ -5464,28 +5467,30 @@
   ASSERT_TRUE(rfh_d);
   EXPECT_EQ(LifecycleStateImpl::kSpeculative, rfh_d->lifecycle_state());
 
-  // ForEachFrame does not expose the speculative RFH.
-  EXPECT_THAT(CollectAllFrames(rfh_a),
+  // ForEachRenderFrameHost does not expose the speculative RFH.
+  EXPECT_THAT(CollectAllRenderFrameHosts(rfh_a),
               testing::ElementsAre(rfh_a, rfh_b, rfh_c));
 
   // When we request the speculative RFH, we visit it.
-  EXPECT_THAT(CollectAllFramesIncludingSpeculative(rfh_a),
+  EXPECT_THAT(CollectAllRenderFrameHostsIncludingSpeculative(rfh_a),
               testing::UnorderedElementsAre(rfh_a, rfh_b, rfh_d, rfh_c));
 
   // When beginning iteration from the current RFH of the navigating frame, we
   // also visit the speculative RFH.
-  EXPECT_THAT(CollectAllFramesIncludingSpeculative(rfh_b),
+  EXPECT_THAT(CollectAllRenderFrameHostsIncludingSpeculative(rfh_b),
               testing::UnorderedElementsAre(rfh_b, rfh_d, rfh_c));
 
-  // If ForEachFrame is called on a speculative RFH directly, do nothing.
-  rfh_d->ForEachFrame(base::BindRepeating([](RenderFrameHostImpl* rfh) {
-    ADD_FAILURE() << "Visited speculative RFH";
-    return RenderFrameHost::FrameIterationAction::kStop;
-  }));
+  // If ForEachRenderFrameHost is called on a speculative RFH directly, do
+  // nothing.
+  rfh_d->ForEachRenderFrameHost(
+      base::BindRepeating([](RenderFrameHostImpl* rfh) {
+        ADD_FAILURE() << "Visited speculative RFH";
+        return RenderFrameHost::FrameIterationAction::kStop;
+      }));
 
   // If we request speculative RFHs and directly call this on a speculative RFH,
   // just visit the given speculative RFH.
-  EXPECT_THAT(CollectAllFramesIncludingSpeculative(rfh_d),
+  EXPECT_THAT(CollectAllRenderFrameHostsIncludingSpeculative(rfh_d),
               testing::ElementsAre(rfh_d));
 
   // Test that iteration stops when requested.
@@ -5493,7 +5498,7 @@
     // We don't check the RFHs visited in the interest of not overtesting the
     // ordering of speculative RFHs.
     bool stopped = false;
-    rfh_a->ForEachFrameIncludingSpeculative(
+    rfh_a->ForEachRenderFrameHostIncludingSpeculative(
         base::BindLambdaForTesting([&](RenderFrameHostImpl* rfh) {
           EXPECT_FALSE(stopped);
           if (rfh->lifecycle_state() == LifecycleStateImpl::kSpeculative) {
@@ -5506,7 +5511,7 @@
 
   {
     bool stopped = false;
-    rfh_b->ForEachFrameIncludingSpeculative(
+    rfh_b->ForEachRenderFrameHostIncludingSpeculative(
         base::BindLambdaForTesting([&](RenderFrameHostImpl* rfh) {
           EXPECT_FALSE(stopped);
           if (rfh->lifecycle_state() == LifecycleStateImpl::kSpeculative) {
@@ -5521,7 +5526,7 @@
   // speculative RFH skips the children but still includes the speculative RFH.
   {
     std::vector<RenderFrameHostImpl*> visited_frames;
-    rfh_a->ForEachFrameIncludingSpeculative(
+    rfh_a->ForEachRenderFrameHostIncludingSpeculative(
         base::BindLambdaForTesting([&](RenderFrameHostImpl* rfh) {
           visited_frames.push_back(rfh);
           return (rfh == rfh_b)
@@ -5534,7 +5539,7 @@
 
   {
     std::vector<RenderFrameHostImpl*> visited_frames;
-    rfh_b->ForEachFrameIncludingSpeculative(
+    rfh_b->ForEachRenderFrameHostIncludingSpeculative(
         base::BindLambdaForTesting([&](RenderFrameHostImpl* rfh) {
           visited_frames.push_back(rfh);
           return (rfh == rfh_b)
@@ -5548,7 +5553,7 @@
   // here for completeness of testing.
   {
     std::vector<RenderFrameHostImpl*> visited_frames;
-    rfh_a->ForEachFrameIncludingSpeculative(
+    rfh_a->ForEachRenderFrameHostIncludingSpeculative(
         base::BindLambdaForTesting([&](RenderFrameHostImpl* rfh) {
           visited_frames.push_back(rfh);
           return (rfh->lifecycle_state() == LifecycleStateImpl::kSpeculative)
@@ -5561,7 +5566,7 @@
 
   {
     std::vector<RenderFrameHostImpl*> visited_frames;
-    rfh_b->ForEachFrameIncludingSpeculative(
+    rfh_b->ForEachRenderFrameHostIncludingSpeculative(
         base::BindLambdaForTesting([&](RenderFrameHostImpl* rfh) {
           visited_frames.push_back(rfh);
           return (rfh->lifecycle_state() == LifecycleStateImpl::kSpeculative)
@@ -5574,7 +5579,7 @@
 }
 
 IN_PROC_BROWSER_TEST_F(RenderFrameHostImplBrowserTest,
-                       ForEachFramePendingDeletion) {
+                       ForEachRenderFrameHostPendingDeletion) {
   GURL url_a(embedded_test_server()->GetURL(
       "a.com", "/cross_site_iframe_factory.html?a(b(c))"));
   GURL url_d(embedded_test_server()->GetURL("d.com", "/title1.html"));
@@ -5593,20 +5598,22 @@
   EXPECT_TRUE(NavigateToURL(shell(), url_d));
   RenderFrameHostImpl* rfh_d = root_frame_host();
 
-  // ForEachFrame on the primary RFH does not visit the pending delete RFHs.
-  EXPECT_THAT(CollectAllFrames(rfh_d), testing::ElementsAre(rfh_d));
-
-  // ForEachFrame on the pending delete RFHs only visits the pending delete
+  // ForEachRenderFrameHost on the primary RFH does not visit the pending delete
   // RFHs.
-  EXPECT_THAT(CollectAllFrames(rfh_a),
+  EXPECT_THAT(CollectAllRenderFrameHosts(rfh_d), testing::ElementsAre(rfh_d));
+
+  // ForEachRenderFrameHost on the pending delete RFHs only visits the pending
+  // delete RFHs.
+  EXPECT_THAT(CollectAllRenderFrameHosts(rfh_a),
               testing::ElementsAre(rfh_a, rfh_b, rfh_c));
-  EXPECT_THAT(CollectAllFrames(rfh_b), testing::ElementsAre(rfh_b, rfh_c));
+  EXPECT_THAT(CollectAllRenderFrameHosts(rfh_b),
+              testing::ElementsAre(rfh_b, rfh_c));
 }
 
-// Tests that RenderFrameHost::ForEachFrame visits the frames of an inner
-// WebContents.
+// Tests that RenderFrameHost::ForEachRenderFrameHost visits the frames of an
+// inner WebContents.
 IN_PROC_BROWSER_TEST_F(RenderFrameHostImplBrowserTest,
-                       ForEachFrameInnerContents) {
+                       ForEachRenderFrameHostInnerContents) {
   GURL url_a(embedded_test_server()->GetURL("a.com", "/page_with_iframe.html"));
   GURL url_b(embedded_test_server()->GetURL("b.com", "/title1.html"));
 
@@ -5618,11 +5625,12 @@
 
   RenderFrameHostImpl* rfh_b = inner_contents->GetMainFrame();
 
-  EXPECT_THAT(CollectAllFrames(rfh_a), testing::ElementsAre(rfh_a, rfh_b));
+  EXPECT_THAT(CollectAllRenderFrameHosts(rfh_a),
+              testing::ElementsAre(rfh_a, rfh_b));
 }
 
 IN_PROC_BROWSER_TEST_F(RenderFrameHostImplBrowserTest,
-                       ForEachFrameInnerContentsWithSubframes) {
+                       ForEachRenderFrameHostInnerContentsWithSubframes) {
   GURL url_a(embedded_test_server()->GetURL(
       "a.com", "/cross_site_iframe_factory.html?a(a(a),a)"));
   GURL url_b(embedded_test_server()->GetURL(
@@ -5644,13 +5652,13 @@
   RenderFrameHostImpl* rfh_d = rfh_c->child_at(0)->current_frame_host();
   RenderFrameHostImpl* rfh_e = rfh_b->child_at(1)->current_frame_host();
 
-  EXPECT_THAT(CollectAllFrames(rfh_a_main),
+  EXPECT_THAT(CollectAllRenderFrameHosts(rfh_a_main),
               testing::ElementsAre(rfh_a_main, rfh_a_sub1, rfh_a_sub2, rfh_b,
                                    rfh_c, rfh_e, rfh_d));
 }
 
 IN_PROC_BROWSER_TEST_F(RenderFrameHostImplBrowserTest,
-                       ForEachFrameMultipleInnerContents) {
+                       ForEachRenderFrameHostMultipleInnerContents) {
   // After attaching inner contents, this will be A(B(C),D)
   GURL url_a(embedded_test_server()->GetURL(
       "a.com", "/cross_site_iframe_factory.html?a(a,a)"));
@@ -5677,7 +5685,7 @@
   ASSERT_TRUE(NavigateToURLFromRenderer(contents_d, url_d));
   RenderFrameHostImpl* rfh_d = contents_d->GetMainFrame();
 
-  EXPECT_THAT(CollectAllFrames(rfh_a),
+  EXPECT_THAT(CollectAllRenderFrameHosts(rfh_a),
               testing::ElementsAre(rfh_a, rfh_b, rfh_d, rfh_c));
 }
 
diff --git a/content/public/browser/browser_context.h b/content/public/browser/browser_context.h
index f0edf20..6d00b535 100644
--- a/content/public/browser/browser_context.h
+++ b/content/public/browser/browser_context.h
@@ -101,7 +101,7 @@
   // non-virtual instance methods.
   //
   // TODO(https://crbug.com/1179776): Finish converting the methods in this
-  // section into non-virtual instance methods.  (The old, abandoned  practice
+  // section into non-virtual instance methods.  (The old, abandoned practice
   // was to make the methods in this section `static` and have them take
   // `BrowserContext* self` as the first parameter.)
   //
@@ -190,8 +190,7 @@
 
   // Delivers a push message with |data| to the Service Worker identified by
   // |origin| and |service_worker_registration_id|.
-  static void DeliverPushMessage(
-      BrowserContext* self,
+  void DeliverPushMessage(
       const GURL& origin,
       int64_t service_worker_registration_id,
       const std::string& message_id,
@@ -201,8 +200,7 @@
   // Fires a push subscription change event to the Service Worker identified by
   // |origin| and |service_worker_registration_id| with |new_subscription| and
   // |old_subscription| as event information.
-  static void FirePushSubscriptionChangeEvent(
-      BrowserContext* self,
+  void FirePushSubscriptionChangeEvent(
       const GURL& origin,
       int64_t service_worker_registration_id,
       blink::mojom::PushSubscriptionPtr new_subscription,
diff --git a/content/public/browser/browser_plugin_guest_manager.h b/content/public/browser/browser_plugin_guest_manager.h
index 26c9f37..53820ce 100644
--- a/content/public/browser/browser_plugin_guest_manager.h
+++ b/content/public/browser/browser_plugin_guest_manager.h
@@ -24,7 +24,7 @@
       WebContents* owner_web_contents,
       base::RepeatingCallback<void(WebContents*)> callback) {}
 
-  // Prefer using |RenderFrameHost::ForEachFrame|.
+  // Prefer using |RenderFrameHost::ForEachRenderFrameHost|.
   // Iterates over all WebContents belonging to a given |owner_web_contents|,
   // calling |callback| for each. If one of the callbacks returns true, then
   // the iteration exits early.
diff --git a/content/public/browser/render_frame_host.h b/content/public/browser/render_frame_host.h
index f387432a..2a682b0d 100644
--- a/content/public/browser/render_frame_host.h
+++ b/content/public/browser/render_frame_host.h
@@ -256,13 +256,13 @@
   // This does not consider inner frame trees.
   virtual bool IsDescendantOf(RenderFrameHost* ancestor) = 0;
 
-  // |ForEachFrame| traverses this RenderFrameHost and all of its descendants,
-  // including frames in any inner frame trees, in breadth-first order.
-  // Examples of features that have inner frame trees are portals or GuestViews.
-  // Note: The RenderFrameHost parameter is not guaranteed to have a live
-  // RenderFrame counterpart in the renderer process. Callbacks should check
-  // IsRenderFrameLive(), as sending IPC messages to it in this case will fail
-  // silently.
+  // |ForEachRenderFrameHost| traverses this RenderFrameHost and all of its
+  // descendants, including frames in any inner frame trees, in breadth-first
+  // order. Examples of features that have inner frame trees are portals or
+  // GuestViews. Note: The RenderFrameHost parameter is not guaranteed to have a
+  // live RenderFrame counterpart in the renderer process. Callbacks should
+  // check IsRenderFrameLive(), as sending IPC messages to it in this case will
+  // fail silently.
   //
   // The callback returns a FrameIterationAction which determines if/how
   // iteration on subsequent frames continues. The FrameIterationAction may be
@@ -281,8 +281,9 @@
       base::RepeatingCallback<FrameIterationAction(RenderFrameHost*)>;
   using FrameIterationAlwaysContinueCallback =
       base::RepeatingCallback<void(RenderFrameHost*)>;
-  virtual void ForEachFrame(FrameIterationCallback on_frame) = 0;
-  virtual void ForEachFrame(FrameIterationAlwaysContinueCallback on_frame) = 0;
+  virtual void ForEachRenderFrameHost(FrameIterationCallback on_frame) = 0;
+  virtual void ForEachRenderFrameHost(
+      FrameIterationAlwaysContinueCallback on_frame) = 0;
 
   // Returns the FrameTreeNode ID for this frame. This ID is browser-global and
   // uniquely identifies a frame that hosts content. The identifier is fixed at
diff --git a/content/public/common/content_features.cc b/content/public/common/content_features.cc
index 3f3a2c6..1629868 100644
--- a/content/public/common/content_features.cc
+++ b/content/public/common/content_features.cc
@@ -1008,7 +1008,7 @@
     "RetryGetVideoCaptureDeviceInfos", base::FEATURE_DISABLED_BY_DEFAULT};
 
 const base::Feature kDesktopCaptureMacV2{"DesktopCaptureMacV2",
-                                         base::FEATURE_ENABLED_BY_DEFAULT};
+                                         base::FEATURE_DISABLED_BY_DEFAULT};
 
 const base::Feature kWindowCaptureMacV2{"WindowCaptureMacV2",
                                         base::FEATURE_ENABLED_BY_DEFAULT};
diff --git a/content/public/test/browser_test_utils.cc b/content/public/test/browser_test_utils.cc
index 871303cd..7086213 100644
--- a/content/public/test/browser_test_utils.cc
+++ b/content/public/test/browser_test_utils.cc
@@ -1815,9 +1815,10 @@
   return rfh->frame_tree_node()->child_at(index)->current_frame_host();
 }
 
-std::vector<RenderFrameHost*> CollectAllFrames(RenderFrameHost* starting_rfh) {
+std::vector<RenderFrameHost*> CollectAllRenderFrameHosts(
+    RenderFrameHost* starting_rfh) {
   std::vector<RenderFrameHost*> visited_frames;
-  starting_rfh->ForEachFrame(base::BindLambdaForTesting(
+  starting_rfh->ForEachRenderFrameHost(base::BindLambdaForTesting(
       [&](RenderFrameHost* rfh) { visited_frames.push_back(rfh); }));
   return visited_frames;
 }
diff --git a/content/public/test/browser_test_utils.h b/content/public/test/browser_test_utils.h
index f911e8f..8e4ecb6 100644
--- a/content/public/test/browser_test_utils.h
+++ b/content/public/test/browser_test_utils.h
@@ -872,9 +872,10 @@
 // RenderFrameHost.  Returns nullptr if such child frame does not exist.
 RenderFrameHost* ChildFrameAt(RenderFrameHost* frame, size_t index);
 
-// Returns the frames visited by |RenderFrameHost::ForEachFrame| in the same
-// order.
-std::vector<RenderFrameHost*> CollectAllFrames(RenderFrameHost* starting_rfh);
+// Returns the frames visited by |RenderFrameHost::ForEachRenderFrameHost| in
+// the same order.
+std::vector<RenderFrameHost*> CollectAllRenderFrameHosts(
+    RenderFrameHost* starting_rfh);
 
 // Executes the WebUI resource test runner injecting each resource ID in
 // |js_resource_ids| prior to executing the tests.
diff --git a/content/test/content_browser_test_utils_internal.cc b/content/test/content_browser_test_utils_internal.cc
index 857a329..4101312 100644
--- a/content/test/content_browser_test_utils_internal.cc
+++ b/content/test/content_browser_test_utils_internal.cc
@@ -159,19 +159,21 @@
   return root->child_at(root->child_count() - 1)->current_frame_host();
 }
 
-std::vector<RenderFrameHostImpl*> CollectAllFrames(
+std::vector<RenderFrameHostImpl*> CollectAllRenderFrameHosts(
     RenderFrameHostImpl* starting_rfh) {
   std::vector<RenderFrameHostImpl*> visited_frames;
-  starting_rfh->ForEachFrame(base::BindLambdaForTesting(
+  starting_rfh->ForEachRenderFrameHost(base::BindLambdaForTesting(
       [&](RenderFrameHostImpl* rfh) { visited_frames.push_back(rfh); }));
   return visited_frames;
 }
 
-std::vector<RenderFrameHostImpl*> CollectAllFramesIncludingSpeculative(
+std::vector<RenderFrameHostImpl*>
+CollectAllRenderFrameHostsIncludingSpeculative(
     RenderFrameHostImpl* starting_rfh) {
   std::vector<RenderFrameHostImpl*> visited_frames;
-  starting_rfh->ForEachFrameIncludingSpeculative(base::BindLambdaForTesting(
-      [&](RenderFrameHostImpl* rfh) { visited_frames.push_back(rfh); }));
+  starting_rfh->ForEachRenderFrameHostIncludingSpeculative(
+      base::BindLambdaForTesting(
+          [&](RenderFrameHostImpl* rfh) { visited_frames.push_back(rfh); }));
   return visited_frames;
 }
 
diff --git a/content/test/content_browser_test_utils_internal.h b/content/test/content_browser_test_utils_internal.h
index ae21989..53bb446 100644
--- a/content/test/content_browser_test_utils_internal.h
+++ b/content/test/content_browser_test_utils_internal.h
@@ -83,11 +83,12 @@
                                 const GURL& url,
                                 bool wait_for_navigation);
 
-// Returns the frames visited by |RenderFrameHostImpl::ForEachFrame| in the same
-// order.
-std::vector<RenderFrameHostImpl*> CollectAllFrames(
+// Returns the frames visited by |RenderFrameHostImpl::ForEachRenderFrameHost|
+// in the same order.
+std::vector<RenderFrameHostImpl*> CollectAllRenderFrameHosts(
     RenderFrameHostImpl* starting_rfh);
-std::vector<RenderFrameHostImpl*> CollectAllFramesIncludingSpeculative(
+std::vector<RenderFrameHostImpl*>
+CollectAllRenderFrameHostsIncludingSpeculative(
     RenderFrameHostImpl* starting_rfh);
 
 // Open a new popup passing no URL to window.open, which results in a blank page
diff --git a/content/test/data/accessibility/html/button-name-calc.html b/content/test/data/accessibility/html/button-name-calc.html
index 39cd40f..6dc8b71 100644
--- a/content/test/data/accessibility/html/button-name-calc.html
+++ b/content/test/data/accessibility/html/button-name-calc.html
@@ -2,6 +2,7 @@
 @BLINK-ALLOW:description*
 @WIN-ALLOW:description*
 @WIN-ALLOW:explicit-name*
+@UIA-WIN-ALLOW:ClassName*
 @UIA-WIN-ALLOW:FullDescription*
 @AURALINUX-ALLOW:description*
 @AURALINUX-ALLOW:explicit-name*
@@ -22,7 +23,7 @@
   <button>Outer <div>inner</div></button>
   <button>Outer <div aria-label="inner1">inner2</div></button>
   <button>Outer <section><div>grandchild</div></section></button>
-  <p aria-label="@NO_DUMP">
+  <p class="foo @NO_DUMP bar">
     <span id="lb3">LabelledBy3</span>
     <span id="lb4">LabelledBy4</span>
     <span id="db4">DescribedBy4</span>
diff --git a/content/test/data/accessibility/html/checkbox-name-calc.html b/content/test/data/accessibility/html/checkbox-name-calc.html
index 77b6cc5..dd9c427 100644
--- a/content/test/data/accessibility/html/checkbox-name-calc.html
+++ b/content/test/data/accessibility/html/checkbox-name-calc.html
@@ -20,7 +20,7 @@
          aria-labelledby="lb4" aria-describedby="db4">
   <input id="c5" type="checkbox" aria-describedby="db5">
 
-  <p aria-label="@NO_DUMP">
+  <p title="@NO_DUMP please">
     <label for="c1">Label1</label>
     <label for="c2">Label2</label>
     <label for="c3">Label3</label>
diff --git a/content/test/data/accessibility/html/reparent-crash-expected-blink.txt b/content/test/data/accessibility/html/reparent-crash-expected-blink.txt
index c0fd23a..cd424930 100644
--- a/content/test/data/accessibility/html/reparent-crash-expected-blink.txt
+++ b/content/test/data/accessibility/html/reparent-crash-expected-blink.txt
@@ -1,7 +1,7 @@
 rootWebArea
 ++genericContainer ignored
 ++++genericContainer ignored
-++++++listItem name='@NO_CHILDREN_DUMP'
+++++++listItem description='@NO_CHILDREN_DUMP' descriptionFrom=ariaDescription
 ++++++genericContainer
 ++++++++staticText name='Done'
 ++++++++++inlineTextBox name='Done'
diff --git a/content/test/data/accessibility/html/reparent-crash.html b/content/test/data/accessibility/html/reparent-crash.html
index 4e25a7611..b0b1069 100644
--- a/content/test/data/accessibility/html/reparent-crash.html
+++ b/content/test/data/accessibility/html/reparent-crash.html
@@ -9,7 +9,7 @@
 -->
 
 <img id="cl"></img>
-<li aria-label="@NO_CHILDREN_DUMP">
+<li aria-description="@NO_CHILDREN_DUMP">
   <div id="aw"><pre id="d3"><object id="b" aria-hidden=true></object></pre></div>
 </li>
 <object id="z"><div id="b5"></div> <option></option></object>
diff --git a/content/test/data/accessibility/readme.md b/content/test/data/accessibility/readme.md
index bfcd768..d200be1 100644
--- a/content/test/data/accessibility/readme.md
+++ b/content/test/data/accessibility/readme.md
@@ -240,12 +240,17 @@
 
 #### @NO_DUMP and @NO_CHILDREN_DUMP
 
-To skip dumping a particular element, make its accessible name equal to
-`@NO_DUMP`, for example `<div aria-label="@NO_DUMP"></div>`.
+To skip dumping a particular element, add `@NO_DUMP` to a property that will
+be exposed as an ax::mojom::StringAttribute, for example
+`<div class="@NO_DUMP"></div>`.
 
-To skip dumping all children of a particular element, make its accessible
-name equal to `@NO_CHILDREN_DUMP`, for example
-`<div aria-label="@NO_CHILDREN_DUMP"></div>`.
+To skip dumping all children of a particular element, add `@NO_CHILDREN_DUMP`
+to a property that will be exposed as an ax::mojom::StringAttribute, for example
+`<div class="@NO_CHILDREN_DUMP"></div>`.
+
+Note that setting the `aria-label` value to `@NO_DUMP` or `@NO_CHILDREN_DUMP`
+is not guaranteed to work due to certain roles no longer supporting author-
+provided naming in ARIA 1.2.
 
 To load an iframe from a different site, forcing it into a different process,
 use `/cross-site/HOSTNAME/` in the url, for example:
diff --git a/content/test/gpu/gpu_tests/test_expectations/webcodecs_expectations.txt b/content/test/gpu/gpu_tests/test_expectations/webcodecs_expectations.txt
index 587f320..dcc7a09 100644
--- a/content/test/gpu/gpu_tests/test_expectations/webcodecs_expectations.txt
+++ b/content/test/gpu/gpu_tests/test_expectations/webcodecs_expectations.txt
@@ -69,3 +69,5 @@
 crbug.com/1204831 [ chromeos-board-kevin ] WebCodecs_Encode_capture_vp8_require [ Failure ]
 crbug.com/1204831 [ chromeos-board-kevin ] WebCodecs_Encode_capture_avc1.42001E_require [ Failure ]
 
+crbug.com/1207682 [ android ] WebCodecs_* [ RetryOnFailure ]
+
diff --git a/gpu/command_buffer/service/image_reader_gl_owner.cc b/gpu/command_buffer/service/image_reader_gl_owner.cc
index 7b88961..f74ce74 100644
--- a/gpu/command_buffer/service/image_reader_gl_owner.cc
+++ b/gpu/command_buffer/service/image_reader_gl_owner.cc
@@ -320,9 +320,9 @@
   current_image_ref_.emplace(this, image, std::move(scoped_acquire_fence_fd));
 }
 
-void ImageReaderGLOwner::EnsureTexImageBound() {
+void ImageReaderGLOwner::EnsureTexImageBound(GLuint service_id) {
   if (current_image_ref_)
-    current_image_ref_->EnsureBound();
+    current_image_ref_->EnsureBound(service_id);
 }
 
 std::unique_ptr<base::android::ScopedHardwareBufferFenceSync>
@@ -508,17 +508,19 @@
   return base::ScopedFD(HANDLE_EINTR(dup(ready_fence_.get())));
 }
 
-void ImageReaderGLOwner::ScopedCurrentImageRef::EnsureBound() {
-  if (image_bound_)
-    return;
-
-  // Insert an EGL fence and make server wait for image to be available.
+void ImageReaderGLOwner::ScopedCurrentImageRef::EnsureBound(GLuint service_id) {
+  // Same |image_| can be bound multiple times to different |service_id|. So
+  // even if |image_bound_| is true, it might not be for current |service_id|.
+  // Hence we still need to create and bind egl image to the |service_id|.
+  // Also continue to wait on the fence even if it was waited upon during
+  // previous EnsureBound() calls on same image since this call could be on a
+  // different context. Insert an EGL fence and make server wait for image to be
+  // available.
   if (!InsertEglFenceAndWait(GetReadyFence()))
     return;
 
   // Create EGL image from the AImage and bind it to the texture.
-  if (!CreateAndBindEglImage(image_, texture_owner_->GetTextureId(),
-                             &texture_owner_->loader_))
+  if (!CreateAndBindEglImage(image_, service_id, &texture_owner_->loader_))
     return;
 
   image_bound_ = true;
diff --git a/gpu/command_buffer/service/image_reader_gl_owner.h b/gpu/command_buffer/service/image_reader_gl_owner.h
index 4e3403b2..c9534cc 100644
--- a/gpu/command_buffer/service/image_reader_gl_owner.h
+++ b/gpu/command_buffer/service/image_reader_gl_owner.h
@@ -37,7 +37,7 @@
       const base::RepeatingClosure& frame_available_cb) override;
   gl::ScopedJavaSurface CreateJavaSurface() const override;
   void UpdateTexImage() override;
-  void EnsureTexImageBound() override;
+  void EnsureTexImageBound(GLuint service_id) override;
   void ReleaseBackBuffers() override;
   std::unique_ptr<base::android::ScopedHardwareBufferFenceSync>
   GetAHardwareBuffer() override;
@@ -66,7 +66,7 @@
     ~ScopedCurrentImageRef();
     AImage* image() const { return image_; }
     base::ScopedFD GetReadyFence() const;
-    void EnsureBound();
+    void EnsureBound(GLuint service_id);
 
    private:
     ImageReaderGLOwner* texture_owner_;
diff --git a/gpu/command_buffer/service/mock_texture_owner.cc b/gpu/command_buffer/service/mock_texture_owner.cc
index 8993b1a..88c85532 100644
--- a/gpu/command_buffer/service/mock_texture_owner.cc
+++ b/gpu/command_buffer/service/mock_texture_owner.cc
@@ -24,7 +24,7 @@
   ON_CALL(*this, GetTextureId()).WillByDefault(Return(fake_texture_id));
   ON_CALL(*this, GetContext()).WillByDefault(Return(fake_context));
   ON_CALL(*this, GetSurface()).WillByDefault(Return(fake_surface));
-  ON_CALL(*this, EnsureTexImageBound()).WillByDefault(Invoke([this] {
+  ON_CALL(*this, EnsureTexImageBound(_)).WillByDefault(Invoke([this] {
     CHECK(expect_update_tex_image);
   }));
   ON_CALL(*this, RunWhenBufferIsAvailable(_))
diff --git a/gpu/command_buffer/service/mock_texture_owner.h b/gpu/command_buffer/service/mock_texture_owner.h
index c5c39b00..49c0023 100644
--- a/gpu/command_buffer/service/mock_texture_owner.h
+++ b/gpu/command_buffer/service/mock_texture_owner.h
@@ -32,7 +32,7 @@
   MOCK_CONST_METHOD0(GetSurface, gl::GLSurface*());
   MOCK_CONST_METHOD0(CreateJavaSurface, gl::ScopedJavaSurface());
   MOCK_METHOD0(UpdateTexImage, void());
-  MOCK_METHOD0(EnsureTexImageBound, void());
+  MOCK_METHOD1(EnsureTexImageBound, void(GLuint));
   MOCK_METHOD0(ReleaseBackBuffers, void());
   MOCK_METHOD0(ReleaseResources, void());
   MOCK_METHOD1(SetFrameAvailableCallback, void(const base::RepeatingClosure&));
diff --git a/gpu/command_buffer/service/raster_decoder.cc b/gpu/command_buffer/service/raster_decoder.cc
index a4be3ab..b21a02c 100644
--- a/gpu/command_buffer/service/raster_decoder.cc
+++ b/gpu/command_buffer/service/raster_decoder.cc
@@ -82,7 +82,11 @@
 #if BUILDFLAG(ENABLE_VULKAN)
 #include "components/viz/common/gpu/vulkan_context_provider.h"
 #include "gpu/vulkan/vulkan_device_queue.h"
-#endif
+#endif  // BUILDFLAG(ENABLE_VULKAN)
+
+#if defined(OS_WIN)
+#include "gpu/command_buffer/service/shared_image_backing_factory_d3d.h"
+#endif  // OS_WIN
 
 // Local versions of the SET_GL_ERROR macros
 #define LOCAL_SET_GL_ERROR(error, function_name, msg) \
@@ -1162,6 +1166,10 @@
     caps.texture_half_float_linear =
         feature_info()->feature_flags().enable_texture_half_float_linear;
   }
+#if defined(OS_WIN)
+  caps.shared_image_swap_chain =
+      SharedImageBackingFactoryD3D::IsSwapChainSupported();
+#endif  // OS_WIN
   return caps;
 }
 
diff --git a/gpu/command_buffer/service/shared_image_video.cc b/gpu/command_buffer/service/shared_image_video.cc
index da8ba72..6c12b26 100644
--- a/gpu/command_buffer/service/shared_image_video.cc
+++ b/gpu/command_buffer/service/shared_image_video.cc
@@ -13,6 +13,7 @@
 #include "components/viz/common/resources/resource_sizes.h"
 #include "gpu/command_buffer/common/shared_image_usage.h"
 #include "gpu/command_buffer/service/abstract_texture.h"
+#include "gpu/command_buffer/service/abstract_texture_impl.h"
 #include "gpu/command_buffer/service/ahardwarebuffer_utils.h"
 #include "gpu/command_buffer/service/mailbox_manager.h"
 #include "gpu/command_buffer/service/memory_tracking.h"
@@ -148,14 +149,20 @@
 class SharedImageRepresentationGLTextureVideo
     : public SharedImageRepresentationGLTexture {
  public:
-  SharedImageRepresentationGLTextureVideo(SharedImageManager* manager,
-                                          SharedImageVideo* backing,
-                                          MemoryTypeTracker* tracker,
-                                          gles2::Texture* texture)
+  SharedImageRepresentationGLTextureVideo(
+      SharedImageManager* manager,
+      SharedImageVideo* backing,
+      MemoryTypeTracker* tracker,
+      std::unique_ptr<gles2::AbstractTexture> texture)
       : SharedImageRepresentationGLTexture(manager, backing, tracker),
-        texture_(texture) {}
+        texture_(std::move(texture)) {}
 
-  gles2::Texture* GetTexture() override { return texture_; }
+  gles2::Texture* GetTexture() override {
+    auto* texture = gles2::Texture::CheckedCast(texture_->GetTextureBase());
+    DCHECK(texture);
+
+    return texture;
+  }
 
   bool BeginAccess(GLenum mode) override {
     // This representation should only be called for read or overlay.
@@ -163,14 +170,14 @@
            mode == GL_SHARED_IMAGE_ACCESS_MODE_OVERLAY_CHROMIUM);
 
     auto* video_backing = static_cast<SharedImageVideo*>(backing());
-    video_backing->BeginGLReadAccess();
+    video_backing->BeginGLReadAccess(texture_->service_id());
     return true;
   }
 
   void EndAccess() override {}
 
  private:
-  gles2::Texture* texture_;
+  std::unique_ptr<gles2::AbstractTexture> texture_;
 
   DISALLOW_COPY_AND_ASSIGN(SharedImageRepresentationGLTextureVideo);
 };
@@ -183,18 +190,20 @@
       SharedImageManager* manager,
       SharedImageVideo* backing,
       MemoryTypeTracker* tracker,
-      scoped_refptr<gles2::TexturePassthrough> texture)
+      std::unique_ptr<gles2::AbstractTexture> abstract_texture)
       : SharedImageRepresentationGLTexturePassthrough(manager,
                                                       backing,
                                                       tracker),
-        texture_(std::move(texture)) {
+        abstract_texture_(std::move(abstract_texture)),
+        passthrough_texture_(gles2::TexturePassthrough::CheckedCast(
+            abstract_texture_->GetTextureBase())) {
     // TODO(https://crbug.com/1172769): Remove this CHECK.
-    CHECK(texture_);
+    CHECK(passthrough_texture_);
   }
 
   const scoped_refptr<gles2::TexturePassthrough>& GetTexturePassthrough()
       override {
-    return texture_;
+    return passthrough_texture_;
   }
 
   bool BeginAccess(GLenum mode) override {
@@ -203,14 +212,15 @@
            mode == GL_SHARED_IMAGE_ACCESS_MODE_OVERLAY_CHROMIUM);
 
     auto* video_backing = static_cast<SharedImageVideo*>(backing());
-    video_backing->BeginGLReadAccess();
+    video_backing->BeginGLReadAccess(passthrough_texture_->service_id());
     return true;
   }
 
   void EndAccess() override {}
 
  private:
-  scoped_refptr<gles2::TexturePassthrough> texture_;
+  std::unique_ptr<gles2::AbstractTexture> abstract_texture_;
+  scoped_refptr<gles2::TexturePassthrough> passthrough_texture_;
 
   DISALLOW_COPY_AND_ASSIGN(SharedImageRepresentationGLTexturePassthroughVideo);
 };
@@ -325,15 +335,14 @@
   // which should result in no image.
   if (!stream_texture_sii_->HasTextureOwner())
     return nullptr;
-  // TODO(vikassoni): We would want to give the TextureOwner's underlying
-  // Texture, but it was not set with the correct size. The AbstractTexture,
-  // that we use for legacy mailbox, is correctly set.
-  auto* texture =
-      gles2::Texture::CheckedCast(abstract_texture_->GetTextureBase());
-  DCHECK(texture);
+
+  // Generate an abstract texture.
+  auto texture = GenAbstractTexture(context_state_, /*passthrough=*/false);
+  if (!texture)
+    return nullptr;
 
   return std::make_unique<SharedImageRepresentationGLTextureVideo>(
-      manager, this, tracker, texture);
+      manager, this, tracker, std::move(texture));
 }
 
 // TODO(vikassoni): Currently GLRenderer doesn't support overlays with shared
@@ -347,13 +356,11 @@
   // which should result in no image.
   if (!stream_texture_sii_->HasTextureOwner())
     return nullptr;
-  // TODO(vikassoni): We would want to give the TextureOwner's underlying
-  // Texture, but it was not set with the correct size. The AbstractTexture,
-  // that we use for legacy mailbox, is correctly set.
-  scoped_refptr<gles2::TexturePassthrough> texture =
-      gles2::TexturePassthrough::CheckedCast(
-          abstract_texture_->GetTextureBase());
-  DCHECK(texture);
+
+  // Generate an abstract texture.
+  auto texture = GenAbstractTexture(context_state_, /*passthrough=*/true);
+  if (!texture)
+    return nullptr;
 
   return std::make_unique<SharedImageRepresentationGLTexturePassthroughVideo>(
       manager, this, tracker, std::move(texture));
@@ -378,32 +385,60 @@
   }
 
   DCHECK(context_state->GrContextIsGL());
-  auto* texture_base = stream_texture_sii_->GetTextureBase();
-  DCHECK(texture_base);
+  const bool passthrough = Passthrough();
+  auto texture = GenAbstractTexture(context_state, passthrough);
+  if (!texture)
+    return nullptr;
 
-  // In GL mode, create the SharedImageRepresentationGLTexture*Video
-  // representation to use with SharedImageRepresentationVideoSkiaGL.
   std::unique_ptr<gpu::SharedImageRepresentationGLTextureBase>
       gl_representation;
-  if (texture_base->GetType() == gpu::TextureBase::Type::kValidated) {
-    gl_representation =
-        std::make_unique<SharedImageRepresentationGLTextureVideo>(
-            manager, this, tracker, gles2::Texture::CheckedCast(texture_base));
-  } else {
+  if (passthrough) {
     gl_representation =
         std::make_unique<SharedImageRepresentationGLTexturePassthroughVideo>(
-            manager, this, tracker,
-            gles2::TexturePassthrough::CheckedCast(texture_base));
+            manager, this, tracker, std::move(texture));
+  } else {
+    gl_representation =
+        std::make_unique<SharedImageRepresentationGLTextureVideo>(
+            manager, this, tracker, std::move(texture));
   }
-
   return SharedImageRepresentationSkiaGL::Create(std::move(gl_representation),
                                                  std::move(context_state),
                                                  manager, this, tracker);
 }
 
-void SharedImageVideo::BeginGLReadAccess() {
-  // Render the codec image.
-  stream_texture_sii_->UpdateAndBindTexImage();
+bool SharedImageVideo::Passthrough() {
+  auto* texture_base = stream_texture_sii_->GetTextureBase();
+  DCHECK(texture_base);
+
+  return (texture_base->GetType() == gpu::TextureBase::Type::kPassthrough);
+}
+
+std::unique_ptr<gles2::AbstractTexture> SharedImageVideo::GenAbstractTexture(
+    scoped_refptr<SharedContextState> context_state,
+    const bool passthrough) {
+  std::unique_ptr<gles2::AbstractTexture> texture;
+  if (passthrough) {
+    texture = std::make_unique<gles2::AbstractTextureImplPassthrough>(
+        GL_TEXTURE_EXTERNAL_OES);
+  } else {
+    texture = std::make_unique<gles2::AbstractTextureImpl>(
+        GL_TEXTURE_EXTERNAL_OES, GL_RGBA, size().width(), size().height(), 1, 0,
+        GL_RGBA, GL_UNSIGNED_BYTE);
+  }
+
+  // If TextureOwner binds texture implicitly on update, that means it will use
+  // TextureOwner texture_id to update and bind. Hence use TextureOwner
+  // texture_id in abstract texture via BindStreamTextureImage().
+  if (stream_texture_sii_->TextureOwnerBindsTextureOnUpdate()) {
+    texture->BindStreamTextureImage(
+        stream_texture_sii_.get(),
+        stream_texture_sii_->GetTextureBase()->service_id());
+  }
+  return texture;
+}
+
+void SharedImageVideo::BeginGLReadAccess(const GLuint service_id) {
+  stream_texture_sii_->UpdateAndBindTexImage(service_id);
 }
 
 // Representation of SharedImageVideo as an overlay plane.
diff --git a/gpu/command_buffer/service/shared_image_video.h b/gpu/command_buffer/service/shared_image_video.h
index 230fdfb..a30bcc8 100644
--- a/gpu/command_buffer/service/shared_image_video.h
+++ b/gpu/command_buffer/service/shared_image_video.h
@@ -89,7 +89,16 @@
   friend class SharedImageRepresentationVideoSkiaVk;
   friend class SharedImageRepresentationOverlayVideo;
 
-  void BeginGLReadAccess();
+  // Whether we're using the passthrough command decoder and should generate
+  // passthrough textures.
+  bool Passthrough();
+
+  // Helper method to generate an abstract texture.
+  std::unique_ptr<gles2::AbstractTexture> GenAbstractTexture(
+      scoped_refptr<SharedContextState> context_state,
+      const bool passthrough);
+
+  void BeginGLReadAccess(const GLuint service_id);
 
   scoped_refptr<StreamTextureSharedImageInterface> stream_texture_sii_;
 
diff --git a/gpu/command_buffer/service/stream_texture_shared_image_interface.h b/gpu/command_buffer/service/stream_texture_shared_image_interface.h
index ffdd3af..668843a6 100644
--- a/gpu/command_buffer/service/stream_texture_shared_image_interface.h
+++ b/gpu/command_buffer/service/stream_texture_shared_image_interface.h
@@ -18,7 +18,7 @@
 class GPU_GLES2_EXPORT StreamTextureSharedImageInterface : public gl::GLImage {
  public:
   enum class BindingsMode {
-    // Ensures that the TextureOwner's texture is bound to the latest image, if
+    // Ensures that the texture is bound to the latest image, if
     // it requires explicit binding.
     kEnsureTexImageBound,
 
@@ -39,9 +39,14 @@
   // or not.
   virtual bool IsUsingGpuMemory() const = 0;
 
-  // Update the texture image to the most recent frame and bind it to the
-  // texture.
-  virtual void UpdateAndBindTexImage() = 0;
+  // Update texture image to the most recent frame and bind it to the provided
+  // texture |service_id| if TextureOwner does not implicitly binds texture
+  // during the update.
+  // If TextureOwner() always binds texture implicitly during the update, then
+  // it will always bind it to TextureOwner's texture id and not to the
+  // |service_id|.
+  virtual void UpdateAndBindTexImage(GLuint service_id) = 0;
+
   virtual bool HasTextureOwner() const = 0;
   virtual TextureBase* GetTextureBase() const = 0;
 
@@ -53,6 +58,10 @@
   // the overlay promotion. Return true if it could render to overlay correctly.
   virtual bool RenderToOverlay() = 0;
 
+  // Whether TextureOwner's implementation binds texture to TextureOwner owned
+  // texture_id during the texture update.
+  virtual bool TextureOwnerBindsTextureOnUpdate() = 0;
+
  protected:
   ~StreamTextureSharedImageInterface() override = default;
 };
diff --git a/gpu/command_buffer/service/surface_texture_gl_owner.cc b/gpu/command_buffer/service/surface_texture_gl_owner.cc
index ce6c674e7..633cba0 100644
--- a/gpu/command_buffer/service/surface_texture_gl_owner.cc
+++ b/gpu/command_buffer/service/surface_texture_gl_owner.cc
@@ -77,7 +77,7 @@
     surface_texture_->UpdateTexImage();
 }
 
-void SurfaceTextureGLOwner::EnsureTexImageBound() {
+void SurfaceTextureGLOwner::EnsureTexImageBound(GLuint service_id) {
   NOTREACHED();
 }
 
diff --git a/gpu/command_buffer/service/surface_texture_gl_owner.h b/gpu/command_buffer/service/surface_texture_gl_owner.h
index bb32efd..7223405 100644
--- a/gpu/command_buffer/service/surface_texture_gl_owner.h
+++ b/gpu/command_buffer/service/surface_texture_gl_owner.h
@@ -32,7 +32,7 @@
       const base::RepeatingClosure& frame_available_cb) override;
   gl::ScopedJavaSurface CreateJavaSurface() const override;
   void UpdateTexImage() override;
-  void EnsureTexImageBound() override;
+  void EnsureTexImageBound(GLuint service_id) override;
   void ReleaseBackBuffers() override;
   std::unique_ptr<base::android::ScopedHardwareBufferFenceSync>
   GetAHardwareBuffer() override;
diff --git a/gpu/command_buffer/service/texture_owner.h b/gpu/command_buffer/service/texture_owner.h
index bf47f63a..7f5b20f 100644
--- a/gpu/command_buffer/service/texture_owner.h
+++ b/gpu/command_buffer/service/texture_owner.h
@@ -86,10 +86,10 @@
   // Update the texture image using the latest available image data.
   virtual void UpdateTexImage() = 0;
 
-  // Ensures that the latest texture image is bound to the texture target.
-  // Should only be used if the TextureOwner requires explicit binding of the
-  // image after an update.
-  virtual void EnsureTexImageBound() = 0;
+  // Ensures that the latest texture image is bound to the provided texture
+  // service_id. Should only be used if the TextureOwner requires explicit
+  // binding of the image after an update.
+  virtual void EnsureTexImageBound(GLuint service_id) = 0;
 
   // Transformation matrix if any associated with the texture image.
   virtual void ReleaseBackBuffers() = 0;
diff --git a/gpu/ipc/service/stream_texture_android.cc b/gpu/ipc/service/stream_texture_android.cc
index 487540c6..c091b09 100644
--- a/gpu/ipc/service/stream_texture_android.cc
+++ b/gpu/ipc/service/stream_texture_android.cc
@@ -134,8 +134,8 @@
   return true;
 }
 
-void StreamTexture::UpdateAndBindTexImage() {
-  UpdateTexImage(BindingsMode::kEnsureTexImageBound);
+void StreamTexture::UpdateAndBindTexImage(GLuint service_id) {
+  UpdateTexImage(BindingsMode::kEnsureTexImageBound, service_id);
 }
 
 bool StreamTexture::HasTextureOwner() const {
@@ -154,25 +154,40 @@
   return false;
 }
 
+bool StreamTexture::TextureOwnerBindsTextureOnUpdate() {
+  DCHECK(texture_owner_);
+  return texture_owner_->binds_texture_on_update();
+}
+
 void StreamTexture::OnContextLost() {
   texture_owner_ = nullptr;
 }
 
-void StreamTexture::UpdateTexImage(BindingsMode bindings_mode) {
+void StreamTexture::UpdateTexImage(BindingsMode bindings_mode,
+                                   GLuint service_id) {
   DCHECK(texture_owner_.get());
 
-  if (!has_pending_frame_) return;
+  if (!has_pending_frame_) {
+    // Same frame can be bound multiple times to a different |service_id|. For
+    // eg: SharedImageVideo::ProduceGLTexture/ProduceSkia() and hence
+    // BeginAccess() can happen multiple times with the same frame by the time
+    // next frame is available. In those cases new service_id is generated and
+    // the frame although doesn't require any update, still needs to be bound to
+    // new service_id.
+    EnsureBoundIfNeeded(bindings_mode, service_id);
+    return;
+  }
 
   std::unique_ptr<ui::ScopedMakeCurrent> scoped_make_current;
   base::Optional<ScopedRestoreTextureBinding> scoped_restore_texture;
-  if (texture_owner_->binds_texture_on_update() ||
-      (bindings_mode == BindingsMode::kEnsureTexImageBound)) {
+  if (texture_owner_->binds_texture_on_update()) {
     // If the texture_owner() binds the texture while doing the texture update
-    // (UpdateTexImage), like in SurfaceTexture case, OR if it was explicitly
-    // specified to bind the texture via bindings_mode, then only make the
-    // context current. For AImageReader, since we only acquire the latest image
-    // from it during the texture update process, there is no need to make it's
-    // context current if its not specified via bindings_mode.
+    // (UpdateTexImage), like in SurfaceTexture case, then make sure that the
+    // texture owner's context is made current. This is because the texture
+    // which will be bound was generated on TextureOwner's context.
+    // For AImageReader case, the texture which will be bound will not
+    // necessarily be TextureOwner's texture and hence caller is responsible to
+    // handle making correct context current before binding the texture.
     scoped_make_current = MakeCurrent(context_state_.get());
 
     // If updating the image will implicitly update the texture bindings then
@@ -183,18 +198,24 @@
     }
   }
   texture_owner_->UpdateTexImage();
-  EnsureBoundIfNeeded(bindings_mode);
+  EnsureBoundIfNeeded(bindings_mode, service_id);
   has_pending_frame_ = false;
 }
 
-void StreamTexture::EnsureBoundIfNeeded(BindingsMode mode) {
+void StreamTexture::EnsureBoundIfNeeded(BindingsMode mode, GLuint service_id) {
   DCHECK(texture_owner_);
 
-  if (texture_owner_->binds_texture_on_update())
+  if (texture_owner_->binds_texture_on_update()) {
+    if (mode == BindingsMode::kEnsureTexImageBound) {
+      DCHECK_EQ(service_id, texture_owner_->GetTextureId());
+    }
     return;
+  }
   if (mode != BindingsMode::kEnsureTexImageBound)
     return;
-  texture_owner_->EnsureTexImageBound();
+
+  DCHECK_GT(service_id, static_cast<unsigned>(0));
+  texture_owner_->EnsureTexImageBound(service_id);
 }
 
 bool StreamTexture::CopyTexImage(unsigned target) {
@@ -207,17 +228,21 @@
   GLint texture_id;
   glGetIntegerv(GL_TEXTURE_BINDING_EXTERNAL_OES, &texture_id);
 
-  // The following code only works if we're being asked to copy into
-  // |texture_id_|. Copying into a different texture is not supported.
-  // On some devices GL_TEXTURE_BINDING_EXTERNAL_OES is not supported as
-  // glGetIntegerv() parameter. In this case the value of |texture_id| will be
-  // zero and we assume that it is properly bound to |texture_id_|.
+  // CopyTexImage will only be called for TextureOwner's SurfaceTexture
+  // implementation which binds texture to TextureOwner's texture_id on update.
+  // Also ensure that the CopyTexImage() is always called on TextureOwner's
+  // context.
+  DCHECK(texture_owner_->binds_texture_on_update());
+  DCHECK(texture_owner_->GetContext()->IsCurrent(nullptr));
   if (texture_id > 0 &&
       static_cast<unsigned>(texture_id) != texture_owner_->GetTextureId())
     return false;
 
-  UpdateTexImage(BindingsMode::kEnsureTexImageBound);
-
+  // On some devices GL_TEXTURE_BINDING_EXTERNAL_OES is not supported as
+  // glGetIntegerv() parameter. In this case the value of |texture_id| will be
+  // zero and we assume that it is properly bound to TextureOwner's texture id..
+  UpdateTexImage(BindingsMode::kEnsureTexImageBound,
+                 texture_owner_->GetTextureId());
   return true;
 }
 
@@ -232,7 +257,10 @@
   if (rotated_visible_size_.IsEmpty())
     return;
 
-  UpdateTexImage(BindingsMode::kEnsureTexImageBound);
+  // We only need TextureOwner to get the latest image and do not require it to
+  // be bound to a texture if TextureOwner does not binds texture on update.
+  // Hence pass service_id as 0.
+  UpdateTexImage(BindingsMode::kRestoreIfBound, /*service_id=*/0);
 
   gfx::Rect visible_rect;
   gfx::Size coded_size;
@@ -406,7 +434,8 @@
 
   // Using BindingsMode::kDontRestoreIfBound here since we do not want to bind
   // the image. We just want to get the AHardwareBuffer from the latest image.
-  UpdateTexImage(BindingsMode::kDontRestoreIfBound);
+  // Hence pass service_id as 0.
+  UpdateTexImage(BindingsMode::kDontRestoreIfBound, /*service_id=*/0);
   return texture_owner_->GetAHardwareBuffer();
 }
 
diff --git a/gpu/ipc/service/stream_texture_android.h b/gpu/ipc/service/stream_texture_android.h
index 4d29d5f..93e8552 100644
--- a/gpu/ipc/service/stream_texture_android.h
+++ b/gpu/ipc/service/stream_texture_android.h
@@ -82,17 +82,27 @@
   // gpu::StreamTextureSharedImageInterface implementation.
   void ReleaseResources() override {}
   bool IsUsingGpuMemory() const override;
-  void UpdateAndBindTexImage() override;
+  void UpdateAndBindTexImage(GLuint service_id) override;
   bool HasTextureOwner() const override;
   TextureBase* GetTextureBase() const override;
   void NotifyOverlayPromotion(bool promotion, const gfx::Rect& bounds) override;
   bool RenderToOverlay() override;
+  bool TextureOwnerBindsTextureOnUpdate() override;
 
   // SharedContextState::ContextLostObserver implementation.
   void OnContextLost() override;
 
-  void UpdateTexImage(BindingsMode bindings_mode);
-  void EnsureBoundIfNeeded(BindingsMode mode);
+  // Update the TextureOwner to get the latest image. Also bind the latest image
+  // to the provided |service_id| if TextureOwner does not binds texture on
+  // update. If |bindings_mode| is other than kEnsureTexImageBound, then
+  // |service_id| is not required.
+  void UpdateTexImage(BindingsMode bindings_mode, GLuint service_id);
+
+  // Ensure that the latest image is bound to the texture |service_id| if
+  // TextureOwner does not binds texture on update. If TextureOwner binds
+  // texture on update, then it will always be bound to the TextureOwners
+  // texture and |service_id| will be ignored.
+  void EnsureBoundIfNeeded(BindingsMode mode, GLuint service_id);
   gpu::Mailbox CreateSharedImage(const gfx::Size& coded_size);
 
   // Called when a new frame is available for the SurfaceOwner.
diff --git a/ipc/ipc_message_utils.cc b/ipc/ipc_message_utils.cc
index 4cb4e41..c87ddb67 100644
--- a/ipc/ipc_message_utils.cc
+++ b/ipc/ipc_message_utils.cc
@@ -128,7 +128,7 @@
       const base::DictionaryValue* dict =
           static_cast<const base::DictionaryValue*>(value);
 
-      WriteParam(m, base::checked_cast<int>(dict->size()));
+      WriteParam(m, base::checked_cast<int>(dict->DictSize()));
 
       for (base::DictionaryValue::Iterator it(*dict); !it.IsAtEnd();
            it.Advance()) {
diff --git a/media/gpu/android/codec_image.cc b/media/gpu/android/codec_image.cc
index 3721ceb..f176cad 100644
--- a/media/gpu/android/codec_image.cc
+++ b/media/gpu/android/codec_image.cc
@@ -84,17 +84,24 @@
   if (!output_buffer_renderer_)
     return true;
 
-  GLint bound_service_id = 0;
-  glGetIntegerv(GL_TEXTURE_BINDING_EXTERNAL_OES, &bound_service_id);
-  // The currently bound texture should be the texture owner's texture.
-  if (bound_service_id !=
-      static_cast<GLint>(
-          output_buffer_renderer_->texture_owner()->GetTextureId()))
+  GLint texture_id = 0;
+  glGetIntegerv(GL_TEXTURE_BINDING_EXTERNAL_OES, &texture_id);
+
+  // CopyTexImage will only be called for TextureOwner's SurfaceTexture
+  // implementation which binds texture to TextureOwner's texture_id on update.
+  DCHECK(output_buffer_renderer_->texture_owner()->binds_texture_on_update());
+  if (texture_id > 0 &&
+      static_cast<unsigned>(texture_id) !=
+          output_buffer_renderer_->texture_owner()->GetTextureId()) {
     return false;
+  }
 
-
+  // On some devices GL_TEXTURE_BINDING_EXTERNAL_OES is not supported as
+  // glGetIntegerv() parameter. In this case the value of |texture_id| will be
+  // zero and we assume that it is properly bound to TextureOwner's texture id.
   output_buffer_renderer_->RenderToTextureOwnerFrontBuffer(
-      BindingsMode::kEnsureTexImageBound);
+      BindingsMode::kEnsureTexImageBound,
+      output_buffer_renderer_->texture_owner()->GetTextureId());
   return true;
 }
 
@@ -168,8 +175,9 @@
   return output_buffer_renderer_->was_tex_image_bound();
 }
 
-void CodecImage::UpdateAndBindTexImage() {
-  RenderToTextureOwnerFrontBuffer(BindingsMode::kEnsureTexImageBound);
+void CodecImage::UpdateAndBindTexImage(GLuint service_id) {
+  RenderToTextureOwnerFrontBuffer(BindingsMode::kEnsureTexImageBound,
+                                  service_id);
 }
 
 bool CodecImage::HasTextureOwner() const {
@@ -193,11 +201,12 @@
   return output_buffer_renderer_->RenderToTextureOwnerBackBuffer();
 }
 
-bool CodecImage::RenderToTextureOwnerFrontBuffer(BindingsMode bindings_mode) {
+bool CodecImage::RenderToTextureOwnerFrontBuffer(BindingsMode bindings_mode,
+                                                 GLuint service_id) {
   if (!output_buffer_renderer_)
     return false;
-  return output_buffer_renderer_->RenderToTextureOwnerFrontBuffer(
-      bindings_mode);
+  return output_buffer_renderer_->RenderToTextureOwnerFrontBuffer(bindings_mode,
+                                                                  service_id);
 }
 
 bool CodecImage::RenderToOverlay() {
@@ -206,6 +215,12 @@
   return output_buffer_renderer_->RenderToOverlay();
 }
 
+bool CodecImage::TextureOwnerBindsTextureOnUpdate() {
+  if (!output_buffer_renderer_)
+    return false;
+  return output_buffer_renderer_->texture_owner()->binds_texture_on_update();
+}
+
 void CodecImage::ReleaseCodecBuffer() {
   output_buffer_renderer_.reset();
 }
@@ -219,7 +234,11 @@
   if (!output_buffer_renderer_)
     return nullptr;
 
-  RenderToTextureOwnerFrontBuffer(BindingsMode::kDontRestoreIfBound);
+  // Using BindingsMode::kDontRestoreIfBound here since we do not want to bind
+  // the image. We just want to get the AHardwareBuffer from the latest image.
+  // Hence pass service_id as 0.
+  RenderToTextureOwnerFrontBuffer(BindingsMode::kDontRestoreIfBound,
+                                  0 /* service_id */);
   return output_buffer_renderer_->texture_owner()->GetAHardwareBuffer();
 }
 
diff --git a/media/gpu/android/codec_image.h b/media/gpu/android/codec_image.h
index d71cda8f..531f0d6 100644
--- a/media/gpu/android/codec_image.h
+++ b/media/gpu/android/codec_image.h
@@ -99,13 +99,14 @@
   // gpu::StreamTextureSharedImageInterface implementation.
   void ReleaseResources() override;
   bool IsUsingGpuMemory() const override;
-  void UpdateAndBindTexImage() override;
+  void UpdateAndBindTexImage(GLuint service_id) override;
   bool HasTextureOwner() const override;
   gpu::TextureBase* GetTextureBase() const override;
   void NotifyOverlayPromotion(bool promotion, const gfx::Rect& bounds) override;
   // Renders this image to the overlay. Returns true if the buffer is in the
   // overlay front buffer. Returns false if the buffer was invalidated.
   bool RenderToOverlay() override;
+  bool TextureOwnerBindsTextureOnUpdate() override;
 
   // Whether the codec buffer has been rendered to the front buffer.
   bool was_rendered_to_front_buffer() const {
@@ -156,7 +157,12 @@
   // Renders this image to the texture owner front buffer by first rendering
   // it to the back buffer if it's not already there, and then waiting for the
   // frame available event before calling UpdateTexImage().
-  bool RenderToTextureOwnerFrontBuffer(BindingsMode bindings_mode);
+  // Also bind the latest image
+  // to the provided |service_id| if TextureOwner does not binds texture on
+  // update. If |bindings_mode| is other than kEnsureTexImageBound, then
+  // |service_id| is not required.
+  bool RenderToTextureOwnerFrontBuffer(BindingsMode bindings_mode,
+                                       GLuint service_id);
 
   // Whether this image is texture_owner or overlay backed.
   bool is_texture_owner_backed_ = false;
diff --git a/media/gpu/android/codec_image_unittest.cc b/media/gpu/android/codec_image_unittest.cc
index 29a09b3..bdc1676a 100644
--- a/media/gpu/android/codec_image_unittest.cc
+++ b/media/gpu/android/codec_image_unittest.cc
@@ -207,20 +207,6 @@
   ASSERT_TRUE(i->was_rendered_to_front_buffer());
 }
 
-TEST_F(CodecImageTestExplicitBind, CopyTexImageTriggersFrontBufferRendering) {
-  auto i = NewImage(kTextureOwner);
-  // Verify that the release comes before the wait.
-  InSequence s;
-  EXPECT_CALL(*codec_, ReleaseOutputBuffer(_, true));
-  EXPECT_CALL(*codec_buffer_wait_coordinator_, WaitForFrameAvailable());
-  EXPECT_CALL(*codec_buffer_wait_coordinator_->texture_owner(),
-              UpdateTexImage());
-  EXPECT_CALL(*codec_buffer_wait_coordinator_->texture_owner(),
-              EnsureTexImageBound());
-  i->CopyTexImage(GL_TEXTURE_EXTERNAL_OES);
-  ASSERT_TRUE(i->was_rendered_to_front_buffer());
-}
-
 TEST_F(CodecImageTest, ScheduleOverlayPlaneTriggersFrontBufferRendering) {
   auto i = NewImage(kOverlay);
   EXPECT_CALL(*codec_, ReleaseOutputBuffer(_, true));
@@ -380,7 +366,8 @@
   i->NotifyUnused();
   EXPECT_FALSE(i->RenderToTextureOwnerBackBuffer());
   EXPECT_FALSE(i->RenderToTextureOwnerFrontBuffer(
-      CodecImage::BindingsMode::kEnsureTexImageBound));
+      CodecImage::BindingsMode::kEnsureTexImageBound,
+      codec_buffer_wait_coordinator_->texture_owner()->GetTextureId()));
 }
 
 TEST_F(CodecImageTest, CodedSizeVsVisibleSize) {
diff --git a/media/gpu/android/codec_output_buffer_renderer.cc b/media/gpu/android/codec_output_buffer_renderer.cc
index 9d93ed1..22684df 100644
--- a/media/gpu/android/codec_output_buffer_renderer.cc
+++ b/media/gpu/android/codec_output_buffer_renderer.cc
@@ -14,6 +14,7 @@
 #include "ui/gl/scoped_make_current.h"
 
 namespace media {
+
 namespace {
 
 // Makes |texture_owner|'s context current if it isn't already.
@@ -80,7 +81,8 @@
 }
 
 bool CodecOutputBufferRenderer::RenderToTextureOwnerFrontBuffer(
-    BindingsMode bindings_mode) {
+    BindingsMode bindings_mode,
+    GLuint service_id) {
   // Normally, we should have a wait coordinator if we're called.  However, if
   // the renderer is torn down (either VideoFrameSubmitter or the whole process)
   // before we get returns back from viz, then we can be notified that we're
@@ -90,7 +92,7 @@
     return false;
 
   if (phase_ == Phase::kInFrontBuffer) {
-    EnsureBoundIfNeeded(bindings_mode);
+    EnsureBoundIfNeeded(bindings_mode, service_id);
     return true;
   }
   if (phase_ == Phase::kInvalidated)
@@ -100,14 +102,14 @@
   base::Optional<gpu::ScopedRestoreTextureBinding> scoped_restore_texture;
 
   if (codec_buffer_wait_coordinator_->texture_owner()
-          ->binds_texture_on_update() ||
-      (bindings_mode == BindingsMode::kEnsureTexImageBound)) {
+          ->binds_texture_on_update()) {
     // If the texture_owner() binds the texture while doing the texture update
-    // (UpdateTexImage), like in SurfaceTexture case, OR if it was explicitly
-    // specified to bind the texture via bindings_mode, then only make the
-    // context current. For AImageReader, since we only acquire the latest image
-    // from it during the texture update process, there is no need to make it's
-    // context current if its not specified via bindings_mode.
+    // (UpdateTexImage), like in SurfaceTexture case, then make sure that the
+    // texture owner's context is made current. This is because the texture
+    // which will be bound was generated on TextureOwner's context.
+    // For AImageReader case, the texture which will be bound will not
+    // necessarily be TextureOwner's texture and hence caller is responsible to
+    // handle making correct context current before binding the texture.
     scoped_make_current = MakeCurrentIfNeeded(
         codec_buffer_wait_coordinator_->texture_owner().get());
 
@@ -144,21 +146,30 @@
     codec_buffer_wait_coordinator_->WaitForFrameAvailable();
 
   codec_buffer_wait_coordinator_->texture_owner()->UpdateTexImage();
-  EnsureBoundIfNeeded(bindings_mode);
+  EnsureBoundIfNeeded(bindings_mode, service_id);
   return true;
 }
 
-void CodecOutputBufferRenderer::EnsureBoundIfNeeded(BindingsMode mode) {
+void CodecOutputBufferRenderer::EnsureBoundIfNeeded(BindingsMode mode,
+                                                    GLuint service_id) {
   DCHECK(codec_buffer_wait_coordinator_);
 
   if (codec_buffer_wait_coordinator_->texture_owner()
           ->binds_texture_on_update()) {
+    if (mode == BindingsMode::kEnsureTexImageBound) {
+      DCHECK_EQ(
+          service_id,
+          codec_buffer_wait_coordinator_->texture_owner()->GetTextureId());
+    }
     was_tex_image_bound_ = true;
     return;
   }
   if (mode != BindingsMode::kEnsureTexImageBound)
     return;
-  codec_buffer_wait_coordinator_->texture_owner()->EnsureTexImageBound();
+
+  DCHECK_GT(service_id, 0u);
+  codec_buffer_wait_coordinator_->texture_owner()->EnsureTexImageBound(
+      service_id);
   was_tex_image_bound_ = true;
 }
 
@@ -178,9 +189,11 @@
 
 bool CodecOutputBufferRenderer::RenderToFrontBuffer() {
   // This code is used to trigger early rendering of the image before it is used
-  // for compositing, there is no need to bind the image.
+  // for compositing, there is no need to bind the image. Hence pass texture
+  // service_id as 0.
   return codec_buffer_wait_coordinator_
-             ? RenderToTextureOwnerFrontBuffer(BindingsMode::kRestoreIfBound)
+             ? RenderToTextureOwnerFrontBuffer(BindingsMode::kRestoreIfBound,
+                                               0 /* service_id */)
              : RenderToOverlay();
 }
 
diff --git a/media/gpu/android/codec_output_buffer_renderer.h b/media/gpu/android/codec_output_buffer_renderer.h
index c60e8268..e8c2377 100644
--- a/media/gpu/android/codec_output_buffer_renderer.h
+++ b/media/gpu/android/codec_output_buffer_renderer.h
@@ -39,7 +39,11 @@
   // Renders this image to the texture owner front buffer by first rendering
   // it to the back buffer if it's not already there, and then waiting for the
   // frame available event before calling UpdateTexImage().
-  bool RenderToTextureOwnerFrontBuffer(BindingsMode bindings_mode);
+  // Also bind the latest imagecto the provided |service_id| if TextureOwner
+  // does not binds texture on update. If |bindings_mode| is other than
+  // kEnsureTexImageBound, then |service_id| is not required.
+  bool RenderToTextureOwnerFrontBuffer(BindingsMode bindings_mode,
+                                       GLuint service_id);
 
   // Renders this image to the front buffer of its backing surface.
   // Returns true if the buffer is in the front buffer. Returns false if the
@@ -80,7 +84,11 @@
   // kInFrontBuffer and kInvalidated are terminal.
   enum class Phase { kInCodec, kInBackBuffer, kInFrontBuffer, kInvalidated };
 
-  void EnsureBoundIfNeeded(BindingsMode mode);
+  // Ensure that the latest image is bound to the texture |service_id| if
+  // TextureOwner does not binds texture on update. If TextureOwner binds
+  // texture on update, then it will always be bound to the TextureOwners
+  // texture and |service_id| will be ignored.
+  void EnsureBoundIfNeeded(BindingsMode mode, GLuint service_id);
 
   void set_phase_for_testing(Phase phase) { phase_ = phase; }
 
diff --git a/media/gpu/android/frame_info_helper.cc b/media/gpu/android/frame_info_helper.cc
index 6d4b209..1287357 100644
--- a/media/gpu/android/frame_info_helper.cc
+++ b/media/gpu/android/frame_info_helper.cc
@@ -80,7 +80,8 @@
       base::Optional<FrameInfo> info;
 
       if (buffer_renderer->RenderToTextureOwnerFrontBuffer(
-              CodecOutputBufferRenderer::BindingsMode::kDontRestoreIfBound)) {
+              CodecOutputBufferRenderer::BindingsMode::kDontRestoreIfBound,
+              0)) {
         gfx::Size coded_size;
         gfx::Rect visible_rect;
         if (texture_owner->GetCodedSizeAndVisibleRect(
diff --git a/media/renderers/BUILD.gn b/media/renderers/BUILD.gn
index 5ab25ad..154f6be4 100644
--- a/media/renderers/BUILD.gn
+++ b/media/renderers/BUILD.gn
@@ -26,6 +26,8 @@
     "renderer_impl.h",
     "video_frame_yuv_converter.cc",
     "video_frame_yuv_converter.h",
+    "video_frame_yuv_mailboxes_holder.cc",
+    "video_frame_yuv_mailboxes_holder.h",
     "video_overlay_factory.cc",
     "video_overlay_factory.h",
     "video_renderer_impl.cc",
diff --git a/media/renderers/video_frame_yuv_converter.cc b/media/renderers/video_frame_yuv_converter.cc
index 6eded70..9147050 100644
--- a/media/renderers/video_frame_yuv_converter.cc
+++ b/media/renderers/video_frame_yuv_converter.cc
@@ -12,6 +12,7 @@
 #include "gpu/command_buffer/client/shared_image_interface.h"
 #include "gpu/command_buffer/common/shared_image_usage.h"
 #include "media/base/video_frame.h"
+#include "media/renderers/video_frame_yuv_mailboxes_holder.h"
 #include "third_party/skia/include/core/SkCanvas.h"
 #include "third_party/skia/include/core/SkImage.h"
 #include "third_party/skia/include/core/SkRefCnt.h"
@@ -26,50 +27,6 @@
 
 namespace {
 
-SkYUVColorSpace ColorSpaceToSkYUVColorSpace(
-    const gfx::ColorSpace& color_space) {
-  // TODO(crbug.com/828599): This should really default to rec709.
-  SkYUVColorSpace sk_color_space = kRec601_SkYUVColorSpace;
-  color_space.ToSkYUVColorSpace(&sk_color_space);
-  return sk_color_space;
-}
-
-viz::ResourceFormat PlaneResourceFormat(int num_channels) {
-  switch (num_channels) {
-    case 1:
-      return viz::LUMINANCE_8;
-    case 2:
-      return viz::RG_88;
-    case 3:
-      return viz::RGBX_8888;
-    case 4:
-      return viz::RGBA_8888;
-  }
-  NOTREACHED();
-  return viz::RGBA_8888;
-}
-
-GLenum PlaneGLFormat(int num_channels) {
-  return viz::TextureStorageFormat(PlaneResourceFormat(num_channels));
-}
-
-std::tuple<SkYUVAInfo::PlaneConfig, SkYUVAInfo::Subsampling>
-VideoPixelFormatToSkiaValues(VideoPixelFormat video_format) {
-  // To expand support for additional VideoFormats expand this switch. Note that
-  // we do assume 8 bit formats. With that exception, anything else should work.
-  switch (video_format) {
-    case PIXEL_FORMAT_NV12:
-      return {SkYUVAInfo::PlaneConfig::kY_UV, SkYUVAInfo::Subsampling::k420};
-    case PIXEL_FORMAT_I420:
-      return {SkYUVAInfo::PlaneConfig::kY_U_V, SkYUVAInfo::Subsampling::k420};
-    case PIXEL_FORMAT_I420A:
-      return {SkYUVAInfo::PlaneConfig::kY_U_V_A, SkYUVAInfo::Subsampling::k420};
-    default:
-      return {SkYUVAInfo::PlaneConfig::kUnknown,
-              SkYUVAInfo::Subsampling::kUnknown};
-  }
-}
-
 SkColorType GetCompatibleSurfaceColorType(GrGLenum format) {
   switch (format) {
     case GL_RGBA8:
@@ -166,281 +123,13 @@
 
 }  // namespace
 
-class VideoFrameYUVConverter::VideoFrameYUVMailboxesHolder {
- public:
-  VideoFrameYUVMailboxesHolder() = default;
-  ~VideoFrameYUVMailboxesHolder() { ReleaseCachedData(); }
-
-  void ReleaseCachedData();
-  void ReleaseTextures();
-
-  // Extracts shared image information if |video_frame| is texture backed or
-  // creates new shared images and uploads YUV data to GPU if |video_frame| is
-  // mappable. This function can be called repeatedly to re-use shared images in
-  // the case of CPU backed VideoFrames. The planes are returned in |mailboxes|.
-  void VideoFrameToMailboxes(
-      const VideoFrame* video_frame,
-      viz::RasterContextProvider* raster_context_provider,
-      gpu::Mailbox mailboxes[]);
-
-  // Like VideoFrameToMailboxes but imports the textures from the mailboxes and
-  // returns the planes as a set of YUVA GrBackendTextures.
-  GrYUVABackendTextures VideoFrameToSkiaTextures(
-      const VideoFrame* video_frame,
-      viz::RasterContextProvider* raster_context_provider);
-
-  SkYUVAPixmaps VideoFrameToSkiaPixmaps(const VideoFrame* video_frame);
-
-  SkYUVAInfo::PlaneConfig plane_config() const { return plane_config_; }
-
-  SkYUVAInfo::Subsampling subsampling() const { return subsampling_; }
-
- private:
-  static constexpr size_t kMaxPlanes =
-      static_cast<size_t>(SkYUVAInfo::kMaxPlanes);
-
-  void ImportTextures();
-  size_t NumPlanes() {
-    return static_cast<size_t>(SkYUVAInfo::NumPlanes(plane_config_));
-  }
-
-  scoped_refptr<viz::RasterContextProvider> provider_;
-  bool imported_textures_ = false;
-  SkYUVAInfo::PlaneConfig plane_config_ = SkYUVAInfo::PlaneConfig::kUnknown;
-  SkYUVAInfo::Subsampling subsampling_ = SkYUVAInfo::Subsampling::kUnknown;
-  bool created_shared_images_ = false;
-  gfx::Size cached_video_size_;
-  gfx::ColorSpace cached_video_color_space_;
-  std::array<gpu::MailboxHolder, kMaxPlanes> holders_;
-
-  struct YUVPlaneTextureInfo {
-    GrGLTextureInfo texture = {0, 0};
-    bool is_shared_image = false;
-  };
-  std::array<YUVPlaneTextureInfo, kMaxPlanes> textures_;
-};
-
-void VideoFrameYUVConverter::VideoFrameYUVMailboxesHolder::ReleaseCachedData() {
-  if (holders_[0].mailbox.IsZero())
-    return;
-
-  ReleaseTextures();
-
-  // Don't destroy shared images we don't own.
-  if (!created_shared_images_)
-    return;
-
-  auto* ri = provider_->RasterInterface();
-  DCHECK(ri);
-  gpu::SyncToken token;
-  ri->GenUnverifiedSyncTokenCHROMIUM(token.GetData());
-
-  auto* sii = provider_->SharedImageInterface();
-  DCHECK(sii);
-  for (auto& mailbox_holder : holders_) {
-    if (!mailbox_holder.mailbox.IsZero())
-      sii->DestroySharedImage(token, mailbox_holder.mailbox);
-    mailbox_holder.mailbox.SetZero();
-  }
-
-  created_shared_images_ = false;
-}
-
-void VideoFrameYUVConverter::VideoFrameYUVMailboxesHolder::
-    VideoFrameToMailboxes(const VideoFrame* video_frame,
-                          viz::RasterContextProvider* raster_context_provider,
-                          gpu::Mailbox mailboxes[]) {
-  std::tie(plane_config_, subsampling_) =
-      VideoPixelFormatToSkiaValues(video_frame->format());
-  DCHECK_NE(plane_config_, SkYUVAInfo::PlaneConfig::kUnknown);
-
-  // If we have cached shared images but the provider or video has changed we
-  // need to release shared images created on the old context and recreate them.
-  if (created_shared_images_ &&
-      (provider_.get() != raster_context_provider ||
-       video_frame->coded_size() != cached_video_size_ ||
-       video_frame->ColorSpace() != cached_video_color_space_))
-    ReleaseCachedData();
-  provider_ = raster_context_provider;
-  DCHECK(provider_);
-  auto* ri = provider_->RasterInterface();
-  DCHECK(ri);
-
-  gfx::Size video_size = video_frame->coded_size();
-  SkISize plane_sizes[SkYUVAInfo::kMaxPlanes];
-  size_t num_planes = SkYUVAInfo::PlaneDimensions(
-      {video_size.width(), video_size.height()}, plane_config_, subsampling_,
-      kTopLeft_SkEncodedOrigin, plane_sizes);
-
-  if (video_frame->HasTextures()) {
-    DCHECK_EQ(num_planes, video_frame->NumTextures());
-    for (size_t plane = 0; plane < video_frame->NumTextures(); ++plane) {
-      holders_[plane] = video_frame->mailbox_holder(plane);
-      DCHECK(holders_[plane].texture_target == GL_TEXTURE_2D ||
-             holders_[plane].texture_target == GL_TEXTURE_EXTERNAL_OES ||
-             holders_[plane].texture_target == GL_TEXTURE_RECTANGLE_ARB)
-          << "Unsupported texture target " << std::hex << std::showbase
-          << holders_[plane].texture_target;
-      ri->WaitSyncTokenCHROMIUM(holders_[plane].sync_token.GetConstData());
-      mailboxes[plane] = holders_[plane].mailbox;
-    }
-  } else {
-    if (!created_shared_images_) {
-      auto* sii = provider_->SharedImageInterface();
-      DCHECK(sii);
-      uint32_t mailbox_usage;
-      if (provider_->ContextCapabilities().supports_oop_raster) {
-        mailbox_usage = gpu::SHARED_IMAGE_USAGE_RASTER |
-                        gpu::SHARED_IMAGE_USAGE_OOP_RASTERIZATION;
-      } else {
-        mailbox_usage = gpu::SHARED_IMAGE_USAGE_GLES2;
-      }
-      for (size_t plane = 0; plane < num_planes; ++plane) {
-        gfx::Size tex_size = {plane_sizes[plane].width(),
-                              plane_sizes[plane].height()};
-        int num_channels = SkYUVAInfo::NumChannelsInPlane(plane_config_, plane);
-        viz::ResourceFormat format = PlaneResourceFormat(num_channels);
-        holders_[plane].mailbox = sii->CreateSharedImage(
-            format, tex_size, video_frame->ColorSpace(),
-            kTopLeft_GrSurfaceOrigin, kPremul_SkAlphaType, mailbox_usage,
-            gpu::kNullSurfaceHandle);
-        holders_[plane].texture_target = GL_TEXTURE_2D;
-      }
-
-      // Split up shared image creation from upload so we only have to wait on
-      // one sync token.
-      ri->WaitSyncTokenCHROMIUM(sii->GenUnverifiedSyncToken().GetConstData());
-
-      cached_video_size_ = video_frame->coded_size();
-      cached_video_color_space_ = video_frame->ColorSpace();
-      created_shared_images_ = true;
-    }
-
-    // If we have cached shared images that have been imported release them to
-    // prevent writing to a shared image for which we're holding read access.
-    ReleaseTextures();
-
-    for (size_t plane = 0; plane < num_planes; ++plane) {
-      int num_channels = SkYUVAInfo::NumChannelsInPlane(plane_config_, plane);
-      SkColorType color_type = SkYUVAPixmapInfo::DefaultColorTypeForDataType(
-          SkYUVAPixmaps::DataType::kUnorm8, num_channels);
-      SkImageInfo info = SkImageInfo::Make(plane_sizes[plane], color_type,
-                                           kUnknown_SkAlphaType);
-      ri->WritePixels(holders_[plane].mailbox, 0, 0, GL_TEXTURE_2D,
-                      video_frame->stride(plane), info,
-                      video_frame->data(plane));
-      mailboxes[plane] = holders_[plane].mailbox;
-    }
-  }
-}
-
-GrYUVABackendTextures
-VideoFrameYUVConverter::VideoFrameYUVMailboxesHolder::VideoFrameToSkiaTextures(
-    const VideoFrame* video_frame,
-    viz::RasterContextProvider* raster_context_provider) {
-  gpu::Mailbox mailboxes[kMaxPlanes];
-  VideoFrameToMailboxes(video_frame, raster_context_provider, mailboxes);
-  ImportTextures();
-  SkISize video_size{video_frame->coded_size().width(),
-                     video_frame->coded_size().height()};
-  SkYUVAInfo yuva_info(video_size, plane_config_, subsampling_,
-                       ColorSpaceToSkYUVColorSpace(video_frame->ColorSpace()));
-  GrBackendTexture backend_textures[SkYUVAInfo::kMaxPlanes];
-  SkISize plane_sizes[SkYUVAInfo::kMaxPlanes];
-  int num_planes = yuva_info.planeDimensions(plane_sizes);
-  for (int i = 0; i < num_planes; ++i) {
-    backend_textures[i] = {plane_sizes[i].width(), plane_sizes[i].height(),
-                           GrMipmapped::kNo, textures_[i].texture};
-  }
-  return GrYUVABackendTextures(yuva_info, backend_textures,
-                               kTopLeft_GrSurfaceOrigin);
-}
-
-SkYUVAPixmaps
-VideoFrameYUVConverter::VideoFrameYUVMailboxesHolder::VideoFrameToSkiaPixmaps(
-    const VideoFrame* video_frame) {
-  std::tie(plane_config_, subsampling_) =
-      VideoPixelFormatToSkiaValues(video_frame->format());
-  DCHECK_NE(plane_config_, SkYUVAInfo::PlaneConfig::kUnknown);
-
-  SkISize video_size{video_frame->coded_size().width(),
-                     video_frame->coded_size().height()};
-  SkYUVAInfo yuva_info(video_size, plane_config_, subsampling_,
-                       ColorSpaceToSkYUVColorSpace(video_frame->ColorSpace()));
-  SkPixmap pixmaps[SkYUVAInfo::kMaxPlanes];
-  SkISize plane_sizes[SkYUVAInfo::kMaxPlanes];
-  int num_planes = yuva_info.planeDimensions(plane_sizes);
-
-  // Create SkImageInfos with the appropriate color types for 8 bit unorm data
-  // based on plane config.
-  size_t row_bytes[kMaxPlanes];
-  for (int i = 0; i < num_planes; ++i) {
-    row_bytes[i] =
-        VideoFrame::RowBytes(i, video_frame->format(), plane_sizes[i].width());
-  }
-
-  SkYUVAPixmapInfo pixmaps_infos(yuva_info, SkYUVAPixmaps::DataType::kUnorm8,
-                                 row_bytes);
-
-  for (int i = 0; i < num_planes; ++i) {
-    pixmaps[i].reset(pixmaps_infos.planeInfo(i), video_frame->data(i),
-                     pixmaps_infos.rowBytes(i));
-  }
-
-  return SkYUVAPixmaps::FromExternalPixmaps(yuva_info, pixmaps);
-}
-
-void VideoFrameYUVConverter::VideoFrameYUVMailboxesHolder::ImportTextures() {
-  DCHECK(!imported_textures_)
-      << "Textures should always be released after converting video frame. "
-         "Call ReleaseTextures() for each call to VideoFrameToSkiaTextures()";
-
-  auto* ri = provider_->RasterInterface();
-  for (size_t plane = 0; plane < NumPlanes(); ++plane) {
-    textures_[plane].texture.fID =
-        ri->CreateAndConsumeForGpuRaster(holders_[plane].mailbox);
-    if (holders_[plane].mailbox.IsSharedImage()) {
-      textures_[plane].is_shared_image = true;
-      ri->BeginSharedImageAccessDirectCHROMIUM(
-          textures_[plane].texture.fID,
-          GL_SHARED_IMAGE_ACCESS_MODE_READ_CHROMIUM);
-    }
-
-    int num_channels = SkYUVAInfo::NumChannelsInPlane(plane_config_, plane);
-    textures_[plane].texture.fTarget = holders_[plane].texture_target;
-    textures_[plane].texture.fFormat = PlaneGLFormat(num_channels);
-  }
-
-  imported_textures_ = true;
-}
-
-void VideoFrameYUVConverter::VideoFrameYUVMailboxesHolder::ReleaseTextures() {
-  if (!imported_textures_)
-    return;
-
-  auto* ri = provider_->RasterInterface();
-  DCHECK(ri);
-  for (auto& tex_info : textures_) {
-    if (!tex_info.texture.fID)
-      continue;
-
-    if (tex_info.is_shared_image)
-      ri->EndSharedImageAccessDirectCHROMIUM(tex_info.texture.fID);
-    ri->DeleteGpuRasterTexture(tex_info.texture.fID);
-
-    tex_info.texture.fID = 0;
-  }
-
-  imported_textures_ = false;
-}
-
 VideoFrameYUVConverter::VideoFrameYUVConverter() = default;
 VideoFrameYUVConverter::~VideoFrameYUVConverter() = default;
 
 bool VideoFrameYUVConverter::IsVideoFrameFormatSupported(
     const VideoFrame& video_frame) {
-  return std::get<0>(VideoPixelFormatToSkiaValues(video_frame.format())) !=
-         SkYUVAInfo::PlaneConfig::kUnknown;
+  return std::get<0>(VideoFrameYUVMailboxesHolder::VideoPixelFormatToSkiaValues(
+             video_frame.format())) != SkYUVAInfo::PlaneConfig::kUnknown;
 }
 
 bool VideoFrameYUVConverter::ConvertYUVVideoFrameNoCaching(
@@ -482,7 +171,8 @@
   DCHECK(ri);
   ri->WaitSyncTokenCHROMIUM(dest_mailbox_holder.sync_token.GetConstData());
   SkYUVColorSpace color_space =
-      ColorSpaceToSkYUVColorSpace(video_frame->ColorSpace());
+      VideoFrameYUVMailboxesHolder::ColorSpaceToSkYUVColorSpace(
+          video_frame->ColorSpace());
 
   gpu::Mailbox mailboxes[SkYUVAInfo::kMaxPlanes]{};
   holder_->VideoFrameToMailboxes(video_frame, raster_context_provider,
diff --git a/media/renderers/video_frame_yuv_converter.h b/media/renderers/video_frame_yuv_converter.h
index c8e761f..c7f4759 100644
--- a/media/renderers/video_frame_yuv_converter.h
+++ b/media/renderers/video_frame_yuv_converter.h
@@ -20,6 +20,7 @@
 namespace media {
 
 class VideoFrame;
+class VideoFrameYUVMailboxesHolder;
 
 // Converts YUV video frames to RGB format and stores the results in the
 // provided mailbox. The caller of functions in this class maintains ownership
@@ -87,9 +88,8 @@
       bool use_visible_rect,
       bool use_sk_pixmap);
 
-  class VideoFrameYUVMailboxesHolder;
   std::unique_ptr<VideoFrameYUVMailboxesHolder> holder_;
 };
 }  // namespace media
 
-#endif  // MEDIA_RENDERERS_VIDEO_FRAME_YUV_CONVERTER_H_
\ No newline at end of file
+#endif  // MEDIA_RENDERERS_VIDEO_FRAME_YUV_CONVERTER_H_
diff --git a/media/renderers/video_frame_yuv_mailboxes_holder.cc b/media/renderers/video_frame_yuv_mailboxes_holder.cc
new file mode 100644
index 0000000..a0db97f
--- /dev/null
+++ b/media/renderers/video_frame_yuv_mailboxes_holder.cc
@@ -0,0 +1,289 @@
+// 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/renderers/video_frame_yuv_mailboxes_holder.h"
+
+#include <GLES3/gl3.h>
+
+#include "components/viz/common/gpu/raster_context_provider.h"
+#include "components/viz/common/resources/resource_format_utils.h"
+#include "gpu/GLES2/gl2extchromium.h"
+#include "gpu/command_buffer/client/raster_interface.h"
+#include "gpu/command_buffer/client/shared_image_interface.h"
+#include "gpu/command_buffer/common/shared_image_usage.h"
+#include "third_party/skia/include/core/SkYUVAPixmaps.h"
+#include "third_party/skia/include/gpu/gl/GrGLTypes.h"
+
+namespace media {
+
+namespace {
+
+viz::ResourceFormat PlaneResourceFormat(int num_channels) {
+  switch (num_channels) {
+    case 1:
+      return viz::LUMINANCE_8;
+    case 2:
+      return viz::RG_88;
+    case 3:
+      return viz::RGBX_8888;
+    case 4:
+      return viz::RGBA_8888;
+  }
+  NOTREACHED();
+  return viz::RGBA_8888;
+}
+
+GLenum PlaneGLFormat(int num_channels) {
+  return viz::TextureStorageFormat(PlaneResourceFormat(num_channels));
+}
+
+}  // namespace
+
+VideoFrameYUVMailboxesHolder::VideoFrameYUVMailboxesHolder() = default;
+
+VideoFrameYUVMailboxesHolder::~VideoFrameYUVMailboxesHolder() {
+  ReleaseCachedData();
+}
+
+void VideoFrameYUVMailboxesHolder::ReleaseCachedData() {
+  if (holders_[0].mailbox.IsZero())
+    return;
+
+  ReleaseTextures();
+
+  // Don't destroy shared images we don't own.
+  if (!created_shared_images_)
+    return;
+
+  auto* ri = provider_->RasterInterface();
+  DCHECK(ri);
+  gpu::SyncToken token;
+  ri->GenUnverifiedSyncTokenCHROMIUM(token.GetData());
+
+  auto* sii = provider_->SharedImageInterface();
+  DCHECK(sii);
+  for (auto& mailbox_holder : holders_) {
+    if (!mailbox_holder.mailbox.IsZero())
+      sii->DestroySharedImage(token, mailbox_holder.mailbox);
+    mailbox_holder.mailbox.SetZero();
+  }
+
+  created_shared_images_ = false;
+}
+
+void VideoFrameYUVMailboxesHolder::VideoFrameToMailboxes(
+    const VideoFrame* video_frame,
+    viz::RasterContextProvider* raster_context_provider,
+    gpu::Mailbox mailboxes[]) {
+  std::tie(plane_config_, subsampling_) =
+      VideoPixelFormatToSkiaValues(video_frame->format());
+  DCHECK_NE(plane_config_, SkYUVAInfo::PlaneConfig::kUnknown);
+
+  // If we have cached shared images but the provider or video has changed we
+  // need to release shared images created on the old context and recreate them.
+  if (created_shared_images_ &&
+      (provider_.get() != raster_context_provider ||
+       video_frame->coded_size() != cached_video_size_ ||
+       video_frame->ColorSpace() != cached_video_color_space_))
+    ReleaseCachedData();
+  provider_ = raster_context_provider;
+  DCHECK(provider_);
+  auto* ri = provider_->RasterInterface();
+  DCHECK(ri);
+
+  gfx::Size video_size = video_frame->coded_size();
+  SkISize plane_sizes[SkYUVAInfo::kMaxPlanes];
+  size_t num_planes = SkYUVAInfo::PlaneDimensions(
+      {video_size.width(), video_size.height()}, plane_config_, subsampling_,
+      kTopLeft_SkEncodedOrigin, plane_sizes);
+
+  if (video_frame->HasTextures()) {
+    DCHECK_EQ(num_planes, video_frame->NumTextures());
+    for (size_t plane = 0; plane < video_frame->NumTextures(); ++plane) {
+      holders_[plane] = video_frame->mailbox_holder(plane);
+      DCHECK(holders_[plane].texture_target == GL_TEXTURE_2D ||
+             holders_[plane].texture_target == GL_TEXTURE_EXTERNAL_OES ||
+             holders_[plane].texture_target == GL_TEXTURE_RECTANGLE_ARB)
+          << "Unsupported texture target " << std::hex << std::showbase
+          << holders_[plane].texture_target;
+      ri->WaitSyncTokenCHROMIUM(holders_[plane].sync_token.GetConstData());
+      mailboxes[plane] = holders_[plane].mailbox;
+    }
+  } else {
+    if (!created_shared_images_) {
+      auto* sii = provider_->SharedImageInterface();
+      DCHECK(sii);
+      uint32_t mailbox_usage;
+      if (provider_->ContextCapabilities().supports_oop_raster) {
+        mailbox_usage = gpu::SHARED_IMAGE_USAGE_RASTER |
+                        gpu::SHARED_IMAGE_USAGE_OOP_RASTERIZATION;
+      } else {
+        mailbox_usage = gpu::SHARED_IMAGE_USAGE_GLES2;
+      }
+      for (size_t plane = 0; plane < num_planes; ++plane) {
+        gfx::Size tex_size = {plane_sizes[plane].width(),
+                              plane_sizes[plane].height()};
+        int num_channels = SkYUVAInfo::NumChannelsInPlane(plane_config_, plane);
+        viz::ResourceFormat format = PlaneResourceFormat(num_channels);
+        holders_[plane].mailbox = sii->CreateSharedImage(
+            format, tex_size, video_frame->ColorSpace(),
+            kTopLeft_GrSurfaceOrigin, kPremul_SkAlphaType, mailbox_usage,
+            gpu::kNullSurfaceHandle);
+        holders_[plane].texture_target = GL_TEXTURE_2D;
+      }
+
+      // Split up shared image creation from upload so we only have to wait on
+      // one sync token.
+      ri->WaitSyncTokenCHROMIUM(sii->GenUnverifiedSyncToken().GetConstData());
+
+      cached_video_size_ = video_frame->coded_size();
+      cached_video_color_space_ = video_frame->ColorSpace();
+      created_shared_images_ = true;
+    }
+
+    // If we have cached shared images that have been imported release them to
+    // prevent writing to a shared image for which we're holding read access.
+    ReleaseTextures();
+
+    for (size_t plane = 0; plane < num_planes; ++plane) {
+      int num_channels = SkYUVAInfo::NumChannelsInPlane(plane_config_, plane);
+      SkColorType color_type = SkYUVAPixmapInfo::DefaultColorTypeForDataType(
+          SkYUVAPixmaps::DataType::kUnorm8, num_channels);
+      SkImageInfo info = SkImageInfo::Make(plane_sizes[plane], color_type,
+                                           kUnknown_SkAlphaType);
+      ri->WritePixels(holders_[plane].mailbox, 0, 0, GL_TEXTURE_2D,
+                      video_frame->stride(plane), info,
+                      video_frame->data(plane));
+      mailboxes[plane] = holders_[plane].mailbox;
+    }
+  }
+}
+
+GrYUVABackendTextures VideoFrameYUVMailboxesHolder::VideoFrameToSkiaTextures(
+    const VideoFrame* video_frame,
+    viz::RasterContextProvider* raster_context_provider) {
+  gpu::Mailbox mailboxes[kMaxPlanes];
+  VideoFrameToMailboxes(video_frame, raster_context_provider, mailboxes);
+  ImportTextures();
+  SkISize video_size{video_frame->coded_size().width(),
+                     video_frame->coded_size().height()};
+  SkYUVAInfo yuva_info(video_size, plane_config_, subsampling_,
+                       ColorSpaceToSkYUVColorSpace(video_frame->ColorSpace()));
+  GrBackendTexture backend_textures[SkYUVAInfo::kMaxPlanes];
+  SkISize plane_sizes[SkYUVAInfo::kMaxPlanes];
+  int num_planes = yuva_info.planeDimensions(plane_sizes);
+  for (int i = 0; i < num_planes; ++i) {
+    backend_textures[i] = {plane_sizes[i].width(), plane_sizes[i].height(),
+                           GrMipmapped::kNo, textures_[i].texture};
+  }
+  return GrYUVABackendTextures(yuva_info, backend_textures,
+                               kTopLeft_GrSurfaceOrigin);
+}
+
+SkYUVAPixmaps VideoFrameYUVMailboxesHolder::VideoFrameToSkiaPixmaps(
+    const VideoFrame* video_frame) {
+  std::tie(plane_config_, subsampling_) =
+      VideoPixelFormatToSkiaValues(video_frame->format());
+  DCHECK_NE(plane_config_, SkYUVAInfo::PlaneConfig::kUnknown);
+
+  SkISize video_size{video_frame->coded_size().width(),
+                     video_frame->coded_size().height()};
+  SkYUVAInfo yuva_info(video_size, plane_config_, subsampling_,
+                       ColorSpaceToSkYUVColorSpace(video_frame->ColorSpace()));
+  SkPixmap pixmaps[SkYUVAInfo::kMaxPlanes];
+  SkISize plane_sizes[SkYUVAInfo::kMaxPlanes];
+  int num_planes = yuva_info.planeDimensions(plane_sizes);
+
+  // Create SkImageInfos with the appropriate color types for 8 bit unorm data
+  // based on plane config.
+  size_t row_bytes[kMaxPlanes];
+  for (int i = 0; i < num_planes; ++i) {
+    row_bytes[i] =
+        VideoFrame::RowBytes(i, video_frame->format(), plane_sizes[i].width());
+  }
+
+  SkYUVAPixmapInfo pixmaps_infos(yuva_info, SkYUVAPixmaps::DataType::kUnorm8,
+                                 row_bytes);
+
+  for (int i = 0; i < num_planes; ++i) {
+    pixmaps[i].reset(pixmaps_infos.planeInfo(i), video_frame->data(i),
+                     pixmaps_infos.rowBytes(i));
+  }
+
+  return SkYUVAPixmaps::FromExternalPixmaps(yuva_info, pixmaps);
+}
+
+void VideoFrameYUVMailboxesHolder::ImportTextures() {
+  DCHECK(!imported_textures_)
+      << "Textures should always be released after converting video frame. "
+         "Call ReleaseTextures() for each call to VideoFrameToSkiaTextures()";
+
+  auto* ri = provider_->RasterInterface();
+  for (size_t plane = 0; plane < NumPlanes(); ++plane) {
+    textures_[plane].texture.fID =
+        ri->CreateAndConsumeForGpuRaster(holders_[plane].mailbox);
+    if (holders_[plane].mailbox.IsSharedImage()) {
+      textures_[plane].is_shared_image = true;
+      ri->BeginSharedImageAccessDirectCHROMIUM(
+          textures_[plane].texture.fID,
+          GL_SHARED_IMAGE_ACCESS_MODE_READ_CHROMIUM);
+    }
+
+    int num_channels = SkYUVAInfo::NumChannelsInPlane(plane_config_, plane);
+    textures_[plane].texture.fTarget = holders_[plane].texture_target;
+    textures_[plane].texture.fFormat = PlaneGLFormat(num_channels);
+  }
+
+  imported_textures_ = true;
+}
+
+void VideoFrameYUVMailboxesHolder::ReleaseTextures() {
+  if (!imported_textures_)
+    return;
+
+  auto* ri = provider_->RasterInterface();
+  DCHECK(ri);
+  for (auto& tex_info : textures_) {
+    if (!tex_info.texture.fID)
+      continue;
+
+    if (tex_info.is_shared_image)
+      ri->EndSharedImageAccessDirectCHROMIUM(tex_info.texture.fID);
+    ri->DeleteGpuRasterTexture(tex_info.texture.fID);
+
+    tex_info.texture.fID = 0;
+  }
+
+  imported_textures_ = false;
+}
+
+// static
+SkYUVColorSpace VideoFrameYUVMailboxesHolder::ColorSpaceToSkYUVColorSpace(
+    const gfx::ColorSpace& color_space) {
+  // TODO(crbug.com/828599): This should really default to rec709.
+  SkYUVColorSpace sk_color_space = kRec601_SkYUVColorSpace;
+  color_space.ToSkYUVColorSpace(&sk_color_space);
+  return sk_color_space;
+}
+
+// static
+std::tuple<SkYUVAInfo::PlaneConfig, SkYUVAInfo::Subsampling>
+VideoFrameYUVMailboxesHolder::VideoPixelFormatToSkiaValues(
+    VideoPixelFormat video_format) {
+  // To expand support for additional VideoFormats expand this switch. Note that
+  // we do assume 8 bit formats. With that exception, anything else should work.
+  switch (video_format) {
+    case PIXEL_FORMAT_NV12:
+      return {SkYUVAInfo::PlaneConfig::kY_UV, SkYUVAInfo::Subsampling::k420};
+    case PIXEL_FORMAT_I420:
+      return {SkYUVAInfo::PlaneConfig::kY_U_V, SkYUVAInfo::Subsampling::k420};
+    case PIXEL_FORMAT_I420A:
+      return {SkYUVAInfo::PlaneConfig::kY_U_V_A, SkYUVAInfo::Subsampling::k420};
+    default:
+      return {SkYUVAInfo::PlaneConfig::kUnknown,
+              SkYUVAInfo::Subsampling::kUnknown};
+  }
+}
+
+}  // namespace media
diff --git a/media/renderers/video_frame_yuv_mailboxes_holder.h b/media/renderers/video_frame_yuv_mailboxes_holder.h
new file mode 100644
index 0000000..7df4ff6
--- /dev/null
+++ b/media/renderers/video_frame_yuv_mailboxes_holder.h
@@ -0,0 +1,83 @@
+// 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_RENDERERS_VIDEO_FRAME_YUV_MAILBOXES_HOLDER_H_
+#define MEDIA_RENDERERS_VIDEO_FRAME_YUV_MAILBOXES_HOLDER_H_
+
+#include "media/base/video_frame.h"
+#include "third_party/skia/include/core/SkYUVAInfo.h"
+#include "third_party/skia/include/gpu/GrYUVABackendTextures.h"
+#include "third_party/skia/include/gpu/gl/GrGLTypes.h"
+
+namespace viz {
+class RasterContextProvider;
+}  // namespace viz
+
+namespace media {
+
+class VideoFrameYUVMailboxesHolder {
+ public:
+  VideoFrameYUVMailboxesHolder();
+  ~VideoFrameYUVMailboxesHolder();
+
+  void ReleaseCachedData();
+  void ReleaseTextures();
+
+  // Extracts shared image information if |video_frame| is texture backed or
+  // creates new shared images and uploads YUV data to GPU if |video_frame| is
+  // mappable. This function can be called repeatedly to re-use shared images in
+  // the case of CPU backed VideoFrames. The planes are returned in |mailboxes|.
+  void VideoFrameToMailboxes(
+      const VideoFrame* video_frame,
+      viz::RasterContextProvider* raster_context_provider,
+      gpu::Mailbox mailboxes[]);
+
+  // Like VideoFrameToMailboxes but imports the textures from the mailboxes and
+  // returns the planes as a set of YUVA GrBackendTextures.
+  GrYUVABackendTextures VideoFrameToSkiaTextures(
+      const VideoFrame* video_frame,
+      viz::RasterContextProvider* raster_context_provider);
+
+  SkYUVAPixmaps VideoFrameToSkiaPixmaps(const VideoFrame* video_frame);
+
+  SkYUVAInfo::PlaneConfig plane_config() const { return plane_config_; }
+
+  SkYUVAInfo::Subsampling subsampling() const { return subsampling_; }
+
+  // Utility to convert a gfx::ColorSpace to a SkYUVColorSpace.
+  static SkYUVColorSpace ColorSpaceToSkYUVColorSpace(
+      const gfx::ColorSpace& color_space);
+
+  // Utility to convert a media pixel format to SkYUVAInfo.
+  static std::tuple<SkYUVAInfo::PlaneConfig, SkYUVAInfo::Subsampling>
+  VideoPixelFormatToSkiaValues(VideoPixelFormat video_format);
+
+ private:
+  static constexpr size_t kMaxPlanes =
+      static_cast<size_t>(SkYUVAInfo::kMaxPlanes);
+
+  void ImportTextures();
+  size_t NumPlanes() {
+    return static_cast<size_t>(SkYUVAInfo::NumPlanes(plane_config_));
+  }
+
+  scoped_refptr<viz::RasterContextProvider> provider_;
+  bool imported_textures_ = false;
+  SkYUVAInfo::PlaneConfig plane_config_ = SkYUVAInfo::PlaneConfig::kUnknown;
+  SkYUVAInfo::Subsampling subsampling_ = SkYUVAInfo::Subsampling::kUnknown;
+  bool created_shared_images_ = false;
+  gfx::Size cached_video_size_;
+  gfx::ColorSpace cached_video_color_space_;
+  std::array<gpu::MailboxHolder, kMaxPlanes> holders_;
+
+  struct YUVPlaneTextureInfo {
+    GrGLTextureInfo texture = {0, 0};
+    bool is_shared_image = false;
+  };
+  std::array<YUVPlaneTextureInfo, kMaxPlanes> textures_;
+};
+
+}  // namespace media
+
+#endif  // MEDIA_RENDERERS_VIDEO_FRAME_YUV_MAILBOXES_HOLDER_H_
diff --git a/mojo/public/cpp/base/values_mojom_traits.h b/mojo/public/cpp/base/values_mojom_traits.h
index feb3780..dc9d473 100644
--- a/mojo/public/cpp/base/values_mojom_traits.h
+++ b/mojo/public/cpp/base/values_mojom_traits.h
@@ -24,7 +24,7 @@
 
   static size_t GetSize(const base::Value& input) {
     DCHECK(input.is_dict());
-    return static_cast<const base::DictionaryValue&>(input).size();
+    return static_cast<const base::DictionaryValue&>(input).DictSize();
   }
 
   static Iterator GetBegin(const base::Value& input) {
diff --git a/remoting/host/it2me/it2me_native_messaging_host.cc b/remoting/host/it2me/it2me_native_messaging_host.cc
index 35d4836..586646e2 100644
--- a/remoting/host/it2me/it2me_native_messaging_host.cc
+++ b/remoting/host/it2me/it2me_native_messaging_host.cc
@@ -344,7 +344,7 @@
 
   std::unique_ptr<base::DictionaryValue> policies =
       policy_watcher_->GetEffectivePolicies();
-  if (policies->size() == 0) {
+  if (policies->DictSize() == 0) {
     // At this point policies have been read, so if there are none set then
     // it indicates an error. Since this can be fixed by end users it has a
     // dedicated message type rather than the generic "error" so that the
diff --git a/storage/browser/quota/OWNERS b/storage/browser/quota/OWNERS
index 94ee5f9..fc8d259 100644
--- a/storage/browser/quota/OWNERS
+++ b/storage/browser/quota/OWNERS
@@ -1,7 +1,8 @@
 # Primary
-jarrydg@chromium.org
+ayui@chromium.org
 
 # Secondary
+jarrydg@chromium.org
 jsbell@chromium.org
 kinuko@chromium.org
 mek@chromium.org
diff --git a/testing/buildbot/chromium.devtools-frontend.json b/testing/buildbot/chromium.devtools-frontend.json
index 062b03e..480ed2f 100644
--- a/testing/buildbot/chromium.devtools-frontend.json
+++ b/testing/buildbot/chromium.devtools-frontend.json
@@ -19,7 +19,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -49,7 +49,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
@@ -79,7 +79,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
diff --git a/testing/buildbot/client.devtools-frontend.integration.json b/testing/buildbot/client.devtools-frontend.integration.json
index 09c8a5a8..c120bcf2 100644
--- a/testing/buildbot/client.devtools-frontend.integration.json
+++ b/testing/buildbot/client.devtools-frontend.integration.json
@@ -19,7 +19,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -40,7 +40,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -70,7 +70,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
@@ -100,7 +100,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
diff --git a/testing/buildbot/filters/linux-lacros.browser_tests.filter b/testing/buildbot/filters/linux-lacros.browser_tests.filter
index e20953f..c3a7626 100644
--- a/testing/buildbot/filters/linux-lacros.browser_tests.filter
+++ b/testing/buildbot/filters/linux-lacros.browser_tests.filter
@@ -16,8 +16,6 @@
 -All/MultiActionAPITest.PopupCreation/1
 -All/MultiActionAPITest.SessionStorageDoesNotPersistBetweenOpenings/1
 -All/PopupBrowserTest.MoveClampedToCurrentDisplay*
--All/SystemWebAppLinkCaptureBrowserTest.IncognitoBrowserOmniboxLinkCapture*
--All/SystemWebAppLinkCaptureBrowserTest.WindowOpenFromOtherSWA*
 -All/WebRtcScreenCaptureBrowserTestWithPicker.ScreenCaptureVideo*
 -All/WebRtcScreenCaptureBrowserTestWithPicker.ScreenCaptureVideoAndAudio*
 -AppViewTest.KillGuestCommunicatingWithWrongAppView
diff --git a/testing/buildbot/tryserver.devtools-frontend.json b/testing/buildbot/tryserver.devtools-frontend.json
index fe4f44b..956a941 100644
--- a/testing/buildbot/tryserver.devtools-frontend.json
+++ b/testing/buildbot/tryserver.devtools-frontend.json
@@ -16,7 +16,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -47,7 +47,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
@@ -77,7 +77,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
@@ -109,7 +109,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
@@ -139,7 +139,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
diff --git a/testing/buildbot/waterfalls.pyl b/testing/buildbot/waterfalls.pyl
index 5fd4a4a..1fe10da 100644
--- a/testing/buildbot/waterfalls.pyl
+++ b/testing/buildbot/waterfalls.pyl
@@ -2555,7 +2555,7 @@
     'machines': {
       'DevTools Linux (chromium)': {
         'mixins': [
-          'linux-xenial',
+          'linux-bionic',
         ],
         'additional_compile_targets': ['blink_tests'],
         'test_suites': {
@@ -6206,7 +6206,7 @@
     'machines': {
       'DevTools Linux': {
         'mixins': [
-          'linux-xenial',
+          'linux-bionic',
         ],
         'additional_compile_targets': ['blink_tests'],
         'test_suites': {
@@ -6633,7 +6633,7 @@
     'machines': {
       'devtools_frontend_linux_blink_light_rel': {
         'mixins': [
-          'linux-xenial',
+          'linux-bionic',
         ],
         'test_suites': {
           'gtest_tests': 'devtools_browser_tests',
@@ -6642,7 +6642,7 @@
       },
       'devtools_frontend_linux_blink_rel': {
         'mixins': [
-          'linux-xenial',
+          'linux-bionic',
         ],
         'test_suites': {
           'isolated_scripts': 'devtools_webkit_isolated_scripts',
diff --git a/testing/variations/fieldtrial_testing_config.json b/testing/variations/fieldtrial_testing_config.json
index ceb9e6a..3d2f6b5 100644
--- a/testing/variations/fieldtrial_testing_config.json
+++ b/testing/variations/fieldtrial_testing_config.json
@@ -3181,56 +3181,6 @@
             ]
         }
     ],
-    "DownloadsAsMixedContent": [
-        {
-            "platforms": [
-                "chromeos",
-                "chromeos_lacros",
-                "linux",
-                "mac",
-                "windows"
-            ],
-            "experiments": [
-                {
-                    "name": "Enabled",
-                    "params": {
-                        "BlockExtensionList": "",
-                        "SilentBlockExtensionList": "",
-                        "TreatBlockListAsAllowlist": "false",
-                        "TreatSilentBlockListAsAllowlist": "true",
-                        "TreatWarnListAsAllowlist": "false",
-                        "WarnExtensionList": ""
-                    },
-                    "enable_features": [
-                        "TreatUnsafeDownloadsAsActive"
-                    ]
-                }
-            ]
-        }
-    ],
-    "DownloadsAsMixedContentAndroid": [
-        {
-            "platforms": [
-                "android"
-            ],
-            "experiments": [
-                {
-                    "name": "Enabled",
-                    "params": {
-                        "BlockExtensionList": "",
-                        "SilentBlockExtensionList": "",
-                        "TreatBlockListAsAllowlist": "false",
-                        "TreatSilentBlockListAsAllowlist": "true",
-                        "TreatWarnListAsAllowlist": "false",
-                        "WarnExtensionList": ""
-                    },
-                    "enable_features": [
-                        "TreatUnsafeDownloadsAsActive"
-                    ]
-                }
-            ]
-        }
-    ],
     "DriveFsBidirectionalNativeMessaging": [
         {
             "platforms": [
diff --git a/third_party/blink/common/features.cc b/third_party/blink/common/features.cc
index 0e345c5..7d163b55 100644
--- a/third_party/blink/common/features.cc
+++ b/third_party/blink/common/features.cc
@@ -203,11 +203,6 @@
 #endif
 };
 
-// Enables toggling overwrite mode when insert key is pressed.
-// https://crbug.com/1030231.
-const base::Feature kInsertKeyToggleMode = {"InsertKeyToggleMode",
-                                            base::FEATURE_DISABLED_BY_DEFAULT};
-
 // Enables Raw Clipboard. https://crbug.com/897289.
 const base::Feature kRawClipboard{"RawClipboard",
                                   base::FEATURE_DISABLED_BY_DEFAULT};
diff --git a/third_party/blink/public/common/features.h b/third_party/blink/public/common/features.h
index fd4be05..45eac70 100644
--- a/third_party/blink/public/common/features.h
+++ b/third_party/blink/public/common/features.h
@@ -38,7 +38,6 @@
     kFrequencyCappingForOverlayPopupDetection;
 BLINK_COMMON_EXPORT extern const base::Feature
     kFrequencyCappingForLargeStickyAdDetection;
-BLINK_COMMON_EXPORT extern const base::Feature kInsertKeyToggleMode;
 BLINK_COMMON_EXPORT extern const base::Feature kDisplayLocking;
 BLINK_COMMON_EXPORT extern const base::Feature kJSONModules;
 BLINK_COMMON_EXPORT extern const base::Feature kForceSynchronousHTMLParsing;
diff --git a/third_party/blink/public/mojom/web_feature/web_feature.mojom b/third_party/blink/public/mojom/web_feature/web_feature.mojom
index 8a2ef0d8..4d7d414 100644
--- a/third_party/blink/public/mojom/web_feature/web_feature.mojom
+++ b/third_party/blink/public/mojom/web_feature/web_feature.mojom
@@ -3217,6 +3217,10 @@
   kWebAppWindowControlsOverlay = 3902,
   kPaymentRequestShowWithoutGestureOrToken = 3903,
   kV8Navigator_UpdateAdInterestGroups_Method = 3904,
+  kV8Screens_Onscreenschange_AttributeGetter = 3905,
+  kV8Screens_Onscreenschange_AttributeSetter = 3906,
+  kV8Screens_Oncurrentscreenchange_AttributeGetter = 3907,
+  kV8Screens_Oncurrentscreenchange_AttributeSetter = 3908,
 
   // Add new features immediately above this line. Don't change assigned
   // numbers of any item, and don't reuse removed slots.
diff --git a/third_party/blink/renderer/core/core_initializer.h b/third_party/blink/renderer/core/core_initializer.h
index c21241a..39ea294 100644
--- a/third_party/blink/renderer/core/core_initializer.h
+++ b/third_party/blink/renderer/core/core_initializer.h
@@ -128,7 +128,7 @@
   virtual void NotifyOrientationChanged(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;
+  virtual void DidUpdateScreens(LocalFrame& frame, const ScreenInfos&) = 0;
 
  protected:
   // CoreInitializer is only instantiated by subclass ModulesInitializer.
diff --git a/third_party/blink/renderer/core/editing/commands/editor_command.cc b/third_party/blink/renderer/core/editing/commands/editor_command.cc
index a3c8c822..c1ecb64 100644
--- a/third_party/blink/renderer/core/editing/commands/editor_command.cc
+++ b/third_party/blink/renderer/core/editing/commands/editor_command.cc
@@ -28,7 +28,6 @@
 #include "third_party/blink/renderer/core/editing/commands/editor_command.h"
 
 #include "base/metrics/histogram_functions.h"
-#include "third_party/blink/public/common/features.h"
 #include "third_party/blink/public/platform/platform.h"
 #include "third_party/blink/renderer/core/css/css_property_names.h"
 #include "third_party/blink/renderer/core/css/css_property_value_set.h"
@@ -715,12 +714,7 @@
                                    Event*,
                                    EditorCommandSource,
                                    const String&) {
-  // Overwrite mode is disabled by-default. We only return true
-  // if the flag is enabled explicitly by the user in about:flags page.
-  if (base::FeatureList::IsEnabled(features::kInsertKeyToggleMode)) {
-    frame.GetEditor().ToggleOverwriteModeEnabled();
-    return true;
-  }
+  // Overwrite mode is not supported. See https://crbug.com/1030231.
   // We return false to match the expectation of the ExecCommand.
   return false;
 }
diff --git a/third_party/blink/renderer/core/events/event_type_names.json5 b/third_party/blink/renderer/core/events/event_type_names.json5
index b46b56d4..39b97ef 100644
--- a/third_party/blink/renderer/core/events/event_type_names.json5
+++ b/third_party/blink/renderer/core/events/event_type_names.json5
@@ -90,6 +90,7 @@
     "copy",
     "contentdelete",
     "crossoriginmessage",
+    "currentscreenchange",
     "cuechange",
     "cut",
     "datachannel",
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 f24d5c94..85fd8dd 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
@@ -1490,20 +1490,10 @@
 
   SetWindowSegments(visual_properties.root_widget_window_segments);
 
-  const bool screen_infos_changed =
-      widget_base_->screen_infos() != visual_properties.screen_infos;
-
   widget_base_->UpdateSurfaceAndScreenInfo(
       visual_properties.local_surface_id.value_or(viz::LocalSurfaceId()),
       new_compositor_viewport_pixel_rect, visual_properties.screen_infos);
 
-  if (screen_infos_changed) {
-    LocalFrame* frame = LocalRootImpl()->GetFrame();
-    CoreInitializer::GetInstance().NotifyScreensChanged(
-        *frame, visual_properties.screen_infos);
-    // TODO(crbug.com/1182855): Propagate info and events to remote frames.
-  }
-
   // Store this even when auto-resizing, it is the size of the full viewport
   // used for clipping, and this value is propagated down the Widget
   // hierarchy via the VisualProperties waterfall.
@@ -3833,7 +3823,19 @@
     View()->CancelPagePopup();
   }
 
+  // Update Screens interface data before firing any events. The API is designed
+  // to offer synchronous access to the most up-to-date cached screen
+  // information when a change event is fired.  It is not required but it
+  // is convenient to have all ScreenAdvanced objects be up to date when any
+  // window.screen events are fired as well.
+  LocalFrame* frame = LocalRootImpl()->GetFrame();
+  CoreInitializer::GetInstance().DidUpdateScreens(*frame,
+                                                  widget_base_->screen_infos());
+  // TODO(crbug.com/1182855): Propagate info and events to remote frames.
+
   if (previous_original_screen_info != original_screen_info) {
+    // TODO(enne): http://crbug.com/1202981 only send this event when properties
+    // on Screen (vs anything in ScreenInfo) change.
     local_root_->GetFrame()->DomWindow()->screen()->DispatchEvent(
         *Event::Create(event_type_names::kChange));
 
diff --git a/third_party/blink/renderer/core/layout/ng/grid/ng_grid_layout_algorithm.cc b/third_party/blink/renderer/core/layout/ng/grid/ng_grid_layout_algorithm.cc
index fbd6fe3..615db604 100644
--- a/third_party/blink/renderer/core/layout/ng/grid/ng_grid_layout_algorithm.cc
+++ b/third_party/blink/renderer/core/layout/ng/grid/ng_grid_layout_algorithm.cc
@@ -3093,17 +3093,18 @@
 // the extra offset relative to the start of its containing range.
 LayoutUnit ComputeTrackOffsetInRange(
     const NGGridLayoutAlgorithm::SetGeometry& set_geometry,
-    wtf_size_t range_starting_set_index,
-    wtf_size_t range_set_count,
-    wtf_size_t offset_in_range) {
-  if (!range_set_count)
+    const wtf_size_t range_starting_set_index,
+    const wtf_size_t range_set_count,
+    const wtf_size_t offset_in_range) {
+  if (!range_set_count || !offset_in_range)
     return LayoutUnit();
 
   // To compute the index offset, we have to determine the size of the
-  // tracks in the item's range.
+  // tracks within the grid item's span.
   Vector<std::div_t> track_sizes =
       NGGridLayoutAlgorithm::ComputeTrackSizesInRange(
           set_geometry, range_starting_set_index, range_set_count);
+
   // Calculate how many sets there are from the start of the range to the
   // |offset_in_range|. This division can produce a remainder, which would
   // mean that not all of the sets are repeated the same amount of times from
@@ -3111,8 +3112,7 @@
   const wtf_size_t floor_set_track_count = offset_in_range / range_set_count;
   const wtf_size_t remaining_track_count = offset_in_range % range_set_count;
 
-  // Iterate over the sets and add the sizes of the tracks to the
-  // |index_offset|.
+  // Iterate over the sets and add the sizes of the tracks to |index_offset|.
   LayoutUnit index_offset = set_geometry.gutter_size * offset_in_range;
   for (wtf_size_t track_index = 0; track_index < track_sizes.size();
        ++track_index) {
@@ -3127,41 +3127,84 @@
   return index_offset;
 }
 
+template <bool snap_to_end_of_track>
 LayoutUnit TrackOffset(
     const NGGridLayoutAlgorithmTrackCollection& track_collection,
     const NGGridLayoutAlgorithm::SetGeometry& set_geometry,
-    wtf_size_t range_index,
-    wtf_size_t offset_in_range,
-    bool snap_to_end_of_track) {
-  wtf_size_t range_starting_set_index =
-      track_collection.RangeStartingSetIndex(range_index);
-  wtf_size_t range_track_count = track_collection.RangeTrackCount(range_index);
-  wtf_size_t range_set_count = track_collection.RangeSetCount(range_index);
+    const wtf_size_t range_index,
+    const wtf_size_t offset_in_range) {
+  LayoutUnit track_offset;
 
-  LayoutUnit track_offset = set_geometry.sets[range_starting_set_index].offset;
+  const wtf_size_t range_starting_set_index =
+      track_collection.RangeStartingSetIndex(range_index);
+  const wtf_size_t range_track_count =
+      track_collection.RangeTrackCount(range_index);
+  const wtf_size_t range_set_count =
+      track_collection.RangeSetCount(range_index);
+
   if (offset_in_range == range_track_count) {
+    DCHECK(snap_to_end_of_track);
     track_offset =
         set_geometry.sets[range_starting_set_index + range_set_count].offset;
-    snap_to_end_of_track = true;
   } else {
     DCHECK_LT(offset_in_range, range_track_count);
-    // If an out of flow item starts or ends in the middle of a range, compute
-    // and add the extra offset.
-    track_offset +=
+    DCHECK(offset_in_range || !snap_to_end_of_track);
+    // If an out of flow item starts/ends in the middle of a range, compute and
+    // add the extra offset to the start offset of the range.
+    track_offset =
+        set_geometry.sets[range_starting_set_index].offset +
         ComputeTrackOffsetInRange(set_geometry, range_starting_set_index,
                                   range_set_count, offset_in_range);
   }
+
   // |track_offset| includes the gutter size at the end of the last track,
   // when we snap to the end of last track such gutter size should be removed.
-  // However, we don't want to snap to the end of the previous range or
-  // incorrectly remove gutter size from collapsed ranges.
-  if (snap_to_end_of_track && offset_in_range &&
-      (range_set_count || range_starting_set_index)) {
+  // However, only snap if this range is not collapsed or if it can snap to the
+  // end of the last track in the previous range of the collection.
+  if (snap_to_end_of_track && (range_set_count || range_index))
     track_offset -= set_geometry.gutter_size;
-  }
   return track_offset;
 }
 
+LayoutUnit TrackStartOffset(
+    const NGGridLayoutAlgorithmTrackCollection& track_collection,
+    const NGGridLayoutAlgorithm::SetGeometry& set_geometry,
+    const wtf_size_t range_index,
+    const wtf_size_t offset_in_range) {
+  const wtf_size_t range_track_count =
+      track_collection.RangeTrackCount(range_index);
+
+  if (offset_in_range == range_track_count &&
+      range_index == track_collection.RangeCount() - 1) {
+    // The only case where we allow the offset to be equal to the number of
+    // tracks in the range is for the last range in the collection, which should
+    // match the end line of the implicit grid; snap to the track end instead.
+    return TrackOffset</* snap_to_end_of_track */ true>(
+        track_collection, set_geometry, range_index, offset_in_range);
+  }
+
+  DCHECK_LT(offset_in_range, range_track_count);
+  return TrackOffset</* snap_to_end_of_track */ false>(
+      track_collection, set_geometry, range_index, offset_in_range);
+}
+
+LayoutUnit TrackEndOffset(
+    const NGGridLayoutAlgorithmTrackCollection& track_collection,
+    const NGGridLayoutAlgorithm::SetGeometry& set_geometry,
+    const wtf_size_t range_index,
+    const wtf_size_t offset_in_range) {
+  if (!offset_in_range && !range_index) {
+    // Only allow the offset to be 0 for the first range in the collection,
+    // which is the start line of the implicit grid; don't snap to the end.
+    return TrackOffset</* snap_to_end_of_track */ false>(
+        track_collection, set_geometry, range_index, offset_in_range);
+  }
+
+  DCHECK_GT(offset_in_range, 0u);
+  return TrackOffset</* snap_to_end_of_track */ true>(
+      track_collection, set_geometry, range_index, offset_in_range);
+}
+
 }  // namespace
 
 void NGGridLayoutAlgorithm::ComputeGridItemOffsetAndSize(
@@ -3233,26 +3276,25 @@
 
   // If the start line is defined, the size will be calculated by subtracting
   // the offset at |start_index|; otherwise, use the computed border start.
-  if (item_placement.start_range_index != kNotFound &&
-      item_placement.start_offset_in_range != kNotFound) {
-    *start_offset = TrackOffset(
-        track_collection, set_geometry, item_placement.start_range_index,
-        item_placement.start_offset_in_range, /* snap_to_end_of_track */
-        false);
+  if (item_placement.start_range_index != kNotFound) {
+    DCHECK_NE(item_placement.start_offset_in_range, kNotFound);
+    *start_offset = TrackStartOffset(track_collection, set_geometry,
+                                     item_placement.start_range_index,
+                                     item_placement.start_offset_in_range);
   }
 
   // If the end line is defined, the offset (which can be the offset at the
   // start index or the start border) and the added grid gap after the spanned
   // tracks are subtracted from the offset at the end index.
-  if (item_placement.end_range_index != kNotFound &&
-      item_placement.end_offset_in_range != kNotFound) {
-    end_offset = TrackOffset(
-        track_collection, set_geometry, item_placement.end_range_index,
-        item_placement.end_offset_in_range, /* snap_to_end_of_track */ true);
+  if (item_placement.end_range_index != kNotFound) {
+    DCHECK_NE(item_placement.end_offset_in_range, kNotFound);
+
+    end_offset = TrackEndOffset(track_collection, set_geometry,
+                                item_placement.end_range_index,
+                                item_placement.end_offset_in_range);
     *size = end_offset - *start_offset;
   } else {
-    // By the time we call this method, we should not have indefinite track
-    // sizes.
+    // By the time we call this method, we shouldn't have indefinite tracks.
     DCHECK_EQ(set_geometry.sets.back().last_indefinite_index, kNotFound);
 
     // |start_offset| can be greater than |end_offset| if the track sizes from
diff --git a/third_party/blink/renderer/core/layout/ng/ng_layout_overflow_calculator.cc b/third_party/blink/renderer/core/layout/ng/ng_layout_overflow_calculator.cc
index a615553..6f864b4 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_layout_overflow_calculator.cc
+++ b/third_party/blink/renderer/core/layout/ng/ng_layout_overflow_calculator.cc
@@ -52,7 +52,7 @@
       PhysicalRect child_overflow =
           RecalculateLayoutOverflowForFragment(*box_fragment);
       child_overflow.offset += child.offset;
-      calculator.AddOverflow(child_overflow);
+      calculator.AddOverflow(child_overflow, /* child_is_fragmentainer */ true);
     } else {
       calculator.AddChild(*box_fragment, child.offset);
     }
diff --git a/third_party/blink/renderer/core/layout/ng/ng_layout_overflow_calculator.h b/third_party/blink/renderer/core/layout/ng/ng_layout_overflow_calculator.h
index 69d859d..363a341 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_layout_overflow_calculator.h
+++ b/third_party/blink/renderer/core/layout/ng/ng_layout_overflow_calculator.h
@@ -41,7 +41,7 @@
                 PhysicalOffset offset) {
     PhysicalRect child_overflow = LayoutOverflowForPropagation(child_fragment);
     child_overflow.offset += offset;
-    AddOverflow(child_overflow);
+    AddOverflow(child_overflow, child_fragment.IsFragmentainerBox());
   }
 
   // Adds layout-overflow from fragment-items.
@@ -61,11 +61,14 @@
   PhysicalRect LayoutOverflowForPropagation(
       const NGPhysicalBoxFragment& child_fragment);
 
-  void AddOverflow(PhysicalRect child_overflow) {
+  void AddOverflow(PhysicalRect child_overflow,
+                   bool child_is_fragmentainer = false) {
     if (is_scroll_container_)
       child_overflow = AdjustOverflowForScrollOrigin(child_overflow);
 
-    if (!child_overflow.IsEmpty())
+    // A fragmentainer may result in an overflow, even if it is empty. For
+    // example, an overflow as a result of a non-zero column gap.
+    if (!child_overflow.IsEmpty() || child_is_fragmentainer)
       layout_overflow_.UniteEvenIfEmpty(child_overflow);
   }
 
diff --git a/third_party/blink/renderer/modules/media_controls/elements/media_control_popup_menu_element.cc b/third_party/blink/renderer/modules/media_controls/elements/media_control_popup_menu_element.cc
index 5485579..46973bf 100644
--- a/third_party/blink/renderer/modules/media_controls/elements/media_control_popup_menu_element.cc
+++ b/third_party/blink/renderer/modules/media_controls/elements/media_control_popup_menu_element.cc
@@ -150,10 +150,10 @@
     event.stopPropagation();
     event.SetDefaultHandled();
   } else if (event.type() == event_type_names::kFocus) {
-    // When popup menu gain focus from scrolling, switch focus
-    // back to the last focused item in the menu
-    DCHECK(last_focused_element_);
-    last_focused_element_->focus();
+    // When the popup menu gains focus from scrolling, switch focus
+    // back to the last focused item in the menu.
+    if (last_focused_element_)
+      last_focused_element_->focus();
   }
 
   MediaControlDivElement::DefaultEventHandler(event);
diff --git a/third_party/blink/renderer/modules/media_controls/elements/media_control_popup_menu_element.h b/third_party/blink/renderer/modules/media_controls/elements/media_control_popup_menu_element.h
index ed101d0..720bc2c 100644
--- a/third_party/blink/renderer/modules/media_controls/elements/media_control_popup_menu_element.h
+++ b/third_party/blink/renderer/modules/media_controls/elements/media_control_popup_menu_element.h
@@ -56,6 +56,9 @@
   void CloseFromKeyboard();
 
   Member<EventListener> event_listener_;
+  // |last_focused_element_| is used to return focus to the proper element
+  // within the media controls popup menu, after the user finishes interacting
+  // with the popup's scrollbar.
   Member<Element> last_focused_element_;
 };
 
diff --git a/third_party/blink/renderer/modules/modules_initializer.cc b/third_party/blink/renderer/modules/modules_initializer.cc
index 82b5b1e..4078634 100644
--- a/third_party/blink/renderer/modules/modules_initializer.cc
+++ b/third_party/blink/renderer/modules/modules_initializer.cc
@@ -372,14 +372,14 @@
       ->NotifyOrientationChanged();
 }
 
-void ModulesInitializer::NotifyScreensChanged(LocalFrame& frame,
-                                              const ScreenInfos& screen_infos) {
+void ModulesInitializer::DidUpdateScreens(LocalFrame& frame,
+                                          const ScreenInfos& screen_infos) {
   auto* window = frame.DomWindow();
   if (auto* supplement =
           Supplement<LocalDOMWindow>::From<WindowScreens>(window)) {
     // screens() may be null if permission has not been granted.
     if (auto* screens = supplement->screens()) {
-      screens->ScreenInfosChanged(window, screen_infos);
+      screens->UpdateScreenInfos(window, screen_infos);
     }
   }
 }
diff --git a/third_party/blink/renderer/modules/modules_initializer.h b/third_party/blink/renderer/modules/modules_initializer.h
index 5353144..4769e54 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&, const ScreenInfos&) override;
+  void DidUpdateScreens(LocalFrame&, const ScreenInfos&) override;
 };
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/modules/screen_enumeration/screen_advanced.h b/third_party/blink/renderer/modules/screen_enumeration/screen_advanced.h
index 8ead934c..c02c5fd 100644
--- a/third_party/blink/renderer/modules/screen_enumeration/screen_advanced.h
+++ b/third_party/blink/renderer/modules/screen_enumeration/screen_advanced.h
@@ -44,9 +44,10 @@
 
   // Not web-exposed; for internal usage only (see Screen).
   int64_t DisplayId() const override;
+  void UpdateDisplayId(int64_t display_id) { display_id_ = display_id; }
 
  private:
-  const int64_t display_id_;
+  int64_t display_id_;
 };
 
 }  // 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 9f96bd0..50852f0 100644
--- a/third_party/blink/renderer/modules/screen_enumeration/screens.cc
+++ b/third_party/blink/renderer/modules/screen_enumeration/screens.cc
@@ -5,7 +5,6 @@
 #include "third_party/blink/renderer/modules/screen_enumeration/screens.h"
 
 #include "third_party/blink/public/common/widget/screen_info.h"
-#include "third_party/blink/public/common/widget/screen_infos.h"
 #include "third_party/blink/renderer/core/dom/events/event.h"
 #include "third_party/blink/renderer/core/frame/local_dom_window.h"
 #include "third_party/blink/renderer/core/frame/local_frame.h"
@@ -19,7 +18,7 @@
     : ExecutionContextLifecycleObserver(window) {
   LocalFrame* frame = window->GetFrame();
   const auto& screen_infos = frame->GetChromeClient().GetScreenInfos(*frame);
-  ScreenInfosChanged(window, screen_infos);
+  UpdateScreenInfos(window, screen_infos);
 }
 
 const HeapVector<Member<ScreenAdvanced>>& Screens::screens() const {
@@ -30,17 +29,13 @@
   if (!DomWindow())
     return nullptr;
 
-  DCHECK(!screens_.IsEmpty());
+  if (screens_.IsEmpty())
+    return nullptr;
 
-  LocalFrame* frame = DomWindow()->GetFrame();
-  const auto& current_info = frame->GetChromeClient().GetScreenInfo(*frame);
-  for (const auto& screen : screens_) {
-    if (screen->DisplayId() == current_info.display_id)
-      return screen;
-  }
-
-  NOTREACHED() << "No screen found matching the current ScreenInfo id";
-  return nullptr;
+  auto* it = base::ranges::find(screens_, current_display_id_,
+                                &ScreenAdvanced::DisplayId);
+  DCHECK(it != screens_.end());
+  return *it;
 }
 
 const AtomicString& Screens::InterfaceName() const {
@@ -61,8 +56,12 @@
   ExecutionContextLifecycleObserver::Trace(visitor);
 }
 
-void Screens::ScreenInfosChanged(LocalDOMWindow* window,
-                                 const ScreenInfos& infos) {
+void Screens::UpdateScreenInfos(LocalDOMWindow* window,
+                                const ScreenInfos& new_infos) {
+  // Expect that all updates contain a non-zero set of screens.
+  DCHECK(!new_infos.screen_infos.empty());
+
+  // (1) Detect if the set of screens has changed, and update screens_.
   bool added_or_removed = false;
 
   // O(displays) should be small, so O(n^2) check in both directions
@@ -71,7 +70,7 @@
   // 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(),
+    if (base::Contains(new_infos.screen_infos, screens_[i]->DisplayId(),
                        &ScreenInfo::display_id)) {
       ++i;
     } else {
@@ -83,7 +82,7 @@
 
   // Check if any screens have been added, and append them to the end of
   // screens_.
-  for (const auto& info : infos.screen_infos) {
+  for (const auto& info : new_infos.screen_infos) {
     if (!base::Contains(screens_, info.display_id,
                         &ScreenAdvanced::DisplayId)) {
       screens_.push_back(
@@ -92,9 +91,53 @@
     }
   }
 
-  if (added_or_removed) {
-    DispatchEvent(*Event::Create(event_type_names::kChange));
+  // Update current_display_id_ so that currentScreen() is up to date
+  // before we send out any events.
+  current_display_id_ = new_infos.current_display_id;
+
+  // (2) At this point, all data strutures are up to date.
+  // screens_ has the current set of screens.
+  // current_screen_ has new values pushed to it.
+  // (prior to this function) individual ScreenAdvanced objects have new values.
+
+  // (3) Send a change event if the current screen has changed.
+  if (prev_screen_infos_.screen_infos.empty() ||
+      prev_screen_infos_.current() != new_infos.current()) {
+    DispatchEvent(*Event::Create(event_type_names::kCurrentscreenchange));
   }
+
+  // (4) Send a change event if the set of screens has changed.
+  if (added_or_removed) {
+    DispatchEvent(*Event::Create(event_type_names::kScreenschange));
+  }
+
+  // (5) Send change events to individual screens if they have changed.
+  // It's not guaranteed that screen_infos are ordered, so for each screen
+  // find the info that corresponds to it in old_info and new_infos.
+  for (const auto& screen : screens_) {
+    auto id = screen->DisplayId();
+    auto new_it =
+        base::ranges::find(new_infos.screen_infos, id, &ScreenInfo::display_id);
+    DCHECK(new_it != new_infos.screen_infos.end());
+    auto old_it = base::ranges::find(prev_screen_infos_.screen_infos, id,
+                                     &ScreenInfo::display_id);
+    if (old_it != prev_screen_infos_.screen_infos.end() && *old_it != *new_it) {
+      // TODO(enne): http://crbug.com/1202981 only send this event when
+      // properties on ScreenAdvanced (vs anything in ScreenInfo) change.
+      screen->DispatchEvent(*Event::Create(event_type_names::kChange));
+    }
+  }
+
+  // (6) Store screen infos for change comparison next time.
+  //
+  // Aside: Because ScreenAdvanced is a "live" thin wrapper over the ScreenInfo
+  // object owned by WidgetBase, WidgetBase's copy needs to be updated
+  // in UpdateSurfaceAndScreenInfo prior to this UpdateScreenInfos call so that
+  // when the events are fired, the live data is not stale.  Therefore, this
+  // class needs to hold onto the "previous" info so that it knows which pieces
+  // of data have changed, as at a higher level the old data has already been
+  // rewritten with the new.
+  prev_screen_infos_ = new_infos;
 }
 
 }  // 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 4283ba6..9d572af 100644
--- a/third_party/blink/renderer/modules/screen_enumeration/screens.h
+++ b/third_party/blink/renderer/modules/screen_enumeration/screens.h
@@ -5,6 +5,7 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_SCREEN_ENUMERATION_SCREENS_H_
 #define THIRD_PARTY_BLINK_RENDERER_MODULES_SCREEN_ENUMERATION_SCREENS_H_
 
+#include "third_party/blink/public/common/widget/screen_infos.h"
 #include "third_party/blink/renderer/core/dom/events/event_target.h"
 #include "third_party/blink/renderer/core/execution_context/execution_context_lifecycle_observer.h"
 #include "third_party/blink/renderer/modules/modules_export.h"
@@ -15,7 +16,6 @@
 
 class LocalDOMWindow;
 class ScreenAdvanced;
-struct ScreenInfos;
 
 // Interface exposing multi-screen information.
 // https://github.com/webscreens/window-placement
@@ -29,7 +29,8 @@
   // Web-exposed interface:
   const HeapVector<Member<ScreenAdvanced>>& screens() const;
   ScreenAdvanced* currentScreen() const;
-  DEFINE_ATTRIBUTE_EVENT_LISTENER(change, kChange)
+  DEFINE_ATTRIBUTE_EVENT_LISTENER(screenschange, kScreenschange)
+  DEFINE_ATTRIBUTE_EVENT_LISTENER(currentscreenchange, kCurrentscreenchange)
 
   // EventTargetWithInlineData:
   const AtomicString& InterfaceName() const override;
@@ -40,10 +41,14 @@
 
   void Trace(Visitor*) const override;
 
-  // Called when the underlying multi-screen information changes.
-  void ScreenInfosChanged(LocalDOMWindow* window, const ScreenInfos& infos);
+  // Called when there is a visual property update with potentially new
+  // multi-screen information.
+  void UpdateScreenInfos(LocalDOMWindow* window, const ScreenInfos& new_infos);
 
  private:
+  // The ScreenInfos sent by the previous UpdateScreenInfos call.
+  ScreenInfos prev_screen_infos_;
+  int64_t current_display_id_ = ScreenInfo::kInvalidDisplayId;
   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 854b213..f822d3f3 100644
--- a/third_party/blink/renderer/modules/screen_enumeration/screens.idl
+++ b/third_party/blink/renderer/modules/screen_enumeration/screens.idl
@@ -15,7 +15,11 @@
   // Current screen reference; corresponds with window.screen.
   readonly attribute ScreenAdvanced currentScreen;
 
-  // Change event fired when the set of screens 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;
+  [HighEntropy, Measure] attribute EventHandler onscreenschange;
+
+  // Change event fired when any attribute on the currentScreen changes,
+  // including when the window is moved to another screen.
+  [HighEntropy, Measure] attribute EventHandler oncurrentscreenchange;
 };
diff --git a/third_party/blink/renderer/platform/graphics/canvas_resource.cc b/third_party/blink/renderer/platform/graphics/canvas_resource.cc
index c7e8536..66cd920 100644
--- a/third_party/blink/renderer/platform/graphics/canvas_resource.cc
+++ b/third_party/blink/renderer/platform/graphics/canvas_resource.cc
@@ -1219,11 +1219,15 @@
   if (!context_provider_wrapper_)
     return;
 
-  auto* raster_interface =
-      context_provider_wrapper_->ContextProvider()->RasterInterface();
-  DCHECK(raster_interface);
-  raster_interface->EndSharedImageAccessDirectCHROMIUM(back_buffer_texture_id_);
-  raster_interface->DeleteGpuRasterTexture(back_buffer_texture_id_);
+  if (!use_oop_rasterization_) {
+    auto* raster_interface =
+        context_provider_wrapper_->ContextProvider()->RasterInterface();
+    DCHECK(raster_interface);
+    raster_interface->EndSharedImageAccessDirectCHROMIUM(
+        back_buffer_texture_id_);
+    raster_interface->DeleteGpuRasterTexture(back_buffer_texture_id_);
+  }
+
   // No synchronization is needed here because the GL SharedImageRepresentation
   // will keep the backing alive on the service until the textures are deleted.
   auto* sii =
@@ -1294,7 +1298,10 @@
     SkFilterQuality filter_quality)
     : CanvasResource(std::move(provider), filter_quality, params),
       context_provider_wrapper_(std::move(context_provider_wrapper)),
-      size_(size) {
+      size_(size),
+      use_oop_rasterization_(context_provider_wrapper_->ContextProvider()
+                                 ->GetCapabilities()
+                                 .supports_oop_raster) {
   if (!context_provider_wrapper_)
     return;
 
@@ -1303,6 +1310,11 @@
                    gpu::SHARED_IMAGE_USAGE_GLES2_FRAMEBUFFER_HINT |
                    gpu::SHARED_IMAGE_USAGE_SCANOUT;
 
+  if (use_oop_rasterization_) {
+    usage = usage | gpu::SHARED_IMAGE_USAGE_RASTER |
+            gpu::SHARED_IMAGE_USAGE_OOP_RASTERIZATION;
+  }
+
   auto* sii =
       context_provider_wrapper_->ContextProvider()->SharedImageInterface();
   DCHECK(sii);
@@ -1321,6 +1333,11 @@
   DCHECK(raster_interface);
   raster_interface->WaitSyncTokenCHROMIUM(sync_token_.GetData());
 
+  // In OOPR mode we use mailboxes directly. We early out here because
+  // we don't need a texture id, as access is managed in the gpu process.
+  if (use_oop_rasterization_)
+    return;
+
   back_buffer_texture_id_ =
       raster_interface->CreateAndConsumeForGpuRaster(back_buffer_mailbox_);
   raster_interface->BeginSharedImageAccessDirectCHROMIUM(
diff --git a/third_party/blink/renderer/platform/graphics/canvas_resource.h b/third_party/blink/renderer/platform/graphics/canvas_resource.h
index fdaa93d8..2d0d07e2 100644
--- a/third_party/blink/renderer/platform/graphics/canvas_resource.h
+++ b/third_party/blink/renderer/platform/graphics/canvas_resource.h
@@ -650,6 +650,7 @@
   GLenum TextureTarget() const final { return GL_TEXTURE_2D; }
 
   GLuint GetBackBufferTextureId() const { return back_buffer_texture_id_; }
+  const gpu::Mailbox& GetBackBufferMailbox() { return back_buffer_mailbox_; }
 
   void PresentSwapChain();
   const gpu::Mailbox& GetOrCreateGpuMailbox(MailboxSyncMode) override;
@@ -675,6 +676,7 @@
   gpu::Mailbox back_buffer_mailbox_;
   GLuint back_buffer_texture_id_ = 0u;
   gpu::SyncToken sync_token_;
+  const bool use_oop_rasterization_;
 
   bool is_origin_clean_ = true;
 };
diff --git a/third_party/blink/renderer/platform/graphics/canvas_resource_provider.cc b/third_party/blink/renderer/platform/graphics/canvas_resource_provider.cc
index b9267d0..651f981 100644
--- a/third_party/blink/renderer/platform/graphics/canvas_resource_provider.cc
+++ b/third_party/blink/renderer/platform/graphics/canvas_resource_provider.cc
@@ -257,10 +257,10 @@
   bool IsAccelerated() const final { return is_accelerated_; }
   bool SupportsDirectCompositing() const override { return true; }
   bool IsValid() const final {
-    if (use_oop_rasterization_)
-      return !IsGpuContextLost();
-    else
+    if (!use_oop_rasterization_)
       return GetSkSurface() && !IsGpuContextLost();
+    else
+      return !IsGpuContextLost();
   }
 
   bool SupportsSingleBuffering() const override {
@@ -343,17 +343,7 @@
       CanvasResourceProvider::RestoreBackBuffer(image);
       return;
     }
-
-    DCHECK_EQ(image.height(), Size().Height());
-    DCHECK_EQ(image.width(), Size().Width());
-
-    auto sk_image = image.GetSwSkImage();
-    DCHECK(sk_image);
-    SkPixmap map;
-    // We know this SkImage is software backed because it's guaranteed by
-    // PaintImage::GetSwSkImage above
-    sk_image->peekPixels(&map);
-    WritePixels(map.info(), map.addr(), map.rowBytes(), /*x=*/0, /*y=*/0);
+    RestoreBackBufferOOP(image);
   }
 
  protected:
@@ -512,42 +502,10 @@
       return;
     }
     WillDrawInternal(true);
-    gpu::raster::RasterInterface* ri = RasterInterface();
-    SkColor background_color =
-        ColorParams().GetSkAlphaType() == kOpaque_SkAlphaType
-            ? SK_ColorBLACK
-            : SK_ColorTRANSPARENT;
-
-    auto list = base::MakeRefCounted<cc::DisplayItemList>(
-        cc::DisplayItemList::kTopLevelDisplayItemList);
-
-    list->StartPaint();
-    list->push<cc::DrawRecordOp>(std::move(last_recording));
-    list->EndPaintOfUnpaired(gfx::Rect(Size().Width(), Size().Height()));
-    list->Finalize();
-
-    gfx::Size size(Size().Width(), Size().Height());
-    size_t max_op_size_hint =
-        gpu::raster::RasterInterface::kDefaultMaxOpSizeHint;
-    gfx::Rect full_raster_rect(Size().Width(), Size().Height());
-    gfx::Rect playback_rect(Size().Width(), Size().Height());
-    gfx::Vector2dF post_translate(0.f, 0.f);
-    gfx::Vector2dF post_scale(1.f, 1.f);
-
     const bool needs_clear = !is_cleared_;
     is_cleared_ = true;
-
-    ri->BeginRasterCHROMIUM(
-        background_color, needs_clear, /*msaa_sample_count=*/0,
-        /*can_use_lcd_text=*/false, ColorParams().GetStorageGfxColorSpace(),
-        resource()->GetOrCreateGpuMailbox(kUnverifiedSyncToken).name);
-
-    ri->RasterCHROMIUM(list.get(), GetOrCreateCanvasImageProvider(), size,
-                       full_raster_rect, playback_rect, post_translate,
-                       post_scale, false /* requires_clear */,
-                       &max_op_size_hint);
-
-    ri->EndRasterCHROMIUM();
+    RasterRecordOOP(last_recording, needs_clear,
+                    resource()->GetOrCreateGpuMailbox(kUnverifiedSyncToken));
   }
 
   bool ShouldReplaceTargetBuffer(
@@ -775,7 +733,11 @@
                                params,
                                true /*is_origin_top_left*/,
                                std::move(context_provider_wrapper),
-                               std::move(resource_dispatcher)) {
+                               std::move(resource_dispatcher)),
+        use_oop_rasterization_(ContextProviderWrapper()
+                                   ->ContextProvider()
+                                   ->GetCapabilities()
+                                   .supports_oop_raster) {
     resource_ = CanvasResourceSwapChain::Create(
         Size(), ColorParams(), ContextProviderWrapper(), CreateWeakPtr(),
         FilterQuality());
@@ -786,7 +748,13 @@
   }
   ~CanvasResourceProviderSwapChain() override = default;
 
-  bool IsValid() const final { return GetSkSurface() && !IsGpuContextLost(); }
+  bool IsValid() const final {
+    if (!use_oop_rasterization_)
+      return GetSkSurface() && !IsGpuContextLost();
+    else
+      return !IsGpuContextLost();
+  }
+
   bool IsAccelerated() const final { return true; }
   bool SupportsDirectCompositing() const override { return true; }
   bool SupportsSingleBuffering() const override { return true; }
@@ -849,19 +817,67 @@
         ColorParams().GetSkColorSpace(), &props);
   }
 
+  void RasterRecord(sk_sp<cc::PaintRecord> last_recording) override {
+    TRACE_EVENT0("blink", "CanvasResourceProviderSwapChain::RasterRecord");
+    if (!use_oop_rasterization_) {
+      CanvasResourceProvider::RasterRecord(std::move(last_recording));
+      return;
+    }
+    WillDraw();
+    RasterRecordOOP(last_recording, initial_needs_clear_,
+                    resource_->GetBackBufferMailbox());
+    initial_needs_clear_ = false;
+  }
+
+  void RestoreBackBuffer(const cc::PaintImage& image) override {
+    TRACE_EVENT0("blink", "CanvasResourceProviderSwapChain::RestoreBackBuffer");
+    if (!use_oop_rasterization_) {
+      CanvasResourceProvider::RestoreBackBuffer(image);
+      return;
+    }
+    RestoreBackBufferOOP(image);
+  }
+
+  bool WritePixels(const SkImageInfo& orig_info,
+                   const void* pixels,
+                   size_t row_bytes,
+                   int x,
+                   int y) override {
+    if (!use_oop_rasterization_) {
+      return CanvasResourceProvider::WritePixels(orig_info, pixels, row_bytes,
+                                                 x, y);
+    }
+
+    TRACE_EVENT0("blink", "CanvasResourceProviderSwapChain::WritePixels");
+    if (IsGpuContextLost())
+      return false;
+
+    WillDraw();
+    RasterInterface()->WritePixels(resource_->GetBackBufferMailbox(), x, y,
+                                   GetBackingTextureTarget(), row_bytes,
+                                   orig_info, pixels);
+    return true;
+  }
+
   void FlushIfNeeded() {
     if (needs_flush_) {
       // This only flushes recorded draw ops.
       FlushCanvas();
       // Call flushAndSubmit() explicitly so that any non-draw-op rendering by
       // Skia is flushed to GL.  This is needed specifically for WritePixels().
-      GetGrContext()->flushAndSubmit();
+      if (!use_oop_rasterization_)
+        GetGrContext()->flushAndSubmit();
+
       needs_flush_ = false;
     }
   }
 
   bool needs_present_ = false;
   bool needs_flush_ = false;
+  const bool use_oop_rasterization_;
+  // This only matters for the initial backbuffer mailbox, since the frontbuffer
+  // will always have the back texture copied to it prior to any new commands.
+  bool initial_needs_clear_ = true;
   scoped_refptr<CanvasResourceSwapChain> resource_;
 };
 
@@ -1353,6 +1369,43 @@
   GetSkSurface()->flushAndSubmit();
 }
 
+void CanvasResourceProvider::RasterRecordOOP(
+    sk_sp<cc::PaintRecord> last_recording,
+    bool needs_clear,
+    gpu::Mailbox mailbox) {
+  gpu::raster::RasterInterface* ri = RasterInterface();
+  SkColor background_color =
+      ColorParams().GetSkAlphaType() == kOpaque_SkAlphaType
+          ? SK_ColorBLACK
+          : SK_ColorTRANSPARENT;
+
+  auto list = base::MakeRefCounted<cc::DisplayItemList>(
+      cc::DisplayItemList::kTopLevelDisplayItemList);
+
+  list->StartPaint();
+  list->push<cc::DrawRecordOp>(std::move(last_recording));
+  list->EndPaintOfUnpaired(gfx::Rect(Size().Width(), Size().Height()));
+  list->Finalize();
+
+  gfx::Size size(Size().Width(), Size().Height());
+  size_t max_op_size_hint = gpu::raster::RasterInterface::kDefaultMaxOpSizeHint;
+  gfx::Rect full_raster_rect(Size().Width(), Size().Height());
+  gfx::Rect playback_rect(Size().Width(), Size().Height());
+  gfx::Vector2dF post_translate(0.f, 0.f);
+  gfx::Vector2dF post_scale(1.f, 1.f);
+
+  ri->BeginRasterCHROMIUM(
+      background_color, needs_clear, /*msaa_sample_count=*/0,
+      /*can_use_lcd_text=*/false, ColorParams().GetStorageGfxColorSpace(),
+      mailbox.name);
+
+  ri->RasterCHROMIUM(list.get(), GetOrCreateCanvasImageProvider(), size,
+                     full_raster_rect, playback_rect, post_translate,
+                     post_scale, false /* requires_clear */, &max_op_size_hint);
+
+  ri->EndRasterCHROMIUM();
+}
+
 bool CanvasResourceProvider::IsGpuContextLost() const {
   if (type_ == kSkiaDawnSharedImage) {
     return false;
@@ -1529,6 +1582,19 @@
   skia_canvas_->drawImage(image, 0, 0, SkSamplingOptions(), &copy_paint);
 }
 
+void CanvasResourceProvider::RestoreBackBufferOOP(const cc::PaintImage& image) {
+  DCHECK_EQ(image.height(), Size().Height());
+  DCHECK_EQ(image.width(), Size().Width());
+
+  auto sk_image = image.GetSwSkImage();
+  DCHECK(sk_image);
+  SkPixmap map;
+  // We know this SkImage is software backed because it's guaranteed by
+  // PaintImage::GetSwSkImage above
+  sk_image->peekPixels(&map);
+  WritePixels(map.info(), map.addr(), map.rowBytes(), /*x=*/0, /*y=*/0);
+}
+
 bool CanvasResourceProvider::HasRecordedDrawOps() const {
   return recorder_ && recorder_->ListHasDrawOps();
 }
diff --git a/third_party/blink/renderer/platform/graphics/canvas_resource_provider.h b/third_party/blink/renderer/platform/graphics/canvas_resource_provider.h
index 708db3ad..f3448ac8 100644
--- a/third_party/blink/renderer/platform/graphics/canvas_resource_provider.h
+++ b/third_party/blink/renderer/platform/graphics/canvas_resource_provider.h
@@ -272,6 +272,11 @@
   // change.
   cc::PaintImage MakeImageSnapshot();
   virtual void RasterRecord(sk_sp<cc::PaintRecord>);
+  void RasterRecordOOP(sk_sp<cc::PaintRecord> last_recording,
+                       bool needs_clear,
+                       gpu::Mailbox mailbox);
+  void RestoreBackBufferOOP(const cc::PaintImage&);
+
   CanvasImageProvider* GetOrCreateCanvasImageProvider();
   void TearDownSkSurface();
 
diff --git a/third_party/blink/renderer/platform/runtime_enabled_features.json5 b/third_party/blink/renderer/platform/runtime_enabled_features.json5
index a650318..8f79a186 100644
--- a/third_party/blink/renderer/platform/runtime_enabled_features.json5
+++ b/third_party/blink/renderer/platform/runtime_enabled_features.json5
@@ -1178,7 +1178,8 @@
     },
     {
       name: "LayoutNGReplaced",
-      depends_on: ["LayoutNG"]
+      depends_on: ["LayoutNG"],
+      status: "stable",
     },
     {
       name: "LayoutNGTable",
diff --git a/third_party/blink/web_tests/TestExpectations b/third_party/blink/web_tests/TestExpectations
index 4174f663..859b474 100644
--- a/third_party/blink/web_tests/TestExpectations
+++ b/third_party/blink/web_tests/TestExpectations
@@ -548,7 +548,6 @@
 crbug.com/711807 external/wpt/css/CSS2/normal-flow/max-width-applies-to-006.xht [ Failure ]
 crbug.com/711807 external/wpt/css/CSS2/normal-flow/replaced-intrinsic-001.xht [ Failure ]
 crbug.com/711807 external/wpt/css/CSS2/normal-flow/replaced-intrinsic-002.xht [ Failure ]
-crbug.com/1114280 external/wpt/css/CSS2/normal-flow/replaced-intrinsic-003.xht [ Failure ]
 
 #### external/wpt/css/css-position
 crbug.com/752022 external/wpt/css/css-position/sticky/position-sticky-offset-overflow.html [ Failure ]
@@ -558,18 +557,6 @@
 crbug.com/1204827 external/wpt/css/css-sizing/aspect-ratio/flex-aspect-ratio-004.html [ Failure ]
 crbug.com/1164135 external/wpt/css/css-sizing/aspect-ratio/flex-aspect-ratio-025.html [ Failure ]
 crbug.com/1164135 external/wpt/css/css-sizing/aspect-ratio/flex-aspect-ratio-026.html [ Failure ]
-crbug.com/1114280 external/wpt/css/css-sizing/aspect-ratio/replaced-element-034.html [ Failure ]
-crbug.com/1135287 external/wpt/css/css-sizing/image-min-max-content-intrinsic-size-change-001.html [ Failure ]
-crbug.com/1135287 external/wpt/css/css-sizing/image-min-max-content-intrinsic-size-change-002.html [ Failure ]
-crbug.com/1135287 external/wpt/css/css-sizing/image-min-max-content-intrinsic-size-change-003.html [ Failure ]
-crbug.com/1135287 external/wpt/css/css-sizing/image-min-max-content-intrinsic-size-change-004.html [ Failure ]
-crbug.com/1135287 external/wpt/css/css-sizing/image-min-max-content-intrinsic-size-change-005.html [ Failure ]
-crbug.com/1135287 external/wpt/css/css-sizing/image-min-max-content-intrinsic-size-change-006.html [ Failure ]
-crbug.com/1135287 external/wpt/css/css-sizing/image-min-max-content-intrinsic-size-change-007.html [ Failure ]
-crbug.com/1135287 external/wpt/css/css-sizing/image-min-max-content-intrinsic-size-change-008.html [ Failure ]
-crbug.com/1114280 external/wpt/css/css-sizing/replaced-aspect-ratio-stretch-fit-001.html [ Failure ]
-crbug.com/1114280 external/wpt/css/css-sizing/replaced-aspect-ratio-stretch-fit-002.html [ Failure ]
-crbug.com/1114280 external/wpt/css/css-sizing/replaced-aspect-ratio-stretch-fit-003.html [ Failure ]
 
 crbug.com/1008951 external/wpt/css/css-text-decor/text-decoration-subelements-002.html [ Failure ]
 crbug.com/1008951 external/wpt/css/css-text-decor/text-decoration-subelements-003.html [ Failure ]
@@ -822,8 +809,9 @@
 crbug.com/470421 external/wpt/css/css-sizing/aspect-ratio/flex-aspect-ratio-021.html [ Failure ]
 crbug.com/470421 external/wpt/css/css-sizing/aspect-ratio/flex-aspect-ratio-022.html [ Failure ]
 
-# Correct min/max content sizes for a replaced element wont be supported until ReplacedNG.
-crbug.com/1114280 external/wpt/css/css-flexbox/aspect-ratio-intrinsic-size-006.html [ Failure ]
+# Bad test expectations, but we aren't sure if web compatible yet.
+crbug.com/1114280 external/wpt/css/css-flexbox/flexbox-min-width-auto-005.html [ Failure ]
+crbug.com/1114280 external/wpt/css/css-flexbox/flexbox-min-width-auto-006.html [ Failure ]
 
 # [css-lists]
 crbug.com/1123457 external/wpt/css/css-lists/counter-list-item-2.html [ Failure ]
@@ -1099,7 +1087,6 @@
 crbug.com/1131125 virtual/layout_ng_block_frag/fast/multicol/nested-with-forced-breaks-in-eariler-rows.html [ Failure ]
 crbug.com/829028 virtual/layout_ng_block_frag/fast/multicol/nested-with-padding.html [ Failure ]
 crbug.com/1131125 virtual/layout_ng_block_frag/fast/multicol/newmulticol/breaks-3-columns-3.html [ Failure ]
-crbug.com/829028 virtual/layout_ng_block_frag/fast/multicol/overflowing-columns-large-gaps.html [ Failure ]
 crbug.com/829028 virtual/layout_ng_block_frag/fast/multicol/span/outer-column-break-after-inner-spanner-2.html [ Failure ]
 crbug.com/829028 virtual/layout_ng_block_frag/fast/multicol/static-child-becomes-fixedpos.html [ Failure ]
 crbug.com/1079031 virtual/layout_ng_block_frag/fast/multicol/tall-line-in-short-block.html [ Failure ]
@@ -1671,7 +1658,6 @@
 crbug.com/1049599 [ Linux ] virtual/threaded-prefer-compositing/fast/scrolling/events/overscroll-event-fired-to-element-with-overscroll-behavior.html [ Pass Failure Timeout ]
 crbug.com/1049641 http/tests/devtools/sources/debugger/js-with-inline-stylesheets.js [ Pass Failure ]
 crbug.com/1049641 [ Debug ] http/tests/devtools/sources/debugger-breakpoints/set-breakpoint.js [ Pass Failure ]
-crbug.com/1049641 [ Debug ] http/tests/devtools/sources/debugger-breakpoints/set-conditional-breakpoint.js [ Pass Failure ]
 
 # Sheriff 2019-08-19
 crbug.com/626703 [ Win7 ] external/wpt/html/dom/documents/resource-metadata-management/document-lastModified-01.html [ Failure ]
@@ -4005,7 +3991,7 @@
 crbug.com/1045599 external/wpt/css/css-grid/grid-definition/grid-auto-repeat-dynamic-001.html [ Failure ]
 crbug.com/1045599 external/wpt/css/css-grid/grid-definition/grid-auto-repeat-dynamic-003.html [ Failure ]
 crbug.com/1045599 external/wpt/css/css-grid/grid-definition/grid-repeat-max-width-001.html [ Failure ]
-crbug.com/1114280 external/wpt/css/css-grid/grid-items/aspect-ratio-004.html [ Failure ]
+crbug.com/1045599 external/wpt/css/css-grid/grid-items/aspect-ratio-004.html [ Failure ]
 crbug.com/1053825 external/wpt/css/css-grid/grid-model/grid-overflow-padding-001.html [ Failure ]
 crbug.com/1053825 external/wpt/css/css-grid/grid-model/grid-overflow-padding-002.html [ Failure ]
 crbug.com/1053825 external/wpt/css/css-grid/grid-model/grid-areas-overflowing-grid-container-001.html [ Failure ]
@@ -4108,6 +4094,7 @@
 virtual/layout-ng-grid/external/wpt/css/css-grid/grid-definition/grid-auto-repeat-aspect-ratio-002.html [ Pass ]
 virtual/layout-ng-grid/external/wpt/css/css-grid/grid-definition/grid-auto-repeat-dynamic-001.html [ Pass ]
 virtual/layout-ng-grid/external/wpt/css/css-grid/grid-definition/grid-auto-repeat-dynamic-003.html [ Pass ]
+virtual/layout-ng-grid/external/wpt/css/css-grid/grid-items/aspect-ratio-004.html [ Pass ]
 virtual/layout-ng-grid/external/wpt/css/css-grid/grid-items/grid-auto-margin-and-replaced-item-001.html [ Pass ]
 virtual/layout-ng-grid/external/wpt/css/css-grid/grid-model/grid-areas-overflowing-grid-container-001.html [ Pass ]
 virtual/layout-ng-grid/external/wpt/css/css-grid/grid-model/grid-areas-overflowing-grid-container-002.html [ Pass ]
@@ -4532,13 +4519,6 @@
 crbug.com/619427 [ Mac ] fast/overflow/overflow-height-float-not-removed-crash3.html [ Pass Failure ]
 
 # [css-ui] Imported tests from W3C suite.
-crbug.com/669473 external/wpt/css/css-ui/box-sizing-014.html [ Failure ]
-crbug.com/669473 external/wpt/css/css-ui/box-sizing-015.html [ Failure ]
-crbug.com/669473 external/wpt/css/css-ui/box-sizing-016.html [ Failure ]
-crbug.com/669473 external/wpt/css/css-ui/box-sizing-018.html [ Failure ]
-crbug.com/669473 external/wpt/css/css-ui/box-sizing-019.html [ Failure ]
-crbug.com/669473 external/wpt/css/css-ui/box-sizing-024.html [ Failure ]
-crbug.com/669473 external/wpt/css/css-ui/box-sizing-025.html [ Failure ]
 crbug.com/669473 external/wpt/css/css-ui/outline-005.html [ Failure ]
 crbug.com/669473 external/wpt/css/css-ui/outline-006.html [ Failure ]
 
@@ -6229,9 +6209,6 @@
 # Sheriff 2020-09-21
 crbug.com/1130500 [ Mac10.13 Debug ] virtual/plz-dedicated-worker/external/wpt/xhr/xhr-timeout-longtask.any.worker.html [ Pass Failure ]
 
-# Temporarily disabled to unblock https://crrev.com/c/2878734
-crbug.com/1159307 inspector-protocol/debugger/wasm-compiled-streaming-breaks.js [ Pass Failure ]
-
 # Sheriff 2020-09-22
 crbug.com/1130533 [ Mac ] external/wpt/xhr/xhr-timeout-longtask.any.html [ Pass Failure ]
 
@@ -6888,6 +6865,3 @@
 crbug.com/1206734 [ Mac10.13 ] external/wpt/websockets/Send-binary-blob.any.worker.html?wpt_flags=h2 [ Timeout ]
 
 crbug.com/1206108 external/wpt/pointerevents/pointerevent_click_is_a_pointerevent_multiple_clicks.html [ Pass Failure ]
-
-# Temporarily disable the test case. Will be re-enabled in CL 2804364
-crbug.com/1046784 http/tests/devtools/sources/debugger/debugger-scope-resolve-this.js [ Pass Failure ]
diff --git a/third_party/blink/web_tests/external/wpt/html/semantics/interactive-elements/the-dialog-element/dialog-audio-video-crash.html b/third_party/blink/web_tests/external/wpt/html/semantics/interactive-elements/the-dialog-element/dialog-audio-video-crash.html
new file mode 100644
index 0000000..c8c1ab282
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/html/semantics/interactive-elements/the-dialog-element/dialog-audio-video-crash.html
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<link rel="help" href="https://crbug.com/1206122">
+
+<body onload=dlg.show()>
+<dialog id="dlg">
+  <audio></audio>
+  <video></video>
+</dialog>
+
+This test passes if it does not crash.
diff --git a/third_party/blink/web_tests/external/wpt/html/semantics/interactive-elements/the-popup-element/popup-element-not-keyboard-focusable.tentative.html b/third_party/blink/web_tests/external/wpt/html/semantics/interactive-elements/the-popup-element/popup-element-not-keyboard-focusable.tentative.html
new file mode 100644
index 0000000..a8a8b60
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/html/semantics/interactive-elements/the-popup-element/popup-element-not-keyboard-focusable.tentative.html
@@ -0,0 +1,44 @@
+<!DOCTYPE html>
+<meta charset="utf-8" />
+<title>Popup keyboard focus behaviors</title>
+<link rel="author" href="mailto:masonf@chromium.org">
+<link rel=help href="https://open-ui.org/components/popup.research.explainer">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-actions.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+
+<button id=firstfocus>Button 1</button>
+<popup>
+  <p>This is a popup without a focusable element</p>
+</popup>
+<button id=secondfocus>Button 2</button>
+
+<script>
+promise_test(async () => {
+  const b1 = document.getElementById('firstfocus');
+  const b2 = document.getElementById('secondfocus');
+  const popup = document.querySelector('popup');
+  b1.focus();
+  assert_equals(document.activeElement,b1);
+  popup.show();
+  assert_true(popup.open);
+  assert_equals(document.activeElement,b1);
+  // Tab once
+  await new test_driver.send_keys(document.body,'\uE004'); // Tab
+  assert_equals(document.activeElement, b2, 'Keyboard focus should skip the open popup');
+  popup.hide();
+
+  // Add a focusable button to the popup and make sure we can focus that
+  const button = document.createElement('button');
+  popup.appendChild(button);
+  b1.focus();
+  popup.show();
+  assert_equals(document.activeElement,b1);
+  // Tab once
+  await new test_driver.send_keys(document.body,'\uE004'); // Tab
+  assert_equals(document.activeElement, button, 'Keyboard focus should go to the contained button');
+  popup.hide();
+}, "Popup should not be keyboard focusable");
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/webcodecs/videoFrame-readInto.any.js b/third_party/blink/web_tests/external/wpt/webcodecs/videoFrame-readInto.any.js
index aaf95bf2..4f5791b 100644
--- a/third_party/blink/web_tests/external/wpt/webcodecs/videoFrame-readInto.any.js
+++ b/third_party/blink/web_tests/external/wpt/webcodecs/videoFrame-readInto.any.js
@@ -1,112 +1,171 @@
 // META: global=window,dedicatedworker
 
 function makeI420_4x2() {
-  let yData = new Uint8Array([1, 2, 3, 4, 5, 6, 7, 8]);
-  let uData = new Uint8Array([9, 10]);
-  let vData = new Uint8Array([11, 12]);
-  let planes = [{src: yData, stride: 4},
-                {src: uData, stride: 2},
-                {src: vData, stride: 2}];
-  let init = {timestamp: 0,
-              codedWidth: 4,
-              codedHeight: 2};
+  const yData = new Uint8Array([
+      1, 2, 3, 4,
+      5, 6, 7, 8,
+  ]);
+  const uData = new Uint8Array([
+      9, 10,
+  ]);
+  const vData = new Uint8Array([
+      11, 12,
+  ]);
+  const planes = [
+      {data: yData, stride: 4},
+      {data: uData, stride: 2},
+      {data: vData, stride: 2},
+  ];
+  const init = {
+      timestamp: 0,
+      codedWidth: 4,
+      codedHeight: 2,
+  };
   return new VideoFrame('I420', planes, init);
 }
 
 function makeRGBA_2x2() {
-  let planes = [{src: new Uint8Array([1, 2, 3, 4, 5, 6, 7, 8,
-                                      9, 10, 11, 12, 13, 14, 15, 16]),
-                 stride: 8}];
-  let init = {timestamp: 0,
-              codedWidth: 2,
-              codedHeight: 2};
-  // TODO(sandersd): Should be RGBA but that's missing in the IDL right now.
+  const rgbaData = new Uint8Array([
+      1, 2, 3, 4,     5, 6, 7, 8,
+      9, 10, 11, 12,  13, 14, 15, 16,
+  ]);
+  const planes = [
+      {data: rgbaData, stride: 8},
+  ];
+  const init = {
+      timestamp: 0,
+      codedWidth: 2,
+      codedHeight: 2,
+  };
+  // TODO(sandersd): Should be RGBA but the IDL is reversed right now.
   return new VideoFrame('ABGR', planes, init);
 }
 
+function assert_buffer_equals(actual, expected) {
+  assert_true(actual instanceof Uint8Array, 'actual instanceof Uint8Array');
+  assert_true(expected instanceof Uint8Array, 'buffer instanceof Uint8Array');
+  assert_equals(actual.length, expected.length, 'buffer length');
+  for (let i = 0; i < actual.length; i++) {
+    if (actual[i] != expected[i]) {
+      assert_equals(actual[i], expected[i], 'buffer contents at index ' + i);
+    }
+  }
+}
+
+function assert_layout_equals(actual, expected) {
+  assert_equals(actual.length, expected.length, 'layout planes');
+  for (let i = 0; i < actual.length; i++) {
+    assert_object_equals(actual[i], expected[i], 'plane ' + i + ' layout');
+  }
+}
+
 promise_test(async t => {
-  let frame = makeI420_4x2();
-  let buf = new Uint8Array(12);
-  let expected = new Uint8Array([
-      1, 2, 3, 4, 5, 6, 7, 8,  // y
-      9, 10,                   // u
-      11, 12                   // v
+  const frame = makeI420_4x2();
+  const expectedLayout = [
+      {offset: 0, stride: 4},
+      {offset: 8, stride: 2},
+      {offset: 10, stride: 2},
+  ];
+  const expectedData = new Uint8Array([
+      1, 2, 3, 4,  // y
+      5, 6, 7, 8,
+      9, 10,       // u
+      11, 12       // v
   ]);
-  assert_equals(frame.allocationSize(), 12, 'allocationSize()');
-  await frame.readInto(buf);
-  assert_array_equals(buf, expected, 'destination buffer contents');
+  assert_equals(frame.allocationSize(), expectedData.length, 'allocationSize()');
+  const data = new Uint8Array(expectedData.length);
+  const layout = await frame.readInto(data);
+  assert_layout_equals(layout, expectedLayout);
+  assert_buffer_equals(data, expectedData);
 }, 'Test I420 frame.');
 
 promise_test(async t => {
-  let frame = makeRGBA_2x2();
-  let buf = new Uint8Array(16);
-  let expected = new Uint8Array([
-      1, 2, 3, 4, 5, 6, 7, 8,
-      9, 10, 11, 12, 13, 14, 15, 16
+  const frame = makeRGBA_2x2();
+  const expectedLayout = [
+      {offset: 0, stride: 8},
+  ];
+  const expectedData = new Uint8Array([
+      1, 2, 3, 4,     5, 6, 7, 8,
+      9, 10, 11, 12,  13, 14, 15, 16
   ]);
-  assert_equals(frame.allocationSize(), 16, 'allocationSize()');
-  await frame.readInto(buf);
-  assert_array_equals(buf, expected, 'destination buffer contents');
+  assert_equals(frame.allocationSize(), expectedData.length, 'allocationSize()');
+  const data = new Uint8Array(expectedData.length);
+  const layout = await frame.readInto(data);
+  assert_layout_equals(layout, expectedLayout);
+  assert_buffer_equals(data, expectedData);
 }, 'Test RGBA frame.');
 
 promise_test(async t => {
-  let frame = makeI420_4x2();
-  let buf = new Uint8Array(11);
-  let expected = new Uint8Array([
-      1, 2, 3, 4, 5, 6, 7, 8,  // y
-      9, 10,                   // u
-      11, 12                   // v
-  ]);
-  assert_equals(frame.allocationSize(), 12, 'allocationSize()');
-  await promise_rejects_dom(t, 'ConstraintError', frame.readInto(buf));
+  const frame = makeI420_4x2();
+  const data = new Uint8Array(11);
+  await promise_rejects_dom(t, 'ConstraintError', frame.readInto(data));
 }, 'Test undersized buffer.');
 
 promise_test(async t => {
-  let frame = makeI420_4x2();
-  let buf = new Uint8Array(12);
-  let options = {layout: [{}, {}, {}]};
-  let expected = new Uint8Array([
-      1, 2, 3, 4, 5, 6, 7, 8,  // y
-      9, 10,                   // u
-      11, 12                   // v
+  const frame = makeI420_4x2();
+  const options = {
+      layout: [{}, {}, {}],
+  };
+  const expectedLayout = [
+      {offset: 0, stride: 4},
+      {offset: 8, stride: 2},
+      {offset: 10, stride: 2},
+  ];
+  const expectedData = new Uint8Array([
+      1, 2, 3, 4,  // y
+      5, 6, 7, 8,
+      9, 10,       // u
+      11, 12       // v
   ]);
-  assert_equals(frame.allocationSize(options), 12, 'allocationSize()');
-  await frame.readInto(buf, options);
-  assert_array_equals(buf, expected, 'destination buffer contents');
+  assert_equals(frame.allocationSize(options), expectedData.length, 'allocationSize()');
+  const data = new Uint8Array(expectedData.length);
+  const layout = await frame.readInto(data, options);
+  assert_layout_equals(layout, expectedLayout);
+  assert_buffer_equals(data, expectedData);
 }, 'Test layout can be empty.');
 
 promise_test(async t => {
-  let frame = makeI420_4x2();
-  let buf = new Uint8Array(12);
-  let options = {layout: [{}]};
+  const frame = makeI420_4x2();
+  const options = {
+      layout: [{}],
+  };
   assert_throws_dom('ConstraintError', () => frame.allocationSize(options));
-  await promise_rejects_dom(t, 'ConstraintError', frame.readInto(buf, options));
+  const data = new Uint8Array(12);
+  await promise_rejects_dom(t, 'ConstraintError', frame.readInto(data, options));
 }, 'Test incorrect plane count.');
 
 promise_test(async t => {
-  let frame = makeI420_4x2();
-  let buf = new Uint8Array(12);
-  let options = {layout: [{offset: 4, stride: 4},
-                          {offset: 0, stride: 2},
-                          {offset: 2, stride: 2}]};
-  let expected = new Uint8Array([
+  const frame = makeI420_4x2();
+  const options = {
+      layout: [
+          {offset: 4, stride: 4},
+          {offset: 0, stride: 2},
+          {offset: 2, stride: 2},
+      ],
+  };
+  const expectedData = new Uint8Array([
       9, 10,       // u
       11, 12,      // v
       1, 2, 3, 4,  // y
       5, 6, 7, 8,
   ]);
-  assert_equals(frame.allocationSize(options), 12, 'allocationSize()');
-  await frame.readInto(buf, options);
-  assert_array_equals(buf, expected, 'destination buffer contents');
+  assert_equals(frame.allocationSize(options), expectedData.length, 'allocationSize()');
+  const data = new Uint8Array(expectedData.length);
+  const layout = await frame.readInto(data, options);
+  assert_layout_equals(layout, options.layout);
+  assert_buffer_equals(data, expectedData);
 }, 'Test stride and offset work.');
 
 promise_test(async t => {
-  let frame = makeI420_4x2();
-  let buf = new Uint8Array(20);
-  let options = {layout: [{offset: 9, stride: 5},
-                          {offset: 1, stride: 3},
-                          {offset: 5, stride: 3}]};
-  let expected = new Uint8Array([
+  const frame = makeI420_4x2();
+  const options = {
+      layout: [
+          {offset: 9, stride: 5},
+          {offset: 1, stride: 3},
+          {offset: 5, stride: 3},
+      ],
+  };
+  const expectedData = new Uint8Array([
       0,
       9, 10, 0,       // u
       0,
@@ -114,102 +173,142 @@
       0,
       1, 2, 3, 4, 0,  // y
       5, 6, 7, 8, 0,
-      0
   ]);
-  assert_equals(frame.allocationSize(options), 19, 'allocationSize()');
-  await frame.readInto(buf, options);
-  assert_array_equals(buf, expected, 'destination buffer contents');
+  assert_equals(frame.allocationSize(options), expectedData.length, 'allocationSize()');
+  const data = new Uint8Array(expectedData.length);
+  const layout = await frame.readInto(data, options);
+  assert_layout_equals(layout, options.layout);
+  assert_buffer_equals(data, expectedData);
 }, 'Test stride and offset with padding.');
 
 promise_test(async t => {
-  let frame = makeI420_4x2();
-  let buf = new Uint8Array(12);
-  let options = {layout: [{offset: 0, stride: 1},
-                          {offset: 8, stride: 2},
-                          {offset: 10, stride: 2}]};
+  const frame = makeI420_4x2();
+  const options = {
+      layout: [
+          {offset: 0, stride: 1},
+          {offset: 8, stride: 2},
+          {offset: 10, stride: 2},
+      ],
+  };
   assert_throws_dom('ConstraintError', () => frame.allocationSize(options));
-  await promise_rejects_dom(t, 'ConstraintError', frame.readInto(buf, options));
+  const data = new Uint8Array(12);
+  await promise_rejects_dom(t, 'ConstraintError', frame.readInto(data, options));
 }, 'Test invalid stride.');
 
 promise_test(async t => {
-  let frame = makeI420_4x2();
-  let buf = new Uint8Array(12);
-  let options = {layout: [{offset: 0, stride: 4},
-                          {offset: 8, stride: 2},
-                          {offset: 10}]};
+  const frame = makeI420_4x2();
+  const options = {
+      layout: [
+          {offset: 0, stride: 4},
+          {offset: 8, stride: 2},
+          {offset: 10},
+      ],
+  };
   assert_throws_dom('ConstraintError', () => frame.allocationSize(options));
-  await promise_rejects_dom(t, 'ConstraintError', frame.readInto(buf, options));
+  const data = new Uint8Array(12);
+  await promise_rejects_dom(t, 'ConstraintError', frame.readInto(data, options));
 }, 'Test missing stride.');
 
 
 promise_test(async t => {
-  let frame = makeI420_4x2();
-  let buf = new Uint8Array(12);
-  let options = {layout: [{offset: 0, stride: 4},
-                          {offset: 8, stride: 2},
-                          {stride: 2}]};
+  const frame = makeI420_4x2();
+  const options = {
+      layout: [
+          {offset: 0, stride: 4},
+          {offset: 8, stride: 2},
+          {stride: 2},
+      ],
+  };
   assert_throws_dom('ConstraintError', () => frame.allocationSize(options));
-  await promise_rejects_dom(t, 'ConstraintError', frame.readInto(buf, options));
+  const data = new Uint8Array(12);
+  await promise_rejects_dom(t, 'ConstraintError', frame.readInto(data, options));
 }, 'Test missing offset.');
 
 promise_test(async t => {
-  let frame = makeI420_4x2();
-  let buf = new Uint8Array(12);
-  let options = {layout: [{offset: 0, stride: 4},
-                          {offset: 8, stride: 2},
-                          {offset: 2 ** 32 - 2, stride: 2}]};
+  const frame = makeI420_4x2();
+  const options = {
+      layout: [
+          {offset: 0, stride: 4},
+          {offset: 8, stride: 2},
+          {offset: 2 ** 32 - 2, stride: 2},
+      ],
+  };
   assert_throws_dom('ConstraintError', () => frame.allocationSize(options));
-  await promise_rejects_dom(t, 'ConstraintError', frame.readInto(buf, options));
+  const data = new Uint8Array(12);
+  await promise_rejects_dom(t, 'ConstraintError', frame.readInto(data, options));
 }, 'Test address overflow.');
 
 promise_test(async t => {
-  let frame = makeI420_4x2();
-  let buf = new Uint8Array(12);
-  let options = {region: frame.codedRegion};
-  let expected = new Uint8Array([
+  const frame = makeI420_4x2();
+  const options = {
+      region: frame.codedRegion,
+  };
+  const expectedLayout = [
+      {offset: 0, stride: 4},
+      {offset: 8, stride: 2},
+      {offset: 10, stride: 2},
+  ];
+  const expectedData = new Uint8Array([
       1, 2, 3, 4, 5, 6, 7, 8,  // y
       9, 10,                   // u
       11, 12                   // v
   ]);
-  assert_equals(frame.allocationSize(options), 12, 'allocationSize()');
-  await frame.readInto(buf, options);
-  assert_array_equals(buf, expected, 'destination buffer contents');
+  assert_equals(frame.allocationSize(options), expectedData.length, 'allocationSize()');
+  const data = new Uint8Array(expectedData.length);
+  const layout = await frame.readInto(data, options);
+  assert_layout_equals(layout, expectedLayout);
+  assert_buffer_equals(data, expectedData);
 }, 'Test codedRegion.');
 
 promise_test(async t => {
-  let frame = makeI420_4x2();
-  let buf = new Uint8Array(12);
-  let options = {region: {left: 0, top: 0, width: 4, height: 0}};
+  const frame = makeI420_4x2();
+  const options = {
+      region: {left: 0, top: 0, width: 4, height: 0},
+  };
   assert_throws_dom('ConstraintError', () => frame.allocationSize(options));
-  await promise_rejects_dom(t, 'ConstraintError', frame.readInto(buf, options));
+  const data = new Uint8Array(12);
+  await promise_rejects_dom(t, 'ConstraintError', frame.readInto(data, options));
 }, 'Test empty region.');
 
 promise_test(async t => {
-  let frame = makeI420_4x2();
-  let buf = new Uint8Array(12);
-  let options = {region: {left: 0, top: 0, width: 4, height: 1}};
+  const frame = makeI420_4x2();
+  const options = {
+      region: {left: 0, top: 0, width: 4, height: 1},
+  };
   assert_throws_dom('ConstraintError', () => frame.allocationSize(options));
-  await promise_rejects_dom(t, 'ConstraintError', frame.readInto(buf, options));
+  const data = new Uint8Array(12);
+  await promise_rejects_dom(t, 'ConstraintError', frame.readInto(data, options));
 }, 'Test unaligned region.');
 
 promise_test(async t => {
-  let frame = makeI420_4x2();
-  let buf = new Uint8Array(6);
-  let options = {region: {left: 2, top: 0, width: 2, height: 2}};
-  let expected = new Uint8Array([
-      3, 4, 7, 8,  // y
-      10,          // u
-      12           // v
+  const frame = makeI420_4x2();
+  const options = {
+      region: {left: 2, top: 0, width: 2, height: 2},
+  };
+  const expectedLayout = [
+      {offset: 0, stride: 2},
+      {offset: 4, stride: 1},
+      {offset: 5, stride: 1},
+  ];
+  const expectedData = new Uint8Array([
+      3, 4,  // y
+      7, 8,
+      10,    // u
+      12     // v
   ]);
-  assert_equals(frame.allocationSize(options), 6, 'allocationSize()');
-  await frame.readInto(buf, options);
-  assert_array_equals(buf, expected, 'destination buffer contents');
+  assert_equals(frame.allocationSize(options), expectedData.length, 'allocationSize()');
+  const data = new Uint8Array(expectedData.length);
+  const layout = await frame.readInto(data, options);
+  assert_layout_equals(layout, expectedLayout);
+  assert_buffer_equals(data, expectedData);
 }, 'Test left crop.');
 
 promise_test(async t => {
-  let frame = makeI420_4x2();
-  let buf = new Uint8Array(12);
-  let options = {region: {left: 0, top: 0, width: 4, height: 4}};
+  const frame = makeI420_4x2();
+  const options = {
+      region: {left: 0, top: 0, width: 4, height: 4},
+  };
   assert_throws_dom('ConstraintError', () => frame.allocationSize(options));
-  await promise_rejects_dom(t, 'ConstraintError', frame.readInto(buf, options));
+  const data = new Uint8Array(12);
+  await promise_rejects_dom(t, 'ConstraintError', frame.readInto(data, options));
 }, 'Test invalid region.');
diff --git a/third_party/blink/web_tests/fast/replaced/table-percent-width-expected.txt b/third_party/blink/web_tests/fast/replaced/table-percent-width-expected.txt
index 6f74758..9784532 100644
--- a/third_party/blink/web_tests/fast/replaced/table-percent-width-expected.txt
+++ b/third_party/blink/web_tests/fast/replaced/table-percent-width-expected.txt
@@ -16,7 +16,7 @@
 PASS getWidth('img-4') is '102px'
 PASS getHeight('img-4') is '102px'
 PASS getWidth('img-5') is '40px'
-PASS getHeight('img-5') is '102px'
+PASS getHeight('img-5') is '42px'
 PASS successfullyParsed is true
 
 TEST COMPLETE
diff --git a/third_party/blink/web_tests/fast/replaced/table-percent-width.html b/third_party/blink/web_tests/fast/replaced/table-percent-width.html
index 092a1eb..8ea77b33 100644
--- a/third_party/blink/web_tests/fast/replaced/table-percent-width.html
+++ b/third_party/blink/web_tests/fast/replaced/table-percent-width.html
@@ -54,7 +54,7 @@
     shouldBe("getWidth('img-4')", "'102px'");
     shouldBe("getHeight('img-4')", "'102px'");
     shouldBe("getWidth('img-5')", "'40px'");
-    shouldBe("getHeight('img-5')", "'102px'");
+    shouldBe("getHeight('img-5')", "'42px'");
 
     isSuccessfullyParsed();
 
diff --git a/third_party/blink/web_tests/flag-specific/composite-after-paint/paint/invalidation/svg/tspan-pattern-update-expected.txt b/third_party/blink/web_tests/flag-specific/composite-after-paint/paint/invalidation/svg/tspan-pattern-update-expected.txt
index 59b5c22d..406950be 100644
--- a/third_party/blink/web_tests/flag-specific/composite-after-paint/paint/invalidation/svg/tspan-pattern-update-expected.txt
+++ b/third_party/blink/web_tests/flag-specific/composite-after-paint/paint/invalidation/svg/tspan-pattern-update-expected.txt
@@ -6,7 +6,7 @@
       "contentsOpaque": true,
       "backgroundColor": "#FFFFFF",
       "invalidations": [
-        [108, 8, 100, 100]
+        [8, 8, 300, 100]
       ]
     }
   ]
diff --git a/third_party/blink/web_tests/http/tests/devtools/sources/debugger-breakpoints/set-conditional-breakpoint.js b/third_party/blink/web_tests/http/tests/devtools/sources/debugger-breakpoints/set-conditional-breakpoint.js
index c54a401..70150df 100644
--- a/third_party/blink/web_tests/http/tests/devtools/sources/debugger-breakpoints/set-conditional-breakpoint.js
+++ b/third_party/blink/web_tests/http/tests/devtools/sources/debugger-breakpoints/set-conditional-breakpoint.js
@@ -22,8 +22,9 @@
         SourcesTestRunner
             .createNewBreakpoint(currentSourceFrame, 13, 'true', true)
             .then(() => SourcesTestRunner.waitBreakpointSidebarPane())
-            .then(() => setTimeout(() =>
-                SourcesTestRunner.runTestFunction(), 1));
+            .then(
+                () => setTimeout(
+                    () => SourcesTestRunner.runTestFunction(), 1000));
       }
 
       async function didPause(callFrames) {
diff --git a/third_party/blink/web_tests/http/tests/devtools/sources/debugger/debugger-scope-resolve-this-expected.txt b/third_party/blink/web_tests/http/tests/devtools/sources/debugger/debugger-scope-resolve-this-expected.txt
index 8c08f20..2f7c44e 100644
--- a/third_party/blink/web_tests/http/tests/devtools/sources/debugger/debugger-scope-resolve-this-expected.txt
+++ b/third_party/blink/web_tests/http/tests/devtools/sources/debugger/debugger-scope-resolve-this-expected.txt
@@ -7,6 +7,7 @@
 Local
     this: Foo {}
 Closure (Foo.bar)
+    _this: Foo {}
 WindowGlobal
     <section collapsed>
 Script execution resumed.
diff --git a/third_party/blink/web_tests/inspector-protocol/debugger/wasm-compiled-streaming-breaks-expected.txt b/third_party/blink/web_tests/inspector-protocol/debugger/wasm-compiled-streaming-breaks-expected.txt
index 37425ca..ec525a4 100644
--- a/third_party/blink/web_tests/inspector-protocol/debugger/wasm-compiled-streaming-breaks-expected.txt
+++ b/third_party/blink/web_tests/inspector-protocol/debugger/wasm-compiled-streaming-breaks-expected.txt
@@ -2,8 +2,8 @@
 Did enable debugger.
 Paused on debugger.
 call_debugger at :2:4
-call_func at blob:file:///UUID:0:72
-main at blob:file:///UUID:0:81
+$call_func at blob:file:///UUID:0:72
+$main at blob:file:///UUID:0:81
 (anonymous) at :59:39
 Resumed execution.
 
diff --git a/third_party/blink/web_tests/paint/invalidation/svg/tspan-pattern-update-expected.txt b/third_party/blink/web_tests/paint/invalidation/svg/tspan-pattern-update-expected.txt
index 7fc8c61..56d07a3 100644
--- a/third_party/blink/web_tests/paint/invalidation/svg/tspan-pattern-update-expected.txt
+++ b/third_party/blink/web_tests/paint/invalidation/svg/tspan-pattern-update-expected.txt
@@ -6,7 +6,7 @@
       "contentsOpaque": true,
       "backgroundColor": "#FFFFFF",
       "invalidations": [
-        [108, 8, 100, 100]
+        [8, 8, 300, 100]
       ]
     }
   ]
diff --git a/third_party/blink/web_tests/platform/linux/tables/mozilla/bugs/bug137388-2-expected.png b/third_party/blink/web_tests/platform/linux/tables/mozilla/bugs/bug137388-2-expected.png
index 68ed1dae..756fdf6f 100644
--- a/third_party/blink/web_tests/platform/linux/tables/mozilla/bugs/bug137388-2-expected.png
+++ b/third_party/blink/web_tests/platform/linux/tables/mozilla/bugs/bug137388-2-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac-arm11.0/tables/mozilla/bugs/bug137388-2-expected.png b/third_party/blink/web_tests/platform/mac-mac-arm11.0/tables/mozilla/bugs/bug137388-2-expected.png
new file mode 100644
index 0000000..c83c11bd
--- /dev/null
+++ b/third_party/blink/web_tests/platform/mac-mac-arm11.0/tables/mozilla/bugs/bug137388-2-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/tables/mozilla/bugs/bug137388-2-expected.png b/third_party/blink/web_tests/platform/mac/tables/mozilla/bugs/bug137388-2-expected.png
index c83c11bd..77559894 100644
--- a/third_party/blink/web_tests/platform/mac/tables/mozilla/bugs/bug137388-2-expected.png
+++ b/third_party/blink/web_tests/platform/mac/tables/mozilla/bugs/bug137388-2-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/tables/mozilla/bugs/bug137388-2-expected.png b/third_party/blink/web_tests/platform/win/tables/mozilla/bugs/bug137388-2-expected.png
index b39895f..f993e5f 100644
--- a/third_party/blink/web_tests/platform/win/tables/mozilla/bugs/bug137388-2-expected.png
+++ b/third_party/blink/web_tests/platform/win/tables/mozilla/bugs/bug137388-2-expected.png
Binary files differ
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 62878bf..06989d27 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
@@ -8148,10 +8148,12 @@
 interface Screens : EventTarget
     attribute @@toStringTag
     getter currentScreen
-    getter onchange
+    getter oncurrentscreenchange
+    getter onscreenschange
     getter screens
     method constructor
-    setter onchange
+    setter oncurrentscreenchange
+    setter onscreenschange
 interface ScriptProcessorNode : AudioNode
     attribute @@toStringTag
     getter bufferSize
diff --git a/third_party/freetype/README.chromium b/third_party/freetype/README.chromium
index 28465116..9c8ab3c 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-243-g967a34eee
-Revision: 967a34eee3fd34f496366ed1283ab5268d23690a
+Version: VER-2-10-4-244-gb07026452
+Revision: b070264521386666dd0cbbedec469c84b26489ba
 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/sqlite/README.chromium b/third_party/sqlite/README.chromium
index 4ae58cf..7cb5f8c 100644
--- a/third_party/sqlite/README.chromium
+++ b/third_party/sqlite/README.chromium
@@ -1,7 +1,7 @@
 Name: sqlite
 URL: https://sqlite.org/
-Version: 3.34.0
-CPEPrefix: cpe:/a:sqlite:sqlite:3.34.0
+Version: 3.35.5
+CPEPrefix: cpe:/a:sqlite:sqlite:3.35.5
 Included In Release: Yes
 Security Critical: Yes
 License: Public domain
diff --git a/third_party/zlib/google/OWNERS b/third_party/zlib/google/OWNERS
index 868af3c..411670c 100644
--- a/third_party/zlib/google/OWNERS
+++ b/third_party/zlib/google/OWNERS
@@ -1,3 +1,5 @@
+fdegros@chromium.org
+noel@chromium.org
 satorux@chromium.org
 
 # compression_utils*
diff --git a/tools/clang/rewrite_raw_ptr_fields/manual-paths-to-ignore.txt b/tools/clang/rewrite_raw_ptr_fields/manual-paths-to-ignore.txt
index 9509c79..9369c1a 100644
--- a/tools/clang/rewrite_raw_ptr_fields/manual-paths-to-ignore.txt
+++ b/tools/clang/rewrite_raw_ptr_fields/manual-paths-to-ignore.txt
@@ -49,6 +49,15 @@
 base/logging.h
 base/synchronization/lock_impl.h
 
+# Performance related exclusion based on sampling profiler data
+base/bind_internal.h
+base/message_loop/
+base/task/
+base/threading/
+base/timer/
+cc/
+content/browser/scheduler/responsiveness/
+
 # Exclude code that only runs inside a renderer process - renderer
 # processes are excluded for now from the MiraclePtr project scope,
 # because they are sensitive to performance regressions (to a much higher
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml
index 16ae610..2ca4551 100644
--- a/tools/metrics/histograms/enums.xml
+++ b/tools/metrics/histograms/enums.xml
@@ -21619,10 +21619,9 @@
     portrait dimension has 1 subtracted from the enum index to differentiate it
     from the landscape index value.
   </summary>
-  <int value="345599" label="480 x 720"/>
-  <int value="345600" label="720 x 480"/>
-  <int value="349183" label="512 x 682"/>
   <int value="349184" label="682 x 512"/>
+  <int value="387203" label="492 x 787"/>
+  <int value="387204" label="787 x 492"/>
   <int value="409439" label="480 x 853"/>
   <int value="409440" label="853 x 480"/>
   <int value="426399" label="533 x 800"/>
@@ -21637,8 +21636,6 @@
   <int value="479037" label="923 x 519"/>
   <int value="482797" label="566 x 853"/>
   <int value="482798" label="853 x 566"/>
-  <int value="497663" label="576 x 864"/>
-  <int value="497664" label="864 x 576"/>
   <int value="502865" label="614 x 819"/>
   <int value="502866" label="819 x 614"/>
   <int value="505799" label="562 x 900"/>
@@ -21649,6 +21646,12 @@
   <int value="525696" label="888 x 592"/>
   <int value="531647" label="576 x 923"/>
   <int value="531648" label="923 x 576"/>
+  <int value="540329" label="581 x 930"/>
+  <int value="540330" label="930 x 581"/>
+  <int value="544151" label="553 x 984"/>
+  <int value="544152" label="984 x 553"/>
+  <int value="553279" label="608 x 910"/>
+  <int value="553280" label="910 x 608"/>
   <int value="575999" label="600 x 960"/>
   <int value="576000" label="960 x 600"/>
   <int value="589823" label="576 x 1024"/>
@@ -21665,8 +21668,6 @@
   <int value="619500" label="1050 x 590"/>
   <int value="639599" label="600 x 1066"/>
   <int value="639600" label="1066 x 600"/>
-  <int value="641573" label="654 x 981"/>
-  <int value="641574" label="981 x 654"/>
   <int value="649139" label="698 x 930"/>
   <int value="649140" label="930 x 698"/>
   <int value="655359" label="640 x 1024"/>
@@ -21679,24 +21680,26 @@
   <int value="680036" label="1043 x 652"/>
   <int value="696319" label="680 x 1024"/>
   <int value="696320" label="1024 x 680"/>
+  <int value="696737" label="626 x 1113"/>
+  <int value="696738" label="1113 x 626"/>
   <int value="699391" label="683 x 1024"/>
   <int value="699392" label="1024 x 683"/>
   <int value="709955" label="666 x 1066"/>
   <int value="709956" label="1066 x 666"/>
-  <int value="718295" label="692 x 1038"/>
-  <int value="718296" label="1038 x 692"/>
+  <int value="724820" label="673 x 1077"/>
+  <int value="724821" label="1077 x 673"/>
   <int value="728319" label="640 x 1138"/>
   <int value="728320" label="1138 x 640"/>
   <int value="728999" label="675 x 1080"/>
   <int value="729000" label="1080 x 675"/>
+  <int value="736049" label="701 x 1050"/>
+  <int value="736050" label="1050 x 701"/>
   <int value="751444" label="685 x 1097"/>
   <int value="751445" label="1097 x 685"/>
   <int value="760601" label="654 x 1163"/>
   <int value="760602" label="1163 x 654"/>
   <int value="773534" label="695 x 1113"/>
   <int value="773535" label="1113 x 695"/>
-  <int value="777599" label="720 x 1080"/>
-  <int value="777600" label="1080 x 720"/>
   <int value="783731" label="723 x 1084"/>
   <int value="783732" label="1084 x 723"/>
   <int value="784183" label="664 x 1181"/>
@@ -21709,26 +21712,38 @@
   <int value="791729" label="1187 x 667"/>
   <int value="792429" label="727 x 1090"/>
   <int value="792430" label="1090 x 727"/>
+  <int value="796067" label="729 x 1092"/>
+  <int value="796068" label="1092 x 729"/>
+  <int value="808406" label="711 x 1137"/>
+  <int value="808407" label="1137 x 711"/>
   <int value="809999" label="675 x 1200"/>
   <int value="810000" label="1200 x 675"/>
+  <int value="835014" label="685 x 1219"/>
+  <int value="835015" label="1219 x 685"/>
   <int value="845500" label="727 x 1163"/>
   <int value="845501" label="1163 x 727"/>
   <int value="851159" label="692 x 1230"/>
   <int value="851160" label="1230 x 692"/>
+  <int value="864879" label="760 x 1138"/>
+  <int value="864880" label="1138 x 760"/>
   <int value="866217" label="698 x 1241"/>
   <int value="866218" label="1241 x 698"/>
+  <int value="871577" label="738 x 1181"/>
+  <int value="871578" label="1181 x 738"/>
   <int value="893975" label="772 x 1158"/>
   <int value="893976" label="1158 x 772"/>
   <int value="897835" label="772 x 1163"/>
   <int value="897836" label="1163 x 772"/>
   <int value="899999" label="750 x 1200"/>
   <int value="900000" label="1200 x 750"/>
-  <int value="917285" label="782 x 1173"/>
-  <int value="917286" label="1173 x 782"/>
+  <int value="905407" label="752 x 1204"/>
+  <int value="905408" label="1204 x 752"/>
   <int value="921599" label="720 x 1280"/>
   <int value="921600" label="1280 x 720"/>
   <int value="927658" label="761 x 1219"/>
   <int value="927659" label="1219 x 761"/>
+  <int value="941290" label="793 x 1187"/>
+  <int value="941291" label="1187 x 793"/>
   <int value="950299" label="731 x 1300"/>
   <int value="950300" label="1300 x 731"/>
   <int value="959999" label="800 x 1200"/>
@@ -21741,6 +21756,8 @@
   <int value="1001642" label="1226 x 817"/>
   <int value="1023999" label="800 x 1280"/>
   <int value="1024000" label="1280 x 800"/>
+  <int value="1028788" label="829 x 1241"/>
+  <int value="1028789" label="1241 x 829"/>
   <int value="1049087" label="768 x 1366"/>
   <int value="1049088" label="1366 x 768"/>
   <int value="1057040" label="771 x 1371"/>
@@ -21755,6 +21772,8 @@
   <int value="1110389" label="1333 x 833"/>
   <int value="1120333" label="794 x 1411"/>
   <int value="1120334" label="1411 x 794"/>
+  <int value="1128399" label="868 x 1300"/>
+  <int value="1128400" label="1300 x 868"/>
   <int value="1137599" label="800 x 1422"/>
   <int value="1137600" label="1422 x 800"/>
   <int value="1138049" label="843 x 1350"/>
@@ -21769,14 +21788,16 @@
   <int value="1183704" label="1333 x 888"/>
   <int value="1189371" label="818 x 1454"/>
   <int value="1189372" label="1454 x 818"/>
-  <int value="1214999" label="900 x 1350"/>
-  <int value="1215000" label="1350 x 900"/>
+  <int value="1217311" label="872 x 1396"/>
+  <int value="1217312" label="1396 x 872"/>
   <int value="1225079" label="830 x 1476"/>
   <int value="1225080" label="1476 x 830"/>
   <int value="1228799" label="960 x 1280"/>
   <int value="1228800" label="1280 x 960"/>
   <int value="1244501" label="882 x 1411"/>
   <int value="1244502" label="1411 x 882"/>
+  <int value="1245791" label="912 x 1366"/>
+  <int value="1245792" label="1366 x 912"/>
   <int value="1262735" label="888 x 1422"/>
   <int value="1262736" label="1422 x 888"/>
   <int value="1264499" label="843 x 1500"/>
@@ -21791,6 +21812,8 @@
   <int value="1325400" label="1410 x 940"/>
   <int value="1327103" label="864 x 1536"/>
   <int value="1327104" label="1536 x 864"/>
+  <int value="1336267" label="914 x 1462"/>
+  <int value="1336268" label="1462 x 914"/>
   <int value="1342367" label="944 x 1422"/>
   <int value="1342368" label="1422 x 944"/>
   <int value="1362347" label="923 x 1476"/>
@@ -21803,24 +21826,26 @@
   <int value="1459280" label="1480 x 986"/>
   <int value="1468943" label="909 x 1616"/>
   <int value="1468944" label="1616 x 909"/>
+  <int value="1474559" label="960 x 1536"/>
+  <int value="1474560" label="1536 x 960"/>
   <int value="1499999" label="1000 x 1500"/>
   <int value="1500000" label="1500 x 1000"/>
+  <int value="1536720" label="1013 x 1517"/>
+  <int value="1536721" label="1517 x 1013"/>
   <int value="1567190" label="939 x 1669"/>
   <int value="1567191" label="1669 x 939"/>
-  <int value="1585175" label="1028 x 1542"/>
-  <int value="1585176" label="1542 x 1028"/>
   <int value="1599999" label="1000 x 1600"/>
   <int value="1600000" label="1600 x 1000"/>
   <int value="1603813" label="1097 x 1462"/>
   <int value="1603814" label="1462 x 1097"/>
+  <int value="1632159" label="1010 x 1616"/>
+  <int value="1632160" label="1616 x 1010"/>
   <int value="1634903" label="1044 x 1566"/>
   <int value="1634904" label="1566 x 1044"/>
   <int value="1637759" label="960 x 1706"/>
   <int value="1637760" label="1706 x 960"/>
   <int value="1652295" label="964 x 1714"/>
   <int value="1652296" label="1714 x 964"/>
-  <int value="1680103" label="1058 x 1588"/>
-  <int value="1680104" label="1588 x 1058"/>
   <int value="1699199" label="1062 x 1600"/>
   <int value="1699200" label="1600 x 1062"/>
   <int value="1711844" label="981 x 1745"/>
@@ -21831,6 +21856,8 @@
   <int value="1740767" label="1669 x 1043"/>
   <int value="1776999" label="1000 x 1777"/>
   <int value="1777000" label="1777 x 1000"/>
+  <int value="1818595" label="1066 x 1706"/>
+  <int value="1818596" label="1706 x 1066"/>
   <int value="1821599" label="1012 x 1800"/>
   <int value="1821600" label="1800 x 1012"/>
   <int value="1832089" label="1105 x 1658"/>
@@ -21843,18 +21870,16 @@
   <int value="1848150" label="1665 x 1110"/>
   <int value="1879183" label="1028 x 1828"/>
   <int value="1879184" label="1828 x 1028"/>
-  <int value="1897874" label="1125 x 1687"/>
-  <int value="1897875" label="1687 x 1125"/>
   <int value="1902049" label="1090 x 1745"/>
   <int value="1902050" label="1745 x 1090"/>
   <int value="1957387" label="1142 x 1714"/>
   <int value="1957388" label="1714 x 1142"/>
+  <int value="2040102" label="1129 x 1807"/>
+  <int value="2040103" label="1807 x 1129"/>
   <int value="2070349" label="1175 x 1762"/>
   <int value="2070350" label="1762 x 1175"/>
   <int value="2073599" label="1080 x 1920"/>
   <int value="2073600" label="1920 x 1080"/>
-  <int value="2159999" label="1200 x 1800"/>
-  <int value="2160000" label="1800 x 1200"/>
   <int value="2183679" label="1280 x 1706"/>
   <int value="2183680" label="1706 x 1280"/>
   <int value="2219191" label="1214 x 1828"/>
@@ -21867,8 +21892,6 @@
   <int value="2355640" label="1880 x 1253"/>
   <int value="2411735" label="1268 x 1902"/>
   <int value="2411736" label="1902 x 1268"/>
-  <int value="2477479" label="1285 x 1928"/>
-  <int value="2477480" label="1928 x 1285"/>
   <int value="2559599" label="1200 x 2133"/>
   <int value="2559600" label="2133 x 1200"/>
   <int value="2665999" label="1333 x 2000"/>
@@ -21879,10 +21902,10 @@
   <int value="2707396" label="2194 x 1234"/>
   <int value="2936224" label="1285 x 2285"/>
   <int value="2936225" label="2285 x 1285"/>
+  <int value="3007973" label="1371 x 2194"/>
+  <int value="3007974" label="2194 x 1371"/>
   <int value="3020327" label="1416 x 2133"/>
   <int value="3020328" label="2133 x 1416"/>
-  <int value="3110399" label="1440 x 2160"/>
-  <int value="3110400" label="2160 x 1440"/>
   <int value="3145727" label="1536 x 2048"/>
   <int value="3145728" label="2048 x 1536"/>
   <int value="3239999" label="1350 x 2400"/>
@@ -32423,6 +32446,10 @@
   <int value="3902" label="WebAppWindowControlsOverlay"/>
   <int value="3903" label="PaymentRequestShowWithoutGestureOrToken"/>
   <int value="3904" label="V8Navigator_UpdateAdInterestGroups_Method"/>
+  <int value="3905" label="V8Screens_Onscreenschange_AttributeGetter"/>
+  <int value="3906" label="V8Screens_Onscreenschange_AttributeSetter"/>
+  <int value="3907" label="V8Screens_Oncurrentscreenchange_AttributeGetter"/>
+  <int value="3908" label="V8Screens_Oncurrentscreenchange_AttributeSetter"/>
 </enum>
 
 <enum name="FeaturePolicyAllowlistType">
@@ -58751,6 +58778,7 @@
   <int value="700" label="Manage Android Preferences"/>
   <int value="701" label="Remove Play Store"/>
   <int value="702" label="Turn On Play Store"/>
+  <int value="703" label="Restore Apps And Pages On Startup"/>
   <int value="800" label="Set Up Crostini"/>
   <int value="801" label="Uninstall Crostini"/>
   <int value="802" label="Backup Linux Apps And Files"/>
@@ -58831,7 +58859,6 @@
   <int value="1800" label="View Add Kerberos Ticket V2"/>
   <int value="1801" label="Remove Kerberos Ticket V2"/>
   <int value="1802" label="Set Active Kerberos Ticket V2"/>
-  <int value="1900" label="Restore Apps And Pages On Startup"/>
 </enum>
 
 <enum name="OsSettingSearchBoxUserAction">
@@ -58890,7 +58917,6 @@
   <int value="16" label="Reset"/>
   <int value="17" label="About Chrome OS"/>
   <int value="18" label="Kerberos"/>
-  <int value="19" label="On Startup"/>
 </enum>
 
 <enum name="OsSettingsSubpage">
@@ -58936,6 +58962,7 @@
   <int value="702" label="Google Play Store"/>
   <int value="703" label="Plugin Vm Shared Paths"/>
   <int value="704" label="Plugin Vm USB Preferences"/>
+  <int value="705" label="On Startup"/>
   <int value="800" label="Crostini Details"/>
   <int value="801" label="Crostini Manage Shared Folders"/>
   <int value="802" label="Crostini Usb Preferences"/>
diff --git a/tools/metrics/histograms/histograms_xml/ash/histograms.xml b/tools/metrics/histograms/histograms_xml/ash/histograms.xml
index 56beb821..ef51bbb2 100644
--- a/tools/metrics/histograms/histograms_xml/ash/histograms.xml
+++ b/tools/metrics/histograms/histograms_xml/ash/histograms.xml
@@ -2638,7 +2638,7 @@
 </histogram>
 
 <histogram name="Ash.Window.AnimationSmoothness.CrossFade.DragMaximize"
-    units="%" expires_after="2021-06-18">
+    units="%" expires_after="2022-05-10">
   <owner>sammiequon@chromium.org</owner>
   <owner>xdai@chromium.org</owner>
   <summary>
@@ -2652,7 +2652,7 @@
 </histogram>
 
 <histogram name="Ash.Window.AnimationSmoothness.CrossFade.DragUnmaximize"
-    units="%" expires_after="2021-06-18">
+    units="%" expires_after="2022-05-10">
   <owner>sammiequon@chromium.org</owner>
   <owner>xdai@chromium.org</owner>
   <summary>
diff --git a/tools/metrics/histograms/histograms_xml/file/histograms.xml b/tools/metrics/histograms/histograms_xml/file/histograms.xml
index 9fb7088..726c42d 100644
--- a/tools/metrics/histograms/histograms_xml/file/histograms.xml
+++ b/tools/metrics/histograms/histograms_xml/file/histograms.xml
@@ -219,7 +219,7 @@
 </histogram>
 
 <histogram name="FileBrowser.ImportController.DeviceYanked" enum="Boolean"
-    expires_after="M92">
+    expires_after="M102">
   <owner>simmonsjosh@google.com</owner>
   <owner>src/ui/file_manager/OWNERS</owner>
   <summary>
@@ -229,7 +229,7 @@
 </histogram>
 
 <histogram name="FileBrowser.ImportController.ImportCancelled"
-    enum="BooleanCanceled" expires_after="M92">
+    enum="BooleanCanceled" expires_after="M102">
   <owner>simmonsjosh@google.com</owner>
   <owner>src/ui/file_manager/OWNERS</owner>
   <summary>
@@ -465,7 +465,7 @@
 </histogram>
 
 <histogram name="FileBrowser.PhotoEditor.DisplayTime" units="ms"
-    expires_after="M92">
+    expires_after="M98">
   <owner>simmonsjosh@google.com</owner>
   <owner>src/ui/file_manager/OWNERS</owner>
   <summary>
@@ -476,14 +476,14 @@
 </histogram>
 
 <histogram name="FileBrowser.PhotoEditor.FileType" enum="PhotoEditorFileType"
-    expires_after="M92">
+    expires_after="M98">
   <owner>simmonsjosh@google.com</owner>
   <owner>src/ui/file_manager/OWNERS</owner>
   <summary>Chrome OS Photo Editor: the type of the file opened.</summary>
 </histogram>
 
 <histogram name="FileBrowser.PhotoEditor.LoadMode" enum="PhotoEditorLoadMode"
-    expires_after="M92">
+    expires_after="M98">
   <owner>simmonsjosh@google.com</owner>
   <owner>src/ui/file_manager/OWNERS</owner>
   <summary>Chrome OS Photo Editor: the way the image has been loaded.</summary>
@@ -497,7 +497,7 @@
 </histogram>
 
 <histogram name="FileBrowser.PhotoEditor.SaveResult"
-    enum="PhotoEditorSaveResult" expires_after="M92">
+    enum="PhotoEditorSaveResult" expires_after="M98">
   <owner>simmonsjosh@google.com</owner>
   <owner>src/ui/file_manager/OWNERS</owner>
   <summary>
@@ -506,14 +506,14 @@
 </histogram>
 
 <histogram name="FileBrowser.PhotoEditor.SaveTime" units="ms"
-    expires_after="M92">
+    expires_after="M98">
   <owner>simmonsjosh@google.com</owner>
   <owner>src/ui/file_manager/OWNERS</owner>
   <summary>Chrome OS Photo Editor: time to save an image to a file.</summary>
 </histogram>
 
 <histogram name="FileBrowser.PhotoEditor.Size.MB" units="MBytes"
-    expires_after="M92">
+    expires_after="M98">
   <owner>simmonsjosh@google.com</owner>
   <owner>src/ui/file_manager/OWNERS</owner>
   <summary>
@@ -523,7 +523,7 @@
 </histogram>
 
 <histogram name="FileBrowser.PhotoEditor.Size.MPix" units="MPixels"
-    expires_after="M92">
+    expires_after="M98">
   <owner>simmonsjosh@google.com</owner>
   <owner>src/ui/file_manager/OWNERS</owner>
   <summary>
@@ -533,7 +533,7 @@
 </histogram>
 
 <histogram name="FileBrowser.PhotoEditor.Tool" enum="PhotoEditorToolType"
-    expires_after="M92">
+    expires_after="M98">
   <owner>simmonsjosh@google.com</owner>
   <owner>src/ui/file_manager/OWNERS</owner>
   <summary>Chrome OS Photo Editor: the button which the user clicked.</summary>
diff --git a/tools/metrics/histograms/histograms_xml/network/histograms.xml b/tools/metrics/histograms/histograms_xml/network/histograms.xml
index 808b49de..c24fa7d2 100644
--- a/tools/metrics/histograms/histograms_xml/network/histograms.xml
+++ b/tools/metrics/histograms/histograms_xml/network/histograms.xml
@@ -1181,7 +1181,10 @@
 </histogram>
 
 <histogram name="Network.Shill.ServicesOnSameNetwork" units="units"
-    expires_after="2021-12-01">
+    expires_after="M92">
+  <obsolete>
+    Removed 05/2021
+  </obsolete>
   <owner>stevenjb@chromium.org</owner>
   <owner>cros-network-metrics@google.com</owner>
   <summary>
@@ -1649,7 +1652,10 @@
 </histogram>
 
 <histogram name="Network.Shill.Wifi.LinkMonitorBroadcastErrorsAtFailure"
-    units="units" expires_after="2021-12-01">
+    units="units" expires_after="M92">
+  <obsolete>
+    Removed 05/2021
+  </obsolete>
   <owner>briannorris@chromium.org</owner>
   <owner>cros-network-metrics@google.com</owner>
   <summary>
@@ -1660,7 +1666,10 @@
 </histogram>
 
 <histogram name="Network.Shill.Wifi.LinkMonitorFailure"
-    enum="LinkMonitorFailureType" expires_after="2021-12-01">
+    enum="LinkMonitorFailureType" expires_after="M92">
+  <obsolete>
+    Removed 05/2021
+  </obsolete>
   <owner>briannorris@chromium.org</owner>
   <owner>cros-network-metrics@google.com</owner>
   <summary>
@@ -1670,7 +1679,10 @@
 </histogram>
 
 <histogram name="Network.Shill.Wifi.LinkMonitorResponseTimeSample" units="ms"
-    expires_after="2021-12-01">
+    expires_after="M92">
+  <obsolete>
+    Removed 05/2021
+  </obsolete>
   <owner>briannorris@chromium.org</owner>
   <owner>cros-network-metrics@google.com</owner>
   <summary>
@@ -1718,7 +1730,10 @@
 </histogram>
 
 <histogram name="Network.Shill.Wifi.LinkMonitorSecondsToFailure"
-    units="seconds" expires_after="2021-12-01">
+    units="seconds" expires_after="M92">
+  <obsolete>
+    Removed 05/2021
+  </obsolete>
   <owner>briannorris@chromium.org</owner>
   <owner>cros-network-metrics@google.com</owner>
   <summary>
@@ -1728,7 +1743,10 @@
 </histogram>
 
 <histogram name="Network.Shill.Wifi.LinkMonitorUnicastErrorsAtFailure"
-    units="units" expires_after="2021-12-01">
+    units="units" expires_after="M92">
+  <obsolete>
+    Removed 05/2021
+  </obsolete>
   <owner>briannorris@chromium.org</owner>
   <owner>cros-network-metrics@google.com</owner>
   <summary>
diff --git a/tools/metrics/histograms/histograms_xml/new_tab_page/histograms.xml b/tools/metrics/histograms/histograms_xml/new_tab_page/histograms.xml
index 862543f4..d9ada31 100644
--- a/tools/metrics/histograms/histograms_xml/new_tab_page/histograms.xml
+++ b/tools/metrics/histograms/histograms_xml/new_tab_page/histograms.xml
@@ -1035,6 +1035,12 @@
     Histogram for user clicks of the most visited tile. The value is equal to
     the index of the tile.
 
+    For special types of results:
+
+    * Clicks on the &quot;Add Shortcut&quot; tile are not counted.
+
+    * Clicks on the Explore Sites tile on Android are counted.
+
     The user action NewTabPage.MostVisited.Clicked is also logged at the same
     time as this histogram.
 
diff --git a/tools/metrics/histograms/histograms_xml/page/histograms.xml b/tools/metrics/histograms/histograms_xml/page/histograms.xml
index b8f6383a..2f7d8553 100644
--- a/tools/metrics/histograms/histograms_xml/page/histograms.xml
+++ b/tools/metrics/histograms/histograms_xml/page/histograms.xml
@@ -91,9 +91,12 @@
 
 <histogram base="true"
     name="PageLoad.AdPaintTiming.NavigationToFirstContentfulPaint3" units="ms"
-    expires_after="2021-12-31">
+    expires_after="never">
+<!-- expires-never: guiding metric (internal: go/chrome-browser-guiding-metrics) -->
+
   <owner>jkarlin@chromium.org</owner>
   <owner>johnidel@chromium.org</owner>
+  <owner>chrome-analysis-team@google.com</owner>
   <summary>
     Records the time from frame navigation start to FirstContentfulPaint of each
     ad frame that receives a FirstContentfulPaint. The time could be quite
@@ -102,6 +105,9 @@
 
     Recorded for all ad frames with non-zero bytes or cpu usage that receive a
     FirstContentfulPaint. Recorded when the ad frame or page is destroyed.
+
+    Do not modify this metric in any way without contacting
+    chrome-analysis-team@google.com.
   </summary>
 </histogram>
 
diff --git a/tools/metrics/histograms/histograms_xml/safe_browsing/histograms.xml b/tools/metrics/histograms/histograms_xml/safe_browsing/histograms.xml
index 9f41edb5..03a7ed3 100644
--- a/tools/metrics/histograms/histograms_xml/safe_browsing/histograms.xml
+++ b/tools/metrics/histograms/histograms_xml/safe_browsing/histograms.xml
@@ -1043,6 +1043,17 @@
   </summary>
 </histogram>
 
+<histogram name="SafeBrowsing.Pref.MainProfile.SafeBrowsingState"
+    enum="SafeBrowsingState" expires_after="2022-05-01">
+  <owner>jeffcyr@google.com</owner>
+  <owner>chrome-safebrowsing-alerts@google.com</owner>
+  <summary>
+    The current Safe Browsing state of the main profile. Recorded for all uma
+    log at upload time by the SafeBrowsingMetricsProvider for the main profile
+    only.
+  </summary>
+</histogram>
+
 <histogram name="SafeBrowsing.Pref.SawInterstitial" enum="Boolean"
     expires_after="2021-01-30">
   <owner>vakh@chromium.org</owner>
diff --git a/tools/metrics/structured/codegen.py b/tools/metrics/structured/codegen.py
index d6856a9..61b1d45 100644
--- a/tools/metrics/structured/codegen.py
+++ b/tools/metrics/structured/codegen.py
@@ -104,7 +104,7 @@
       self.type = 'std::string&'
       self.setter = 'AddStringMetric'
     elif metric.type == 'int':
-      self.type = 'int'
+      self.type = 'int64_t'
       self.setter = 'AddIntMetric'
     else:
       raise ValueError('Invalid metric type.')
diff --git a/tools/perf/core/perfetto_binary_roller/binary_deps.json b/tools/perf/core/perfetto_binary_roller/binary_deps.json
index 56243d2..71026890 100644
--- a/tools/perf/core/perfetto_binary_roller/binary_deps.json
+++ b/tools/perf/core/perfetto_binary_roller/binary_deps.json
@@ -2,7 +2,7 @@
     "trace_processor_shell": {
         "win": {
             "hash": "23cff3007ff25abed77bdaa20c104a0cfdb12219",
-            "remote_path": "perfetto_binaries/trace_processor_shell/win/a4d450e81e396a2688a422bcb789dbc82ba67537/trace_processor_shell.exe"
+            "remote_path": "perfetto_binaries/trace_processor_shell/win/441fd9bb2503748e5f904f81b728d5b61fd1f997/trace_processor_shell.exe"
         },
         "mac": {
             "hash": "72fc3915dddc48d4d1a256acf8eada25c740eb9e",
@@ -10,7 +10,7 @@
         },
         "linux": {
             "hash": "b95b2b75294ef7c5ed96e53582c5d8f0d4e575bd",
-            "remote_path": "perfetto_binaries/trace_processor_shell/linux/a4d450e81e396a2688a422bcb789dbc82ba67537/trace_processor_shell"
+            "remote_path": "perfetto_binaries/trace_processor_shell/linux/441fd9bb2503748e5f904f81b728d5b61fd1f997/trace_processor_shell"
         }
     },
     "power_profile.sql": {
diff --git a/ui/accessibility/platform/inspect/ax_tree_formatter_base.cc b/ui/accessibility/platform/inspect/ax_tree_formatter_base.cc
index 6090b83..c0496a8 100644
--- a/ui/accessibility/platform/inspect/ax_tree_formatter_base.cc
+++ b/ui/accessibility/platform/inspect/ax_tree_formatter_base.cc
@@ -8,6 +8,7 @@
 #include "base/strings/string_util.h"
 #include "base/strings/stringprintf.h"
 #include "ui/accessibility/ax_tree_id.h"
+#include "ui/accessibility/platform/ax_platform_node_delegate.h"
 #include "ui/accessibility/platform/inspect/ax_property_node.h"
 
 namespace ui {
@@ -29,6 +30,26 @@
 const char AXTreeFormatterBase::kChildrenDictAttr[] = "children";
 const char AXTreeFormatterBase::kScriptsDictAttr[] = "scripts";
 
+bool AXTreeFormatterBase::ShouldDumpNode(
+    const AXPlatformNodeDelegate& node) const {
+  for (const std::pair<ax::mojom::StringAttribute, std::string>&
+           string_attribute : node.GetData().string_attributes) {
+    if (string_attribute.second.find(kSkipString) != std::string::npos)
+      return false;
+  }
+  return true;
+}
+
+bool AXTreeFormatterBase::ShouldDumpChildren(
+    const AXPlatformNodeDelegate& node) const {
+  for (const std::pair<ax::mojom::StringAttribute, std::string>&
+           string_attribute : node.GetData().string_attributes) {
+    if (string_attribute.second.find(kSkipChildren) != std::string::npos)
+      return false;
+  }
+  return true;
+}
+
 std::string AXTreeFormatterBase::Format(AXPlatformNodeDelegate* root) const {
   DCHECK(root);
   return FormatTree(BuildTree(root));
@@ -76,9 +97,15 @@
   if (MatchesNodeFilters(dict))
     return;
 
+  if (dict.DictEmpty())
+    return;
+
   std::string indent = std::string(depth * kIndentSymbolCount, kIndentSymbol);
   std::string line =
       indent + ProcessTreeForOutput(base::Value::AsDictionaryValue(dict));
+
+  // TODO(accessibility): This can be removed once the UIA tree formatter
+  // can call ShouldDumpNode().
   if (line.find(kSkipString) != std::string::npos)
     return;
 
@@ -89,6 +116,9 @@
   base::ReplaceChars(line, "\n", "<newline>", &line);
 
   *contents += line + "\n";
+
+  // TODO(accessibility): This can be removed once the UIA tree formatter
+  // can call ShouldDumpChildren().
   if (line.find(kSkipChildren) != std::string::npos)
     return;
 
diff --git a/ui/accessibility/platform/inspect/ax_tree_formatter_base.h b/ui/accessibility/platform/inspect/ax_tree_formatter_base.h
index 45e80bf..b53da12 100644
--- a/ui/accessibility/platform/inspect/ax_tree_formatter_base.h
+++ b/ui/accessibility/platform/inspect/ax_tree_formatter_base.h
@@ -23,6 +23,9 @@
   AXTreeFormatterBase();
   ~AXTreeFormatterBase() override;
 
+  bool ShouldDumpNode(const AXPlatformNodeDelegate& node) const;
+  bool ShouldDumpChildren(const AXPlatformNodeDelegate& node) const;
+
   // Dumps formatted the given accessibility tree into a string.
   std::string Format(AXPlatformNodeDelegate* root) const override;
 
diff --git a/ui/color/color_id.h b/ui/color/color_id.h
index a2c8b44..e07d44b 100644
--- a/ui/color/color_id.h
+++ b/ui/color/color_id.h
@@ -38,8 +38,6 @@
   E(kColorAvatarIconIncognito, NativeTheme::kColorId_AvatarIconIncognito) \
   E(kColorBubbleBackground, NativeTheme::kColorId_BubbleBackground) \
   E(kColorBubbleBorder, NativeTheme::kColorId_BubbleBorder) \
-  E(kColorBubbleBorderShadowBase, \
-    NativeTheme::kColorId_BubbleBorderShadowBase) \
   E(kColorBubbleBorderShadowLarge, \
     NativeTheme::kColorId_BubbleBorderShadowLarge) \
   E(kColorBubbleBorderShadowSmall, \
@@ -160,6 +158,15 @@
   E(kColorPwaToolbarForeground, \
     NativeTheme::kColorId_CustomTabBarForegroundColor) \
   E(kColorSeparator, NativeTheme::kColorId_SeparatorColor) \
+  E(kColorShadowBase, NativeTheme::kColorId_ShadowBase) \
+  E(kColorShadowValueAmbientShadowElevationThree, \
+    NativeTheme::kColorId_ShadowValueAmbientShadowElevationThree) \
+  E(kColorShadowValueKeyShadowElevationThree, \
+    NativeTheme::kColorId_ShadowValueKeyShadowElevationThree) \
+  E(kColorShadowValueAmbientShadowElevationSixteen, \
+    NativeTheme::kColorId_ShadowValueAmbientShadowElevationSixteen) \
+  E(kColorShadowValueKeyShadowElevationSixteen, \
+    NativeTheme::kColorId_ShadowValueKeyShadowElevationSixteen) \
   E(kColorSliderThumb, NativeTheme::kColorId_SliderThumbDefault) \
   E(kColorSliderThumbMinimal, NativeTheme::kColorId_SliderThumbMinimal) \
   E(kColorSliderTrack, NativeTheme::kColorId_SliderTroughDefault) \
diff --git a/ui/color/ui_color_mixer.cc b/ui/color/ui_color_mixer.cc
index cba05290..17e96a9 100644
--- a/ui/color/ui_color_mixer.cc
+++ b/ui/color/ui_color_mixer.cc
@@ -21,12 +21,8 @@
   mixer[kColorAvatarIconIncognito] = {kColorPrimaryForeground};
   mixer[kColorBubbleBackground] = {kColorPrimaryBackground};
   mixer[kColorBubbleBorder] = {kColorMidground};
-  mixer[kColorBubbleBorderShadowBase] = {dark_window ? SK_ColorBLACK
-                                                     : gfx::kGoogleGrey800};
-  mixer[kColorBubbleBorderShadowLarge] = {
-      SetAlpha(kColorBubbleBorderShadowBase, 0x1A)};
-  mixer[kColorBubbleBorderShadowSmall] = {
-      SetAlpha(kColorBubbleBorderShadowBase, 0x33)};
+  mixer[kColorBubbleBorderShadowLarge] = {SetAlpha(kColorShadowBase, 0x1A)};
+  mixer[kColorBubbleBorderShadowSmall] = {SetAlpha(kColorShadowBase, 0x33)};
   mixer[kColorBubbleBorderWhenShadowPresent] = {SetAlpha(SK_ColorBLACK, 0x26)};
   mixer[kColorBubbleFooterBackground] = {kColorSubtleEmphasisBackground};
   mixer[kColorBubbleFooterBorder] = {kColorMidground};
@@ -117,6 +113,15 @@
   mixer[kColorPwaToolbarBackground] = {kColorEndpointBackground};
   mixer[kColorPwaToolbarForeground] = {kColorEndpointForeground};
   mixer[kColorSeparator] = {kColorMidground};
+  mixer[kColorShadowBase] = {dark_window ? SK_ColorBLACK : gfx::kGoogleGrey800};
+  mixer[kColorShadowValueAmbientShadowElevationThree] =
+      SetAlpha(kColorShadowBase, 0x40);
+  mixer[kColorShadowValueKeyShadowElevationThree] =
+      SetAlpha(kColorShadowBase, 0x66);
+  mixer[kColorShadowValueAmbientShadowElevationSixteen] =
+      SetAlpha(kColorShadowBase, 0x3d);
+  mixer[kColorShadowValueKeyShadowElevationSixteen] =
+      SetAlpha(kColorShadowBase, 0x1a);
   mixer[kColorSliderThumb] = {kColorAccent};
   mixer[kColorSliderThumbMinimal] = {kColorSecondaryForeground};
   mixer[kColorSliderTrack] = {kColorSubtleAccent};
diff --git a/ui/display/manager/display_change_observer.cc b/ui/display/manager/display_change_observer.cc
index 059ed70..311cfe9 100644
--- a/ui/display/manager/display_change_observer.cc
+++ b/ui/display/manager/display_change_observer.cc
@@ -424,11 +424,14 @@
   constexpr gfx::Size k225DisplaySizeHackNocturne(3000, 2000);
   // Keep the Chell's scale factor 2.252 until we make decision.
   constexpr gfx::Size k2DisplaySizeHackChell(3200, 1800);
+  constexpr gfx::Size k18DisplaySizeHackCoachZ(2160, 1440);
 
   if (size_in_pixels == k225DisplaySizeHackNocturne) {
     return kDsf_2_252;
   } else if (size_in_pixels == k2DisplaySizeHackChell) {
     return 2.f;
+  } else if (size_in_pixels == k18DisplaySizeHackCoachZ) {
+    return kDsf_1_8;
   } else {
     for (size_t i = 0; i < base::size(kThresholdTableForInternal); ++i) {
       if (dpi >= kThresholdTableForInternal[i].dpi)
diff --git a/ui/display/manager/display_change_observer_unittest.cc b/ui/display/manager/display_change_observer_unittest.cc
index 04b41e6..05ae1a7b 100644
--- a/ui/display/manager/display_change_observer_unittest.cc
+++ b/ui/display/manager/display_change_observer_unittest.cc
@@ -5,7 +5,9 @@
 #include "ui/display/manager/display_change_observer.h"
 
 #include <cmath>
+#include <set>
 #include <string>
+#include <tuple>
 
 #include "base/strings/stringprintf.h"
 #include "base/test/scoped_feature_list.h"
@@ -235,10 +237,19 @@
   EXPECT_EQ(kDsf_2_666,
             DisplayChangeObserver::FindDeviceScaleFactor(310, gfx::Size()));
 
+  std::set<std::tuple<float, int, int>> dup_check;
+
   for (auto& entry : display_configs) {
+    std::tuple<float, int, int> key{entry.diagonal_size,
+                                    entry.resolution.width(),
+                                    entry.resolution.height()};
+    DCHECK(!dup_check.count(key));
+    dup_check.emplace(key);
+
     SCOPED_TRACE(base::StringPrintf(
         "%dx%d, diag=%1.3f inch, expected=%1.10f", entry.resolution.width(),
         entry.resolution.height(), entry.diagonal_size, entry.expected_dsf));
+
     float dpi = ComputeDpi(entry.diagonal_size, entry.resolution);
     // Check ScaleFactor.
     float scale_factor = ComputeDeviceScaleFactor(dpi, entry.resolution);
@@ -539,10 +550,20 @@
     }
   }
 
-  // With the current set of display configs and zoom levels, there are only 288
+#if 0
+  // Enable this code to re-generate the "EffectiveResolution" in enums.xml.
+  for (auto pair : effective_resolutions) {
+    LOG(ERROR) << "<int value=\"" << pair.first << "\" label=\""
+               << pair.second.width() << " x " << pair.second.height()
+               << "\"/>";
+  }
+#endif
+
+  // With the current set of display configs and zoom levels, there are only 314
   // possible effective resolutions for internal displays in chromebooks. Update
-  // this value when adding a new display config.
-  EXPECT_EQ(effective_resolutions.size(), 288ul);
+  // this value when adding a new display config, and re-generate the
+  // EffectiveResolution value in enum.xml.
+  EXPECT_EQ(effective_resolutions.size(), 314ul);
 }
 #endif
 
diff --git a/ui/display/types/display_constants.h b/ui/display/types/display_constants.h
index ab139363..59150bb7 100644
--- a/ui/display/types/display_constants.h
+++ b/ui/display/types/display_constants.h
@@ -95,10 +95,11 @@
 constexpr float kDsf_1_777 = 1.77777779102325439453125f;
 constexpr float kDsf_2_252 = 2.2522523403167724609375f;
 constexpr float kDsf_2_666 = 2.6666667461395263671875f;
-
+constexpr float kDsf_1_8 = 1.80000007152557373046875f;
 constexpr char kDsfStr_1_777[] = "1.77777779102325439453125";
 constexpr char kDsfStr_2_252[] = "2.2522523403167724609375";
 constexpr char kDsfStr_2_666[] = "2.6666667461395263671875";
+constexpr char kDsfStr_1_8[] = "1.80000007152557373046875";
 
 // The total number of display zoom factors to enumerate.
 constexpr int kNumOfZoomFactors = 9;
@@ -149,13 +150,14 @@
 constexpr gfx::Size kWXGA_800{1280, 800};
 constexpr gfx::Size kHD_PLUS{1600, 900};
 constexpr gfx::Size kFHD{1920, 1080};
+constexpr gfx::Size kSHD{1280, 720};
 constexpr gfx::Size kWUXGA{1920, 1200};
 // Dru
 constexpr gfx::Size kQXGA_P{1536, 2048};
 constexpr gfx::Size kQHD{2560, 1440};
 // Chell
 constexpr gfx::Size kQHD_PLUS{3200, 1800};
-constexpr gfx::Size kUHD{3840, 2160};
+constexpr gfx::Size k4K_UHD{3840, 2160};
 
 // Chromebook special panels
 constexpr gfx::Size kLux{2160, 1440};
@@ -180,32 +182,48 @@
 } display_configs[] = {
     // clang-format off
     // inch, resolution, DSF,        size in DP,  Bad range, size error
-    {10.1f,  kWXGA_800,  1.f,        kWXGA_800,   false,     kExact},
-    {12.1f,  kWXGA_800,  1.f,       kWXGA_800,   true,      kExact},
-    {11.6f,  kWXGA_768,  1.f,        kWXGA_768,   false,     kExact},
-    {13.3f,  kWXGA_768,  1.f,        kWXGA_768,   true,      kExact},
-    {14.f,   kWXGA_768,  1.f,        kWXGA_768,   true,      kExact},
-    {15.6f,  kWXGA_768,  1.f,        kWXGA_768,   true,      kExact},
     {9.7f,   kQXGA_P,    2.0f,       {768, 1024}, false,     kExact},
-    {11.6f,  kFHD,       1.6f,       {1200, 675}, false,     kExact},
-    {13.0f,  kFHD,       1.25f,      {1536, 864}, true,      kExact},
-    {13.3f,  kFHD,       1.25f,      {1536, 864}, true,      kExact},
-    {14.f,   kFHD,       1.25f,      {1536, 864}, false,     kExact},
+    {10.f,   kWXGA_800,  1.25f,      {1024, 640}, false,     kExact},
+    {10.1f,  kWXGA_800,  1.f,        kWXGA_800,   false,     kExact},
+    {10.1f,  kFHD,       1.6,        {1200, 675}, true,      kExact},
     {10.1f,  kWUXGA,     kDsf_1_777, {1080, 675}, false,     kExact},
+    {10.5f,  kWUXGA,     1.6f,       {1200, 750}, true,      kExact},
+    {11.6f,  kWXGA_768,  1.f,        kWXGA_768,   false,     kExact},
+    {11.6f,  kSHD,       1.f,        kSHD,        false,     kExact},
+    {11.6f,  kFHD,       1.6f,       {1200, 675}, false,     kExact},
+    {12.f,   kFHD,       1.6f,       {1200, 675}, false,     kExact},
+    {12.1f,  kWXGA_800,  1.f,        kWXGA_800,   true,      kExact},
     {12.2f,  kWUXGA,     1.6f,       {1200, 750}, false,     kExact},
-    {15.6f,  kWUXGA,     1.f,        kWUXGA,      false,     kExact},
+    {12.2f,  kFHD,       1.6f,       {1200, 675}, false,     kExact},
     {12.3f,  kQHD,       2.f,        {1280, 720}, false,     kExact},
+    {13.0f,  kFHD,       1.25f,      {1536, 864}, true,      kExact},
+    {13.1f,  k4K_UHD,    kDsf_2_666, {1440, 810}, false,     kExact},
+    {13.3f,  kWXGA_768,  1.f,        kWXGA_768,   true,      kExact},
+    {13.3f,  kFHD,       1.25f,      {1536, 864}, true,      kExact},
+    {13.3f,  k4K_UHD,    kDsf_2_666, {1440, 810}, false,     kExact},
+    {13.5f,  kFHD,       1.25f,      {1536, 864}, false,     kExact},
+    {14.f,   kWXGA_768,  1.f,        kWXGA_768,   true,      kExact},
+    {14.f,   kFHD,       1.25f,      {1536, 864}, false,     kExact},
+    {14.f,   kWUXGA,     1.25f,      {1536, 960}, false,     kExact},
+    {14.f,   k4K_UHD,    kDsf_2_666, {1440, 810}, false,     kExact},
+    {15.6f,  kWXGA_768,  1.f,        kWXGA_768,   true,      kExact},
+    {15.6f,  kWUXGA,     1.f,        kWUXGA,      false,     kExact},
+    {15.6f,  kFHD,       1.f,        kFHD,        false,     kExact},
+    {15.6f,  k4K_UHD,    2.4f,       {1600, 900}, false,     kEpsilon},
+    {17.f,   kHD_PLUS,   1.f,        kHD_PLUS,    true,      kExact},
+    {17.f,   kFHD,       1.0f,       {1920, 1080},false,     kExact},
+    {17.3f,  kFHD,       1.0f,       {1920, 1080},false,     kExact},
+    {18.51f, kWXGA_768,  1.0f,       kWXGA_768,   true,      kExact},
 
     // Non standard panels
-    {11.0f,  kLux,       2.f,        {1080, 720}, false,     kExact},
-    {12.02f, kLux,       1.6f,       {1350, 900}, true,      kExact},
-    {13.3f,  kQHD_PLUS,  2.f,        {1600, 900}, false,     kSkip},
-    {13.3f,  kAkaliQHD,  1.6f,       {1410, 940}, false,     kExact},
+    {11.0f,  kLux,       kDsf_1_8,   {1200, 800}, false,     kExact},
+    {12.f,   {1366, 912},1.f,        {1366, 912}, false,     kExact},
     {12.3f,  kEveDisplay,2.0f,       {1200, 800}, false,     kExact},
     {12.85f, kLink,      2.0f,       {1280, 850}, false,     kExact},
     {12.3f,  kNocturne,  kDsf_2_252, {1332, 888}, false,     kEpsilon},
-    {13.1f,  kUHD,       kDsf_2_666, {1440, 810}, false,     kExact},
-    {15.6f,  kUHD,       2.4f,       {1600, 900}, false,     kEpsilon},
+    {13.3f,  kQHD_PLUS,  2.f,        {1600, 900}, false,     kExact},
+    {13.3f,  kAkaliQHD,  1.6f,       {1410, 940}, false,     kExact},
+    {13.6f,  kAkaliQHD,  1.6f,       {1410, 940}, false,     kExact},
 
     // Chromebase
     {19.5,   kHD_PLUS,   1.f,        kHD_PLUS,    true,      kExact},
diff --git a/ui/gfx/shadow_value.cc b/ui/gfx/shadow_value.cc
index c88ba11d..d71ecfd 100644
--- a/ui/gfx/shadow_value.cc
+++ b/ui/gfx/shadow_value.cc
@@ -8,6 +8,7 @@
 
 #include <algorithm>
 
+#include "base/check_op.h"
 #include "base/numerics/safe_conversions.h"
 #include "base/strings/stringprintf.h"
 #include "ui/gfx/color_palette.h"
@@ -71,7 +72,9 @@
 }
 
 // static
-ShadowValues ShadowValue::MakeShadowValues(int elevation, SkColor color) {
+ShadowValues ShadowValue::MakeShadowValues(int elevation,
+                                           SkColor key_shadow_color,
+                                           SkColor ambient_shadow_color) {
   // Refresh uses hand-tweaked shadows corresponding to a small set of
   // elevations. Use the Refresh spec and designer input to add missing shadow
   // values.
@@ -83,21 +86,22 @@
 
   switch (elevation) {
     case 3: {
-      ShadowValue key = {Vector2d(0, 1), 12, SkColorSetA(color, 0x66)};
-      ShadowValue ambient = {Vector2d(0, 4), 64, SkColorSetA(color, 0x40)};
+      ShadowValue key = {Vector2d(0, 1), 12, key_shadow_color};
+      ShadowValue ambient = {Vector2d(0, 4), 64, ambient_shadow_color};
       return {key, ambient};
     }
     case 16: {
       ShadowValue key = {Vector2d(0, 0), kBlurCorrection * 16,
-                         SkColorSetA(color, 0x1a)};
+                         key_shadow_color};
       ShadowValue ambient = {Vector2d(0, 12), kBlurCorrection * 16,
-                             SkColorSetA(color, 0x3d)};
+                             ambient_shadow_color};
       return {key, ambient};
     }
     default:
       // This surface has not been updated for Refresh. Fall back to the
       // deprecated style.
-      return MakeMdShadowValues(elevation, color);
+      DCHECK_EQ(key_shadow_color, ambient_shadow_color);
+      return MakeMdShadowValues(elevation, key_shadow_color);
   }
 }
 
diff --git a/ui/gfx/shadow_value.h b/ui/gfx/shadow_value.h
index 67924a8..fdb8175 100644
--- a/ui/gfx/shadow_value.h
+++ b/ui/gfx/shadow_value.h
@@ -54,8 +54,15 @@
   // a uniform color.
   static Insets GetBlurRegion(const ShadowValues& shadows);
 
-  // Makes ShadowValues for the given elevation and color.
-  static ShadowValues MakeShadowValues(int elevation, SkColor color);
+  // Makes ShadowValues for the given elevation and color. Calls to
+  // MakeShadowValues that expect to fallback to MakeMdShadowValues should pass
+  // in the same base color for |key_shadow_color| and |ambient_shadow_color|
+  // until MakeMdShadowValues is refactored to remove SkColorSetA calls and also
+  // take in its own |key_shadow_color| and |ambient_shadow_color|.
+  // TODO(elainechien): crbug.com/1056950.
+  static ShadowValues MakeShadowValues(int elevation,
+                                       SkColor key_shadow_color,
+                                       SkColor ambient_shadow_color);
   // Makes ShadowValues for MD shadows. This style is deprecated.
   static ShadowValues MakeMdShadowValues(int elevation,
                                          SkColor color = SK_ColorBLACK);
diff --git a/ui/native_theme/common_theme.cc b/ui/native_theme/common_theme.cc
index 4436af30..043ace0 100644
--- a/ui/native_theme/common_theme.cc
+++ b/ui/native_theme/common_theme.cc
@@ -68,10 +68,6 @@
     case NativeTheme::kColorId_FocusedBorderColor:
       return gfx::kGoogleBlue400;
 
-    // Bubble
-    case NativeTheme::kColorId_BubbleBorderShadowBase:
-      return SK_ColorBLACK;
-
     // Button
     case NativeTheme::kColorId_ProminentButtonColor:
       return gfx::kGoogleBlue300;
@@ -88,6 +84,10 @@
     case NativeTheme::kColorId_LabelEnabledColor:
       return gfx::kGoogleGrey200;
 
+    // Shadow
+    case NativeTheme::kColorId_ShadowBase:
+      return SK_ColorBLACK;
+
     // Separator
     case NativeTheme::kColorId_SeparatorColor:
       return gfx::kGoogleGrey800;
@@ -168,18 +168,14 @@
     case NativeTheme::kColorId_BubbleBorder:
       return base_theme->GetUnprocessedSystemColor(
           NativeTheme::kColorId_SeparatorColor, color_scheme);
-    case NativeTheme::kColorId_BubbleBorderShadowBase:
-      return gfx::kGoogleGrey800;
     case NativeTheme::kColorId_BubbleBorderShadowLarge:
-      return SkColorSetA(
-          base_theme->GetUnprocessedSystemColor(
-              NativeTheme::kColorId_BubbleBorderShadowBase, color_scheme),
-          0x1A);
+      return SkColorSetA(base_theme->GetUnprocessedSystemColor(
+                             NativeTheme::kColorId_ShadowBase, color_scheme),
+                         0x1A);
     case NativeTheme::kColorId_BubbleBorderShadowSmall:
-      return SkColorSetA(
-          base_theme->GetUnprocessedSystemColor(
-              NativeTheme::kColorId_BubbleBorderShadowBase, color_scheme),
-          0x33);
+      return SkColorSetA(base_theme->GetUnprocessedSystemColor(
+                             NativeTheme::kColorId_ShadowBase, color_scheme),
+                         0x33);
     case NativeTheme::kColorId_BubbleBorderWhenShadowPresent:
       return SkColorSetA(SK_ColorBLACK, 0x26);
     // Button
@@ -406,6 +402,27 @@
     case NativeTheme::kColorId_SeparatorColor:
       return gfx::kGoogleGrey300;
 
+    // Shadow
+    case NativeTheme::kColorId_ShadowBase:
+      return gfx::kGoogleGrey800;
+
+    case NativeTheme::kColorId_ShadowValueAmbientShadowElevationThree:
+      return SkColorSetA(base_theme->GetUnprocessedSystemColor(
+                             NativeTheme::kColorId_ShadowBase, color_scheme),
+                         0x40);
+    case NativeTheme::kColorId_ShadowValueKeyShadowElevationThree:
+      return SkColorSetA(base_theme->GetUnprocessedSystemColor(
+                             NativeTheme::kColorId_ShadowBase, color_scheme),
+                         0x66);
+    case NativeTheme::kColorId_ShadowValueAmbientShadowElevationSixteen:
+      return SkColorSetA(base_theme->GetUnprocessedSystemColor(
+                             NativeTheme::kColorId_ShadowBase, color_scheme),
+                         0x3d);
+    case NativeTheme::kColorId_ShadowValueKeyShadowElevationSixteen:
+      return SkColorSetA(base_theme->GetUnprocessedSystemColor(
+                             NativeTheme::kColorId_ShadowBase, color_scheme),
+                         0x1a);
+
     // Slider
     case NativeTheme::kColorId_SliderThumbMinimal:
       return base_theme->GetUnprocessedSystemColor(
diff --git a/ui/native_theme/native_theme_color_id.h b/ui/native_theme/native_theme_color_id.h
index 64e6d42..84848edb 100644
--- a/ui/native_theme/native_theme_color_id.h
+++ b/ui/native_theme/native_theme_color_id.h
@@ -107,6 +107,12 @@
   OP(kColorId_SliderTroughMinimal),                                            \
   /* Separator */                                                              \
   OP(kColorId_SeparatorColor),                                                 \
+  /* Shadow */                                                                 \
+  OP(kColorId_ShadowBase),                                                     \
+  OP(kColorId_ShadowValueAmbientShadowElevationThree),                         \
+  OP(kColorId_ShadowValueKeyShadowElevationThree),                             \
+  OP(kColorId_ShadowValueAmbientShadowElevationSixteen),                       \
+  OP(kColorId_ShadowValueKeyShadowElevationSixteen),                           \
   /* Sync info container */                                                    \
   OP(kColorId_SyncInfoContainerPaused),                                        \
   OP(kColorId_SyncInfoContainerError),                                         \
@@ -156,7 +162,6 @@
   OP(kColorId_ThrobberWaitingColor),                                           \
   /* Colors for Bubble Border */                                               \
   OP(kColorId_BubbleBorder),                                                   \
-  OP(kColorId_BubbleBorderShadowBase),                                         \
   OP(kColorId_BubbleBorderShadowLarge),                                        \
   OP(kColorId_BubbleBorderShadowSmall),                                        \
   OP(kColorId_BubbleBorderWhenShadowPresent),                                  \
diff --git a/ui/native_theme/native_theme_utils.cc b/ui/native_theme/native_theme_utils.cc
index 2ed2a1a..d6fc156 100644
--- a/ui/native_theme/native_theme_utils.cc
+++ b/ui/native_theme/native_theme_utils.cc
@@ -53,8 +53,6 @@
         {NTCID::kColorId_AvatarIconIncognito, kColorAvatarIconIncognito},
         {NTCID::kColorId_BubbleBackground, kColorBubbleBackground},
         {NTCID::kColorId_BubbleBorder, kColorBubbleBorder},
-        {NTCID::kColorId_BubbleBorderShadowBase,
-          kColorBubbleBorderShadowBase},
         {NTCID::kColorId_BubbleBorderShadowLarge,
           kColorBubbleBorderShadowLarge},
         {NTCID::kColorId_BubbleBorderShadowSmall,
@@ -166,6 +164,15 @@
         {NTCID::kColorId_SelectedMenuItemForegroundColor,
           kColorMenuItemForegroundSelected},
         {NTCID::kColorId_SeparatorColor, kColorSeparator},
+        {NTCID::kColorId_ShadowBase, kColorShadowBase},
+        {NTCID::kColorId_ShadowValueAmbientShadowElevationThree,
+          kColorShadowValueAmbientShadowElevationThree},
+        {NTCID::kColorId_ShadowValueKeyShadowElevationThree,
+          kColorShadowValueKeyShadowElevationThree},
+        {NTCID::kColorId_ShadowValueAmbientShadowElevationSixteen,
+          kColorShadowValueAmbientShadowElevationSixteen},
+        {NTCID::kColorId_ShadowValueKeyShadowElevationSixteen,
+          kColorShadowValueKeyShadowElevationSixteen},
         {NTCID::kColorId_SliderThumbDefault, kColorSliderThumb},
         {NTCID::kColorId_SliderThumbMinimal, kColorSliderThumbMinimal},
         {NTCID::kColorId_SliderTroughDefault, kColorSliderTrack},
diff --git a/ui/views/bubble/bubble_border.cc b/ui/views/bubble/bubble_border.cc
index a91df76..5ebe246 100644
--- a/ui/views/bubble/bubble_border.cc
+++ b/ui/views/bubble/bubble_border.cc
@@ -55,6 +55,40 @@
   return gfx::Point(rect.right(), rect.CenterPoint().y());
 }
 
+SkColor GetKeyShadowColor(int elevation, const ui::NativeTheme* theme) {
+  switch (elevation) {
+    case 3: {
+      return theme->GetSystemColor(
+          ui::NativeTheme::kColorId_ShadowValueKeyShadowElevationThree);
+    }
+    case 16: {
+      return theme->GetSystemColor(
+          ui::NativeTheme::kColorId_ShadowValueKeyShadowElevationSixteen);
+    }
+    default:
+      // This surface has not been updated for Refresh. Fall back to the
+      // deprecated style.
+      return theme->GetSystemColor(ui::NativeTheme::kColorId_ShadowBase);
+  }
+}
+
+SkColor GetAmbientShadowColor(int elevation, const ui::NativeTheme* theme) {
+  switch (elevation) {
+    case 3: {
+      return theme->GetSystemColor(
+          ui::NativeTheme::kColorId_ShadowValueAmbientShadowElevationThree);
+    }
+    case 16: {
+      return theme->GetSystemColor(
+          ui::NativeTheme::kColorId_ShadowValueAmbientShadowElevationSixteen);
+    }
+    default:
+      // This surface has not been updated for Refresh. Fall back to the
+      // deprecated style.
+      return theme->GetSystemColor(ui::NativeTheme::kColorId_ShadowBase);
+  }
+}
+
 }  // namespace
 
 BubbleBorder::BubbleBorder(Arrow arrow, Shadow shadow, SkColor color)
@@ -240,11 +274,12 @@
     const ui::NativeTheme* theme,
     base::Optional<int> elevation) {
   // If the theme does not exist the shadow values are being created in
-  // order to calculate Insets. In that case the color plays no role so set it
-  // to gfx::kPlaceholderColor.
-  SkColor color = theme ? theme->GetSystemColor(
-                              ui::NativeTheme::kColorId_BubbleBorderShadowBase)
-                        : gfx::kPlaceholderColor;
+  // order to calculate Insets. In that case the color plays no role so always
+  // set those colors to gfx::kPlaceholderColor.
+
+  SkColor color =
+      theme ? theme->GetSystemColor(ui::NativeTheme::kColorId_ShadowBase)
+            : gfx::kPlaceholderColor;
 
   // The shadows are always the same for any elevation and color combination, so
   // construct them once and cache.
@@ -258,7 +293,14 @@
   gfx::ShadowValues shadows;
   if (elevation.has_value()) {
     DCHECK_GE(elevation.value(), 0);
-    shadows = gfx::ShadowValue::MakeShadowValues(elevation.value(), color);
+    SkColor key_shadow_color = theme
+                                   ? GetKeyShadowColor(elevation.value(), theme)
+                                   : gfx::kPlaceholderColor;
+    SkColor ambient_shadow_color =
+        theme ? GetAmbientShadowColor(elevation.value(), theme)
+              : gfx::kPlaceholderColor;
+    shadows = gfx::ShadowValue::MakeShadowValues(
+        elevation.value(), key_shadow_color, ambient_shadow_color);
   } else {
     constexpr int kSmallShadowVerticalOffset = 2;
     constexpr int kSmallShadowBlur = 4;
@@ -294,7 +336,7 @@
   static base::NoDestructor<std::map<ShadowCacheKey, cc::PaintFlags>> flag_map;
   ShadowCacheKey key(
       elevation.value_or(-1),
-      theme->GetSystemColor(ui::NativeTheme::kColorId_BubbleBorderShadowBase));
+      theme->GetSystemColor(ui::NativeTheme::kColorId_ShadowBase));
 
   if (flag_map->find(key) != flag_map->end())
     return flag_map->find(key)->second;