diff --git a/AUTHORS b/AUTHORS
index 364df55a..3b74ab59 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -47,6 +47,7 @@
 Alec Petridis <alecthechop@gmail.com>
 Aleksandar Stojiljkovic <aleksandar.stojiljkovic@intel.com>
 Aleksei Gurianov <gurianov@gmail.com>
+Aleksey Khoroshilov <akhoroshilov@brave.com>
 Alesandro Ortiz <alesandro@alesandroortiz.com>
 Alex Chronopoulos <achronop@gmail.com>
 Alex Gabriel <minilogo@gmail.com>
diff --git a/DEPS b/DEPS
index 5c487ac..68580b0 100644
--- a/DEPS
+++ b/DEPS
@@ -253,7 +253,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling Skia
   # and whatever else without interference from each other.
-  'skia_revision': 'c6e373d47d5172544ed45dba717d1963bca743b5',
+  'skia_revision': 'e69791f92f11d12677b949b723cc9a5cffd0ce0d',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling V8
   # and whatever else without interference from each other.
@@ -261,11 +261,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': '1e1505b53cf065466f21424b5279ace31570ff4c',
+  'angle_revision': '240dcdff9af96217034f7500cfd91cecfa0691e2',
   # 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': '7021c484697491248e8336f1be45aa6d068a8b80',
+  'swiftshader_revision': '9f170f0717a7344a611c19fee90de258de02ecd5',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling PDFium
   # and whatever else without interference from each other.
@@ -320,7 +320,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling catapult
   # and whatever else without interference from each other.
-  'catapult_revision': '62a74cf243b4a63011f561ee10e57635a914df08',
+  'catapult_revision': '392c66f379f22799379b5d10d577bb2a73b2723c',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling libFuzzer
   # 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 devtools-frontend
   # and whatever else without interference from each other.
-  'devtools_frontend_revision': '628e9baa4c133d718cbf428b9337920c44f10b61',
+  'devtools_frontend_revision': 'e0b260259c7e267f882df29d66e53cc5a35ee1f1',
   # 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.
@@ -368,7 +368,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': '2335a974eff76ab3b396652f2a0f051f115ec08c',
+  'dawn_revision': '4570ab5ad89afa51bc03804d2817cf2b72380a60',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
@@ -1592,7 +1592,7 @@
       'packages': [
           {
               'package': 'fuchsia/third_party/android/aemu/release/linux-amd64',
-              'version': 'Lq0crmit6SHAoX9JV6lIzxREjsTFHYpEtIpiuUDOTCEC'
+              'version': 'Mde3bAvPPgfbH8nF27RZ4aKV2maS_0kVpAc8TWSKIAwC'
           },
       ],
       'condition': 'host_os == "linux" and checkout_fuchsia',
@@ -1735,7 +1735,7 @@
     Var('chromium_git') + '/external/github.com/gpuweb/cts.git' + '@' + '96e54a1b92e78f651941f64c703b98a34df37a1f',
 
   'src/third_party/webrtc':
-    Var('webrtc_git') + '/src.git' + '@' + '48cda468aa2343731e252a821f926b05411a1603',
+    Var('webrtc_git') + '/src.git' + '@' + 'c5200da92e1c93607de0a971506ad1b805bb262a',
 
   'src/third_party/libgifcodec':
      Var('skia_git') + '/libgifcodec' + '@'+  Var('libgifcodec_revision'),
@@ -1805,7 +1805,7 @@
     Var('chromium_git') + '/v8/v8.git' + '@' +  Var('v8_revision'),
 
   'src-internal': {
-    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@dfd275583c7e04bb78054d2f10af210669d2a362',
+    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@d9acd22f3ada60fafc9275b128107b6779e4dc05',
     'condition': 'checkout_src_internal',
   },
 
diff --git a/ash/BUILD.gn b/ash/BUILD.gn
index 6cfa4e1b..1b0c859 100644
--- a/ash/BUILD.gn
+++ b/ash/BUILD.gn
@@ -155,6 +155,8 @@
     "ambient/ambient_view_delegate_impl.cc",
     "ambient/ambient_view_delegate_impl.h",
     "ambient/autotest_ambient_api.cc",
+    "ambient/model/ambient_animation_attribution_provider.cc",
+    "ambient/model/ambient_animation_attribution_provider.h",
     "ambient/model/ambient_animation_photo_config.cc",
     "ambient/model/ambient_animation_photo_config.h",
     "ambient/model/ambient_animation_photo_provider.cc",
@@ -2318,6 +2320,7 @@
     "ambient/ambient_photo_cache_unittest.cc",
     "ambient/ambient_photo_controller_unittest.cc",
     "ambient/autotest_ambient_api_unittest.cc",
+    "ambient/model/ambient_animation_attribution_provider_unittest.cc",
     "ambient/model/ambient_animation_photo_config_unittest.cc",
     "ambient/model/ambient_animation_photo_provider_unittest.cc",
     "ambient/model/ambient_backend_model_unittest.cc",
@@ -2950,6 +2953,7 @@
   data = [
     # See DisplayColorManagerTest.
     "display/test_data/",
+    "//cc/test/data/",
   ]
 
   # Disallow depending directly on content.
diff --git a/ash/ambient/model/ambient_animation_attribution_provider.cc b/ash/ambient/model/ambient_animation_attribution_provider.cc
new file mode 100644
index 0000000..c42464c
--- /dev/null
+++ b/ash/ambient/model/ambient_animation_attribution_provider.cc
@@ -0,0 +1,107 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ash/ambient/model/ambient_animation_attribution_provider.h"
+
+#include "ash/ambient/model/ambient_backend_model.h"
+#include "ash/utility/lottie_util.h"
+#include "base/check.h"
+#include "base/containers/flat_set.h"
+#include "base/logging.h"
+#include "cc/paint/skottie_wrapper.h"
+#include "ui/lottie/animation.h"
+
+namespace ash {
+
+namespace {
+
+// Not all text nodes in the animation are necessarily ones that should hold
+// photo attribution. Some animations may have static text embedded in them.
+// Filter these out.
+//
+// The returned vector is intentionally sorted by the corresponding node name
+// in string format:
+// {
+//   hash("CrOS_AttributionNode1"),
+//   hash("CrOS_AttributionNode2"),
+//   ...,
+//   hash("CrOS_AttributionNodeN")
+// }
+// See "UX Guidance" below for why.
+std::vector<cc::SkottieResourceIdHash> GetAttributionNodeIds(
+    const cc::SkottieWrapper& skottie) {
+  base::flat_set<std::string> attribution_node_names_sorted;
+  for (const std::string& text_node_name : skottie.GetTextNodeNames()) {
+    if (IsCustomizableLottieId(text_node_name))
+      attribution_node_names_sorted.insert(text_node_name);
+  }
+  std::vector<cc::SkottieResourceIdHash> attribution_node_ids;
+  for (const std::string& attribution_node_name :
+       attribution_node_names_sorted) {
+    attribution_node_ids.push_back(
+        cc::HashSkottieResourceId(attribution_node_name));
+  }
+  return attribution_node_ids;
+}
+
+}  // namespace
+
+AmbientAnimationAttributionProvider::AmbientAnimationAttributionProvider(
+    AmbientAnimationPhotoProvider* photo_provider,
+    lottie::Animation* animation)
+    : animation_(animation),
+      attribution_node_ids_(GetAttributionNodeIds(*animation_->skottie())) {
+  DCHECK(animation_);
+  observation_.Observe(photo_provider);
+}
+
+AmbientAnimationAttributionProvider::~AmbientAnimationAttributionProvider() =
+    default;
+
+// UX Guidance:
+// 1) The dynamic image assets and attribution text nodes in an animation are
+//    identified by 2 different sets of strings. Ex:
+//    Dynamic image asset ids:
+//    * "CrOS_AssetId1"
+//    * "CrOS_AssetId2"
+//    ...
+//    * "CrOS_AssetIdN"
+//    Attribution text node names:
+//    * "CrOS_AttributionNode1"
+//    * "CrOS_AttributionNode2"
+//    ...
+//    * "CrOS_AttributionNodeN"
+//    Each attribution text node should be assigned the attribution of the
+//    dynamic image asset who shares the same "index". "CrOS_AttributionNode1"
+//    is assigned the attribution for "CrOS_AssetId1", "CrOS_AttributionNode2"
+//    is assigned the attribution for "CrOS_AssetId2", and so on. This is easily
+//    accomplished by sorting the asset ids and attribution nodes as strings
+//    first, then iterating through them simultaneously when assigning. That is
+//    why |new_topics| is a flat_map (whose keys are inherently sorted), and
+//    GetAttributionNodeIds() returns the attribution node ids sorted by their
+//    string names.
+//
+// 2) If a photo has no attribution (an empty string), just set its
+//    corresponding text node to be blank (an empty string). This is a
+//    corner case though. In practice, either all of the photos in the set
+//    should have an associated attribution, or none do.
+void AmbientAnimationAttributionProvider::OnDynamicImageAssetsRefreshed(
+    const base::flat_map</*asset_id*/ std::string,
+                         std::reference_wrapper<const PhotoWithDetails>>&
+        new_topics) {
+  DCHECK_EQ(new_topics.size(), attribution_node_ids_.size())
+      << "All ambient-mode animations should have an equal number of text "
+         "attribution nodes and dynamic image assets.";
+  auto new_topics_iter = new_topics.begin();
+  auto attribution_node_ids_iter = attribution_node_ids_.begin();
+  for (; new_topics_iter != new_topics.end();
+       ++new_topics_iter, ++attribution_node_ids_iter) {
+    const std::string& attribution_text = new_topics_iter->second.get().details;
+    cc::SkottieResourceIdHash attribution_node_id = *attribution_node_ids_iter;
+    DCHECK(animation_->text_map().contains(attribution_node_id));
+    animation_->text_map().at(attribution_node_id).SetText(attribution_text);
+  }
+}
+
+}  // namespace ash
diff --git a/ash/ambient/model/ambient_animation_attribution_provider.h b/ash/ambient/model/ambient_animation_attribution_provider.h
new file mode 100644
index 0000000..dfaf4161
--- /dev/null
+++ b/ash/ambient/model/ambient_animation_attribution_provider.h
@@ -0,0 +1,58 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef ASH_AMBIENT_MODEL_AMBIENT_ANIMATION_ATTRIBUTION_PROVIDER_H_
+#define ASH_AMBIENT_MODEL_AMBIENT_ANIMATION_ATTRIBUTION_PROVIDER_H_
+
+#include <vector>
+
+#include "ash/ambient/model/ambient_animation_photo_provider.h"
+#include "ash/ash_export.h"
+#include "base/memory/raw_ptr.h"
+#include "base/scoped_observation.h"
+#include "cc/paint/skottie_resource_metadata.h"
+
+namespace lottie {
+class Animation;
+}  // namespace lottie
+
+namespace ash {
+
+// "Attribution" refers to the text credits that may optionally accompany each
+// photo that's assigned to a dynamic asset in an animation. The Lottie files
+// for ambient mode have a placeholder for each dynamic asset where its
+// attribution text should go, and AmbientAnimationAttributionProvider's job is
+// to fill in the placeholders with the appropriate text credits.
+class ASH_EXPORT AmbientAnimationAttributionProvider
+    : public AmbientAnimationPhotoProvider::Observer {
+ public:
+  AmbientAnimationAttributionProvider(
+      AmbientAnimationPhotoProvider* photo_provider,
+      lottie::Animation* animation);
+  AmbientAnimationAttributionProvider(
+      const AmbientAnimationAttributionProvider&) = delete;
+  AmbientAnimationAttributionProvider& operator=(
+      const AmbientAnimationAttributionProvider&) = delete;
+  ~AmbientAnimationAttributionProvider() override;
+
+  // AmbientAnimationPhotoProvider::Observer implementation:
+  void OnDynamicImageAssetsRefreshed(
+      const base::flat_map</*asset_id*/ std::string,
+                           std::reference_wrapper<const PhotoWithDetails>>&
+          new_topics) override;
+
+ private:
+  const base::raw_ptr<lottie::Animation> animation_;
+  // Set of text nodes in the animation that should hold attribution for a
+  // photo. It is expected that the size of this vector matches the number of
+  // dynamic image assets in the animation (1 for each photo).
+  const std::vector<cc::SkottieResourceIdHash> attribution_node_ids_;
+  base::ScopedObservation<AmbientAnimationPhotoProvider,
+                          AmbientAnimationPhotoProvider::Observer>
+      observation_{this};
+};
+
+}  // namespace ash
+
+#endif  // ASH_AMBIENT_MODEL_AMBIENT_ANIMATION_ATTRIBUTION_PROVIDER_H_
diff --git a/ash/ambient/model/ambient_animation_attribution_provider_unittest.cc b/ash/ambient/model/ambient_animation_attribution_provider_unittest.cc
new file mode 100644
index 0000000..a203b91
--- /dev/null
+++ b/ash/ambient/model/ambient_animation_attribution_provider_unittest.cc
@@ -0,0 +1,185 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ash/ambient/model/ambient_animation_attribution_provider.h"
+
+#include <functional>
+#include <string>
+#include <utility>
+
+#include "ash/ambient/model/ambient_animation_photo_config.h"
+#include "ash/ambient/model/ambient_animation_photo_provider.h"
+#include "ash/ambient/model/ambient_backend_model.h"
+#include "ash/ambient/resources/ambient_animation_static_resources.h"
+#include "ash/ambient/test/ambient_test_util.h"
+#include "ash/ambient/test/fake_ambient_animation_static_resources.h"
+#include "base/check.h"
+#include "base/containers/flat_map.h"
+#include "base/memory/scoped_refptr.h"
+#include "cc/paint/skottie_frame_data.h"
+#include "cc/paint/skottie_resource_metadata.h"
+#include "cc/paint/skottie_text_property_value.h"
+#include "cc/test/lottie_test_data.h"
+#include "cc/test/skia_common.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
+#include "ui/gfx/geometry/size.h"
+#include "ui/gfx/image/image_skia.h"
+#include "ui/gfx/image/image_unittest_util.h"
+#include "ui/lottie/animation.h"
+
+namespace ash {
+
+using ::testing::Pair;
+using ::testing::UnorderedElementsAre;
+using ImageAsset = ::cc::SkottieFrameDataProvider::ImageAsset;
+
+// Has 2 assets and 2 text nodes.
+class AmbientAnimationAttributionProviderTest : public ::testing::Test {
+ protected:
+  AmbientAnimationAttributionProviderTest(std::string attribution_node_0,
+                                          std::string attribution_node_1,
+                                          std::string asset_id_0,
+                                          std::string asset_id_1)
+      : attribution_node_0_(std::move(attribution_node_0)),
+        attribution_node_1_(std::move(attribution_node_1)),
+        asset_id_0_(std::move(asset_id_0)),
+        asset_id_1_(std::move(asset_id_1)),
+        model_(
+            CreateAmbientAnimationPhotoConfig(BuildSkottieResourceMetadata())),
+        photo_provider_(&static_resources_, &model_),
+        animation_(cc::CreateSkottieFromString(
+            cc::CreateCustomLottieDataWith2TextNodes(attribution_node_0_,
+                                                     attribution_node_1_))),
+        attribution_provider_(&photo_provider_, &animation_) {}
+
+  cc::SkottieResourceMetadataMap BuildSkottieResourceMetadata() const {
+    cc::SkottieResourceMetadataMap resource_metadata;
+    CHECK(resource_metadata.RegisterAsset("dummy-resource-path",
+                                          "dummy-resource-name", asset_id_0_,
+                                          /*size=*/absl::nullopt));
+    CHECK(resource_metadata.RegisterAsset("dummy-resource-path",
+                                          "dummy-resource-name", asset_id_1_,
+                                          /*size=*/absl::nullopt));
+    return resource_metadata;
+  }
+
+  void RefreshDynamicImageAssets(
+      absl::optional<std::string> asset_0_attribution,
+      absl::optional<std::string> asset_1_attribution) {
+    gfx::ImageSkia test_image =
+        gfx::test::CreateImageSkia(/*width=*/10, /*height=*/10);
+    base::flat_map</*asset_id*/ std::string,
+                   std::reference_wrapper<const PhotoWithDetails>>
+        new_topics;
+
+    PhotoWithDetails topic_0;
+    if (asset_0_attribution) {
+      topic_0.photo = test_image;
+      topic_0.details = std::move(*asset_0_attribution);
+      new_topics.emplace(asset_id_0_, std::cref(topic_0));
+    }
+
+    PhotoWithDetails topic_1;
+    if (asset_1_attribution) {
+      topic_1.photo = test_image;
+      topic_1.details = std::move(*asset_1_attribution);
+      new_topics.emplace(asset_id_1_, std::cref(topic_1));
+    }
+
+    attribution_provider_.OnDynamicImageAssetsRefreshed(new_topics);
+  }
+
+  const std::string attribution_node_0_;
+  const std::string attribution_node_1_;
+  const std::string asset_id_0_;
+  const std::string asset_id_1_;
+  AmbientBackendModel model_;
+  FakeAmbientAnimationStaticResources static_resources_;
+  AmbientAnimationPhotoProvider photo_provider_;
+  lottie::Animation animation_;
+  AmbientAnimationAttributionProvider attribution_provider_;
+};
+
+class AmbientAnimationAttributionProviderTest2DynamicAssets
+    : public AmbientAnimationAttributionProviderTest {
+ protected:
+  AmbientAnimationAttributionProviderTest2DynamicAssets()
+      : AmbientAnimationAttributionProviderTest(
+            GenerateLottieCustomizableIdForTesting(0) + "Attribution",
+            GenerateLottieCustomizableIdForTesting(1) + "Attribution",
+            GenerateLottieCustomizableIdForTesting(0) + "Asset",
+            GenerateLottieCustomizableIdForTesting(1) + "Asset") {}
+};
+
+class AmbientAnimationAttributionProviderTest1DynamicAsset
+    : public AmbientAnimationAttributionProviderTest {
+ protected:
+  AmbientAnimationAttributionProviderTest1DynamicAsset()
+      : AmbientAnimationAttributionProviderTest(
+            "static-text-node",
+            GenerateLottieCustomizableIdForTesting(1) + "Attribution",
+            "static-asset-id",
+            GenerateLottieCustomizableIdForTesting(1) + "Asset") {}
+};
+
+TEST_F(AmbientAnimationAttributionProviderTest2DynamicAssets,
+       SetsTextInAnimation) {
+  RefreshDynamicImageAssets("attribution_text_0_a", "attribution_text_1_a");
+  EXPECT_THAT(animation_.text_map(),
+              UnorderedElementsAre(
+                  Pair(cc::HashSkottieResourceId(attribution_node_0_),
+                       cc::SkottieTextPropertyValue("attribution_text_0_a")),
+                  Pair(cc::HashSkottieResourceId(attribution_node_1_),
+                       cc::SkottieTextPropertyValue("attribution_text_1_a"))));
+  RefreshDynamicImageAssets("attribution_text_0_b", "attribution_text_1_b");
+  EXPECT_THAT(animation_.text_map(),
+              UnorderedElementsAre(
+                  Pair(cc::HashSkottieResourceId(attribution_node_0_),
+                       cc::SkottieTextPropertyValue("attribution_text_0_b")),
+                  Pair(cc::HashSkottieResourceId(attribution_node_1_),
+                       cc::SkottieTextPropertyValue("attribution_text_1_b"))));
+}
+
+TEST_F(AmbientAnimationAttributionProviderTest2DynamicAssets,
+       HandlesEmptyAttribution) {
+  RefreshDynamicImageAssets("", "");
+  EXPECT_THAT(
+      animation_.text_map(),
+      UnorderedElementsAre(Pair(cc::HashSkottieResourceId(attribution_node_0_),
+                                cc::SkottieTextPropertyValue("")),
+                           Pair(cc::HashSkottieResourceId(attribution_node_1_),
+                                cc::SkottieTextPropertyValue(""))));
+  RefreshDynamicImageAssets("", "attribution_text_1_a");
+  EXPECT_THAT(animation_.text_map(),
+              UnorderedElementsAre(
+                  Pair(cc::HashSkottieResourceId(attribution_node_0_),
+                       cc::SkottieTextPropertyValue("")),
+                  Pair(cc::HashSkottieResourceId(attribution_node_1_),
+                       cc::SkottieTextPropertyValue("attribution_text_1_a"))));
+  RefreshDynamicImageAssets("attribution_text_0_b", "");
+  EXPECT_THAT(animation_.text_map(),
+              UnorderedElementsAre(
+                  Pair(cc::HashSkottieResourceId(attribution_node_0_),
+                       cc::SkottieTextPropertyValue("attribution_text_0_b")),
+                  Pair(cc::HashSkottieResourceId(attribution_node_1_),
+                       cc::SkottieTextPropertyValue(""))));
+}
+
+TEST_F(AmbientAnimationAttributionProviderTest1DynamicAsset,
+       HandlesNonAttributionTextNodes) {
+  RefreshDynamicImageAssets(absl::nullopt, "attribution_text_1");
+  // The static text node should the value that's baked into the lottie file
+  // (|kLottieDataWith2TextNode1Text|).
+  EXPECT_THAT(animation_.text_map(),
+              UnorderedElementsAre(
+                  Pair(cc::HashSkottieResourceId(attribution_node_0_),
+                       cc::SkottieTextPropertyValue(
+                           std::string(cc::kLottieDataWith2TextNode1Text))),
+                  Pair(cc::HashSkottieResourceId(attribution_node_1_),
+                       cc::SkottieTextPropertyValue("attribution_text_1"))));
+}
+
+}  // namespace ash
diff --git a/ash/app_list/views/app_list_bubble_view_unittest.cc b/ash/app_list/views/app_list_bubble_view_unittest.cc
index bed96a3..16da98a 100644
--- a/ash/app_list/views/app_list_bubble_view_unittest.cc
+++ b/ash/app_list/views/app_list_bubble_view_unittest.cc
@@ -450,7 +450,9 @@
       "Apps.ClamshellLauncher.AnimationSmoothness.OpenAppsPage", 1);
 }
 
-TEST_F(AppListBubbleViewTest, HideAnimationsRecordsSmoothnessHistogram) {
+// TODO(crbug.com/1300774): Disabled due to flakiness.
+TEST_F(AppListBubbleViewTest,
+       DISABLED_HideAnimationsRecordsSmoothnessHistogram) {
   base::HistogramTester histograms;
 
   // Show the app list without animation.
diff --git a/ash/app_list/views/search_result_view.cc b/ash/app_list/views/search_result_view.cc
index b46a3de..78fd2cba 100644
--- a/ash/app_list/views/search_result_view.cc
+++ b/ash/app_list/views/search_result_view.cc
@@ -77,12 +77,8 @@
 
 constexpr int kSearchRatingStarPadding = 4;
 constexpr int kSearchRatingStarSize = 16;
-constexpr int kAnswerCardBorderMargin = 12;
-constexpr gfx::Insets kAnswerCardBorder(kAnswerCardBorderMargin,
-                                        kAnswerCardBorderMargin,
-                                        kAnswerCardBorderMargin,
-                                        kAnswerCardBorderMargin);
-constexpr gfx::Insets kBigTitleBorder(0, 0, 0, kAnswerCardBorderMargin);
+constexpr gfx::Insets kAnswerCardBorder(12, 12, 12, 12);
+constexpr gfx::Insets kBigTitleBorder(0, 14, 0, 12);
 
 views::ImageView* SetupChildImageView(views::FlexLayoutView* parent) {
   views::ImageView* image_view =
@@ -734,12 +730,7 @@
   actions_view()->SetBoundsRect(actions_bounds);
 
   gfx::Rect text_bounds(rect);
-  // Text bounds need to be shifted over by kAnswerCardBorderMargin for answer
-  // card views to make room for the kAnswerCardBorder.
-  text_bounds.set_x(kPreferredIconViewWidth +
-                    (view_type_ == SearchResultViewType::kAnswerCard
-                         ? kAnswerCardBorderMargin
-                         : 0));
+  text_bounds.set_x(kPreferredIconViewWidth);
   if (actions_view()->GetVisible()) {
     text_bounds.set_width(
         rect.width() - kPreferredIconViewWidth - kTextTrailPadding -
diff --git a/ash/components/arc/BUILD.gn b/ash/components/arc/BUILD.gn
index 398620dd..1d91783 100644
--- a/ash/components/arc/BUILD.gn
+++ b/ash/components/arc/BUILD.gn
@@ -74,6 +74,7 @@
     "net/always_on_vpn_manager.h",
     "net/arc_net_host_impl.cc",
     "net/arc_net_host_impl.h",
+    "net/cert_manager.h",
     "obb_mounter/arc_obb_mounter_bridge.cc",
     "obb_mounter/arc_obb_mounter_bridge.h",
     "pay/arc_digital_goods_bridge.cc",
diff --git a/ash/components/arc/net/arc_net_host_impl.cc b/ash/components/arc/net/arc_net_host_impl.cc
index fae60fd..2f937d40 100644
--- a/ash/components/arc/net/arc_net_host_impl.cc
+++ b/ash/components/arc/net/arc_net_host_impl.cc
@@ -10,6 +10,7 @@
 
 #include "ash/components/arc/arc_browser_context_keyed_service_factory_base.h"
 #include "ash/components/arc/arc_prefs.h"
+#include "ash/components/arc/net/cert_manager.h"
 #include "ash/components/arc/session/arc_bridge_service.h"
 #include "base/bind.h"
 #include "base/callback_helpers.h"
@@ -18,10 +19,12 @@
 #include "base/memory/singleton.h"
 #include "base/posix/eintr_wrapper.h"
 #include "base/strings/string_number_conversions.h"
+#include "base/strings/stringprintf.h"
 #include "chromeos/dbus/patchpanel/patchpanel_client.h"
 #include "chromeos/dbus/patchpanel/patchpanel_service.pb.h"
 #include "chromeos/dbus/shill/shill_manager_client.h"
 #include "chromeos/login/login_state/login_state.h"
+#include "chromeos/network/client_cert_util.h"
 #include "chromeos/network/device_state.h"
 #include "chromeos/network/managed_network_configuration_handler.h"
 #include "chromeos/network/network_configuration_handler.h"
@@ -527,6 +530,10 @@
   pref_service_ = pref_service;
 }
 
+void ArcNetHostImpl::SetCertManager(std::unique_ptr<CertManager> cert_manager) {
+  cert_manager_ = std::move(cert_manager);
+}
+
 void ArcNetHostImpl::OnConnectionReady() {
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
 
@@ -958,86 +965,170 @@
       base::BindOnce(&ArcVpnErrorCallback, "disconnecting ARC VPN"));
 }
 
-base::Value ArcNetHostImpl::TranslateEapCredentialsToDict(
-    const mojom::EapCredentials& cred) {
-  base::Value dict(base::Value::Type::DICTIONARY);
+void ArcNetHostImpl::TranslateEapCredentialsToDict(
+    mojom::EapCredentialsPtr cred,
+    base::OnceCallback<void(base::Value)> callback) {
+  if (!cred) {
+    NET_LOG(ERROR) << "Empty EAP credentials";
+    return;
+  }
+  if (!cert_manager_) {
+    NET_LOG(ERROR) << "CertManager is not initialized";
+    return;
+  }
 
-  dict.SetStringKey(shill::kEapMethodProperty, TranslateEapMethod(cred.method));
-  dict.SetStringKey(shill::kEapPhase2AuthProperty,
-                    TranslateEapPhase2Method(cred.phase2_method));
-  if (cred.anonymous_identity.has_value()) {
-    dict.SetStringKey(shill::kEapAnonymousIdentityProperty,
-                      cred.anonymous_identity.value());
+  if (cred->client_certificate_key.has_value() &&
+      cred->client_certificate_pem.has_value() &&
+      cred->client_certificate_pem.value().size() > 0) {
+    // |client_certificate_pem| contains all client certificates inside ARC's
+    // PasspointConfiguration. ARC uses only one of the certificate that match
+    // the certificate SHA-256 fingerprint. Currently, it is assumed that the
+    // first certificate is the used certificate.
+    // TODO(b/195262431): Remove the assumption by passing only the used
+    // certificate to Chrome.
+    // TODO(b/220803680): Remove imported certificates and keys when the
+    // associated passpoint profile is removed.
+    cert_manager_->ImportPrivateKeyAndCert(
+        cred->client_certificate_key.value(),
+        cred->client_certificate_pem.value()[0],
+        base::BindOnce(&ArcNetHostImpl::TranslateEapCredentialsToDictWithCertID,
+                       weak_factory_.GetWeakPtr(), std::move(cred),
+                       std::move(callback)));
+    return;
   }
-  if (cred.identity.has_value())
-    dict.SetStringKey(shill::kEapIdentityProperty, cred.identity.value());
-
-  if (cred.password.has_value())
-    dict.SetStringKey(shill::kEapPasswordProperty, cred.password.value());
-
-  dict.SetStringKey(shill::kEapKeyMgmtProperty,
-                    TranslateKeyManagement(cred.key_management));
-  // TODO(195262431): Provision and fill in certificates.
-  if (cred.subject_match.has_value()) {
-    dict.SetStringKey(shill::kEapSubjectMatchProperty,
-                      cred.subject_match.value());
-  }
-  if (cred.subject_alternative_name_match_list.has_value()) {
-    dict.SetKey(shill::kEapSubjectAlternativeNameMatchProperty,
-                TranslateStringListToValue(
-                    cred.subject_alternative_name_match_list.value()));
-  }
-  if (cred.domain_suffix_match_list.has_value()) {
-    dict.SetKey(
-        shill::kEapDomainSuffixMatchProperty,
-        TranslateStringListToValue(cred.domain_suffix_match_list.value()));
-  }
-  if (cred.tls_version_max.has_value()) {
-    dict.SetStringKey(shill::kEapTLSVersionMaxProperty,
-                      cred.tls_version_max.value());
-  }
-  dict.SetBoolKey(shill::kEapUseSystemCasProperty, cred.use_system_cas);
-  dict.SetBoolKey(shill::kEapUseProactiveKeyCachingProperty,
-                  cred.use_proactive_key_caching);
-  dict.SetBoolKey(shill::kEapUseLoginPasswordProperty, cred.use_login_password);
-
-  return dict;
+  TranslateEapCredentialsToDictWithCertID(std::move(cred), std::move(callback),
+                                          /*cert_id=*/absl::nullopt,
+                                          /*slot_id=*/absl::nullopt);
 }
 
-base::Value ArcNetHostImpl::TranslatePasspointCredentialsToDict(
-    const mojom::PasspointCredentials& cred) {
-  // Fill in EAP credentials fields.
-  if (!cred.eap) {
-    NET_LOG(ERROR) << "mojom::PasspointCredentials has no EAP properties";
-    return base::Value();
+void ArcNetHostImpl::TranslateEapCredentialsToDictWithCertID(
+    mojom::EapCredentialsPtr cred,
+    base::OnceCallback<void(base::Value)> callback,
+    const absl::optional<std::string>& cert_id,
+    const absl::optional<int>& slot_id) {
+  if (!cred) {
+    NET_LOG(ERROR) << "Empty EAP credentials";
+    return;
   }
-  auto dict = TranslateEapCredentialsToDict(*cred.eap);
 
-  // Fill in Passpoint credentials fields.
+  base::Value dict(base::Value::Type::DICTIONARY);
+  dict.SetStringKey(shill::kEapMethodProperty,
+                    TranslateEapMethod(cred->method));
+  dict.SetStringKey(shill::kEapPhase2AuthProperty,
+                    TranslateEapPhase2Method(cred->phase2_method));
+  if (cred->anonymous_identity.has_value()) {
+    dict.SetStringKey(shill::kEapAnonymousIdentityProperty,
+                      cred->anonymous_identity.value());
+  }
+  if (cred->identity.has_value())
+    dict.SetStringKey(shill::kEapIdentityProperty, cred->identity.value());
+
+  if (cred->password.has_value())
+    dict.SetStringKey(shill::kEapPasswordProperty, cred->password.value());
+
+  dict.SetStringKey(shill::kEapKeyMgmtProperty,
+                    TranslateKeyManagement(cred->key_management));
+
+  if (cert_id.has_value() && slot_id.has_value()) {
+    // The ID of imported user certificate and private key is the same, use one
+    // of them.
+    dict.SetStringKey(
+        shill::kEapKeyIdProperty,
+        base::StringPrintf("%i:%s", slot_id.value(), cert_id.value().c_str()));
+    dict.SetStringKey(
+        shill::kEapCertIdProperty,
+        base::StringPrintf("%i:%s", slot_id.value(), cert_id.value().c_str()));
+    dict.SetStringKey(shill::kEapPinProperty,
+                      chromeos::client_cert::kDefaultTPMPin);
+  }
+
+  if (cred->subject_match.has_value()) {
+    dict.SetStringKey(shill::kEapSubjectMatchProperty,
+                      cred->subject_match.value());
+  }
+  if (cred->subject_alternative_name_match_list.has_value()) {
+    dict.SetKey(shill::kEapSubjectAlternativeNameMatchProperty,
+                TranslateStringListToValue(
+                    cred->subject_alternative_name_match_list.value()));
+  }
+  if (cred->domain_suffix_match_list.has_value()) {
+    dict.SetKey(
+        shill::kEapDomainSuffixMatchProperty,
+        TranslateStringListToValue(cred->domain_suffix_match_list.value()));
+  }
+  if (cred->tls_version_max.has_value()) {
+    dict.SetStringKey(shill::kEapTLSVersionMaxProperty,
+                      cred->tls_version_max.value());
+  }
+  dict.SetBoolKey(shill::kEapUseSystemCasProperty, cred->use_system_cas);
+  dict.SetBoolKey(shill::kEapUseProactiveKeyCachingProperty,
+                  cred->use_proactive_key_caching);
+  dict.SetBoolKey(shill::kEapUseLoginPasswordProperty,
+                  cred->use_login_password);
+
+  std::move(callback).Run(std::move(dict));
+}
+
+void ArcNetHostImpl::TranslatePasspointCredentialsToDict(
+    mojom::PasspointCredentialsPtr cred,
+    base::OnceCallback<void(base::Value)> callback) {
+  if (!cred) {
+    NET_LOG(ERROR) << "Empty passpoint credentials";
+    return;
+  }
+  if (!cred->eap) {
+    NET_LOG(ERROR) << "mojom::PasspointCredentials has no EAP properties";
+    return;
+  }
+
+  mojom::EapCredentialsPtr eap = cred->eap.Clone();
+  TranslateEapCredentialsToDict(
+      std::move(eap),
+      base::BindOnce(
+          &ArcNetHostImpl::TranslatePasspointCredentialsToDictWithEapTranslated,
+          weak_factory_.GetWeakPtr(), std::move(cred), std::move(callback)));
+}
+
+void ArcNetHostImpl::TranslatePasspointCredentialsToDictWithEapTranslated(
+    mojom::PasspointCredentialsPtr cred,
+    base::OnceCallback<void(base::Value)> callback,
+    base::Value dict) {
+  if (!cred) {
+    NET_LOG(ERROR) << "Empty passpoint credentials";
+    return;
+  }
+  if (dict.is_none()) {
+    NET_LOG(ERROR) << "Failed to translate EapCredentials properties";
+    return;
+  }
+
   dict.SetKey(shill::kPasspointCredentialsDomainsProperty,
-              TranslateStringListToValue(cred.domains));
-  dict.SetStringKey(shill::kPasspointCredentialsRealmProperty, cred.realm);
+              TranslateStringListToValue(cred->domains));
+  dict.SetStringKey(shill::kPasspointCredentialsRealmProperty, cred->realm);
   dict.SetKey(shill::kPasspointCredentialsHomeOIsProperty,
-              TranslateLongListToStringValue(cred.home_ois));
+              TranslateLongListToStringValue(cred->home_ois));
   dict.SetKey(shill::kPasspointCredentialsRequiredHomeOIsProperty,
-              TranslateLongListToStringValue(cred.required_home_ois));
+              TranslateLongListToStringValue(cred->required_home_ois));
   dict.SetKey(shill::kPasspointCredentialsRoamingConsortiaProperty,
-              TranslateLongListToStringValue(cred.roaming_consortium_ois));
+              TranslateLongListToStringValue(cred->roaming_consortium_ois));
   dict.SetBoolKey(shill::kPasspointCredentialsMeteredOverrideProperty,
-                  cred.metered);
+                  cred->metered);
   dict.SetStringKey(shill::kPasspointCredentialsAndroidPackageNameProperty,
-                    cred.package_name);
+                    cred->package_name);
 
-  return dict;
+  std::move(callback).Run(std::move(dict));
 }
 
 void ArcNetHostImpl::AddPasspointCredentials(
     mojom::PasspointCredentialsPtr credentials) {
-  // TODO(195262431): Support EAP-TLS.
-  if (credentials->eap->method != mojom::EapMethod::kTtls)
-    return;
+  TranslatePasspointCredentialsToDict(
+      std::move(credentials),
+      base::BindOnce(&ArcNetHostImpl::AddPasspointCredentialsWithProperties,
+                     weak_factory_.GetWeakPtr()));
+}
 
-  const auto properties = TranslatePasspointCredentialsToDict(*credentials);
+void ArcNetHostImpl::AddPasspointCredentialsWithProperties(
+    base::Value properties) {
   if (properties.is_none()) {
     NET_LOG(ERROR) << "Failed to translate PasspointCredentials properties";
     return;
diff --git a/ash/components/arc/net/arc_net_host_impl.h b/ash/components/arc/net/arc_net_host_impl.h
index a608171..0bec62d 100644
--- a/ash/components/arc/net/arc_net_host_impl.h
+++ b/ash/components/arc/net/arc_net_host_impl.h
@@ -12,6 +12,7 @@
 #include <vector>
 
 #include "ash/components/arc/mojom/net.mojom.h"
+#include "ash/components/arc/net/cert_manager.h"
 #include "ash/components/arc/session/connection_observer.h"
 #include "base/callback_forward.h"
 #include "base/files/scoped_file.h"
@@ -57,6 +58,7 @@
   ~ArcNetHostImpl() override;
 
   void SetPrefService(PrefService* pref_service);
+  void SetCertManager(std::unique_ptr<CertManager> cert_manager);
 
   // ARC -> Chrome calls:
 
@@ -96,11 +98,6 @@
   std::unique_ptr<base::DictionaryValue> TranslateVpnConfigurationToOnc(
       const mojom::AndroidVpnConfiguration& cfg);
 
-  base::Value TranslateEapCredentialsToDict(const mojom::EapCredentials& cred);
-
-  base::Value TranslatePasspointCredentialsToDict(
-      const mojom::PasspointCredentials& cred);
-
   // Overridden from chromeos::NetworkStateHandlerObserver.
   void ScanCompleted(const chromeos::DeviceState* /*unused*/) override;
   void OnShuttingDown() override;
@@ -168,6 +165,41 @@
   // Ask Android to disconnect any VPN app that is currently connected.
   void DisconnectArcVpn();
 
+  // Translate EAP credentials to base::Value dictionary and run |callback|.
+  // If it is necessary to import certificates this method will asynchronously
+  // import them and run |callback| afterwards.
+  void TranslateEapCredentialsToDict(
+      mojom::EapCredentialsPtr cred,
+      base::OnceCallback<void(base::Value)> callback);
+
+  // Synchronously translate EAP credentials to base::Value dictionary with
+  // empty or imported certificate and slot ID. |callback| is then run with
+  // the translated values.
+  void TranslateEapCredentialsToDictWithCertID(
+      mojom::EapCredentialsPtr cred,
+      base::OnceCallback<void(base::Value)> callback,
+      const absl::optional<std::string>& cert_id,
+      const absl::optional<int>& slot_id);
+
+  // Translate passpoint credentials to base::Value dictionary and run
+  // |callback|. If it is necessary to import certificates this method will
+  // asynchronously import them and run |callback| afterwards.
+  void TranslatePasspointCredentialsToDict(
+      mojom::PasspointCredentialsPtr cred,
+      base::OnceCallback<void(base::Value)> callback);
+
+  // Synchronously translate passpoint credentials to base::Value dictionary
+  // with EAP fields translated inside |dict|. |callback| is then run with
+  // the translated values.
+  void TranslatePasspointCredentialsToDictWithEapTranslated(
+      mojom::PasspointCredentialsPtr cred,
+      base::OnceCallback<void(base::Value)> callback,
+      base::Value dict);
+
+  // Synchronously calls Chrome OS to add passpoint credentials from ARC with
+  // the properties values translated taken from mojo.
+  void AddPasspointCredentialsWithProperties(base::Value properties);
+
   void CreateNetworkSuccessCallback(
       base::OnceCallback<void(const std::string&)> callback,
       const std::string& service_path,
@@ -195,6 +227,8 @@
   // Owned by the user profile whose context was used to initialize |this|.
   PrefService* pref_service_ = nullptr;
 
+  std::unique_ptr<CertManager> cert_manager_;
+
   THREAD_CHECKER(thread_checker_);
   base::WeakPtrFactory<ArcNetHostImpl> weak_factory_{this};
 };
diff --git a/ash/components/arc/net/cert_manager.h b/ash/components/arc/net/cert_manager.h
new file mode 100644
index 0000000..4765427d
--- /dev/null
+++ b/ash/components/arc/net/cert_manager.h
@@ -0,0 +1,36 @@
+// Copyright (c) 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef ASH_COMPONENTS_ARC_NET_CERT_MANAGER_H_
+#define ASH_COMPONENTS_ARC_NET_CERT_MANAGER_H_
+
+#include <string>
+
+#include "base/callback.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
+
+namespace arc {
+
+// CertManager imports plain-text certificates and private keys into Chrome OS'
+// key store (chaps).
+class CertManager {
+ public:
+  using ImportPrivateKeyAndCertCallback =
+      base::OnceCallback<void(const absl::optional<std::string>& cert_id,
+                              const absl::optional<int>& slot_id)>;
+
+  virtual ~CertManager() = default;
+
+  // Asynchronously import a PEM-formatted private and user certificate into
+  // the NSS certificate database. Calls a callback with its ID and the slot
+  // ID of the database. This method will asynchronously fetch the database.
+  virtual void ImportPrivateKeyAndCert(
+      const std::string& key_pem,
+      const std::string& cert_pem,
+      ImportPrivateKeyAndCertCallback callback) = 0;
+};
+
+}  // namespace arc
+
+#endif  // ASH_COMPONENTS_ARC_NET_CERT_MANAGER_H_
diff --git a/ash/components/arc/session/BUILD.gn b/ash/components/arc/session/BUILD.gn
index add6a9e..14adae93 100644
--- a/ash/components/arc/session/BUILD.gn
+++ b/ash/components/arc/session/BUILD.gn
@@ -53,7 +53,6 @@
     "//ash/public/cpp/external_arc:external_arc",
     "//chromeos/components/sensors:buildflags",
     "//chromeos/dbus/dlcservice:dlcservice",
-    "//chromeos/dbus/dlcservice:dlcservice_proto",
     "//chromeos/dbus/session_manager",
     "//chromeos/dbus/upstart",
     "//chromeos/memory:memory",
diff --git a/ash/components/arc/session/arc_dlc_installer.cc b/ash/components/arc/session/arc_dlc_installer.cc
index d92564b..be81a20 100644
--- a/ash/components/arc/session/arc_dlc_installer.cc
+++ b/ash/components/arc/session/arc_dlc_installer.cc
@@ -6,7 +6,6 @@
 
 #include "base/callback_helpers.h"
 #include "base/logging.h"
-#include "chromeos/dbus/dlcservice/dlcservice.pb.h"
 
 namespace arc {
 
@@ -62,10 +61,8 @@
 
   state_ = InstallerState::kInstalling;
   VLOG(2) << "Installing ARC DLC: " << kHoudiniRvcDlc;
-  dlcservice::InstallRequest install_request;
-  install_request.set_id(kHoudiniRvcDlc);
   chromeos::DlcserviceClient::Get()->Install(
-      install_request,
+      kHoudiniRvcDlc,
       base::BindOnce(&ArcDlcInstaller::OnDlcInstalled,
                      weak_ptr_factory_.GetWeakPtr(), kHoudiniRvcDlc),
       base::DoNothing());
diff --git a/ash/shelf/drag_window_from_shelf_controller_unittest.cc b/ash/shelf/drag_window_from_shelf_controller_unittest.cc
index 27a26d15..8d12808 100644
--- a/ash/shelf/drag_window_from_shelf_controller_unittest.cc
+++ b/ash/shelf/drag_window_from_shelf_controller_unittest.cc
@@ -1396,4 +1396,26 @@
   EXPECT_FALSE(transient_child_win1->IsVisible());
   EXPECT_FALSE(transient_child_win2->IsVisible());
 }
+
+// Tests that destroying a dragged window in split view will not cause crash.
+TEST_F(DragWindowFromShelfControllerTest,
+       DestroyWindowDuringDraggingInSplitView) {
+  UpdateDisplay("500x400");
+  const gfx::Rect shelf_bounds =
+      Shelf::ForWindow(Shell::GetPrimaryRootWindow())->GetIdealBounds();
+
+  // Create a window and snapped to the left in split screen.
+  auto window = CreateTestWindow();
+  split_view_controller()->SnapWindow(window.get(), SplitViewController::LEFT);
+
+  // Try to drag the window from shelf.
+  StartDrag(window.get(), shelf_bounds.left_center());
+  Drag(gfx::Point(0, 200), 1.f, 1.f);
+
+  // Destroy the window while dragging. Expect no crash.
+  window.reset();
+
+  EndDrag(shelf_bounds.CenterPoint(), /*velocity_y=*/absl::nullopt);
+}
+
 }  // namespace ash
diff --git a/build/linux/sysroot_scripts/generated_package_lists/sid.amd64 b/build/linux/sysroot_scripts/generated_package_lists/sid.amd64
index 471b6c0..c7889a7 100644
--- a/build/linux/sysroot_scripts/generated_package_lists/sid.amd64
+++ b/build/linux/sysroot_scripts/generated_package_lists/sid.amd64
@@ -191,6 +191,8 @@
 https://snapshot.debian.org/archive/debian/20210819T144544Z/pool/main/libt/libtirpc/libtirpc3_1.3.2-2_amd64.deb
 https://snapshot.debian.org/archive/debian/20210819T144544Z/pool/main/libt/libtool/libltdl7_2.4.6-15_amd64.deb
 https://snapshot.debian.org/archive/debian/20210819T144544Z/pool/main/libu/libunistring/libunistring2_0.9.10-6_amd64.deb
+https://snapshot.debian.org/archive/debian/20210819T144544Z/pool/main/libu/libutempter/libutempter0_1.2.1-2_amd64.deb
+https://snapshot.debian.org/archive/debian/20210819T144544Z/pool/main/libu/libutempter/libutempter-dev_1.2.1-2_amd64.deb
 https://snapshot.debian.org/archive/debian/20210819T144544Z/pool/main/libv/libva/libva2_2.12.0-2_amd64.deb
 https://snapshot.debian.org/archive/debian/20210819T144544Z/pool/main/libv/libva/libva-dev_2.12.0-2_amd64.deb
 https://snapshot.debian.org/archive/debian/20210819T144544Z/pool/main/libv/libva/libva-drm2_2.12.0-2_amd64.deb
diff --git a/build/linux/sysroot_scripts/generated_package_lists/sid.arm b/build/linux/sysroot_scripts/generated_package_lists/sid.arm
index 6371fa5..a268bc5 100644
--- a/build/linux/sysroot_scripts/generated_package_lists/sid.arm
+++ b/build/linux/sysroot_scripts/generated_package_lists/sid.arm
@@ -187,6 +187,8 @@
 https://snapshot.debian.org/archive/debian/20210819T144544Z/pool/main/libt/libtirpc/libtirpc3_1.3.2-2_armhf.deb
 https://snapshot.debian.org/archive/debian/20210819T144544Z/pool/main/libt/libtool/libltdl7_2.4.6-15_armhf.deb
 https://snapshot.debian.org/archive/debian/20210819T144544Z/pool/main/libu/libunistring/libunistring2_0.9.10-6_armhf.deb
+https://snapshot.debian.org/archive/debian/20210819T144544Z/pool/main/libu/libutempter/libutempter0_1.2.1-2_armhf.deb
+https://snapshot.debian.org/archive/debian/20210819T144544Z/pool/main/libu/libutempter/libutempter-dev_1.2.1-2_armhf.deb
 https://snapshot.debian.org/archive/debian/20210819T144544Z/pool/main/libv/libva/libva2_2.12.0-2_armhf.deb
 https://snapshot.debian.org/archive/debian/20210819T144544Z/pool/main/libv/libva/libva-dev_2.12.0-2_armhf.deb
 https://snapshot.debian.org/archive/debian/20210819T144544Z/pool/main/libv/libva/libva-drm2_2.12.0-2_armhf.deb
diff --git a/build/linux/sysroot_scripts/generated_package_lists/sid.arm64 b/build/linux/sysroot_scripts/generated_package_lists/sid.arm64
index 55e48b10..6e80c1c 100644
--- a/build/linux/sysroot_scripts/generated_package_lists/sid.arm64
+++ b/build/linux/sysroot_scripts/generated_package_lists/sid.arm64
@@ -190,6 +190,8 @@
 https://snapshot.debian.org/archive/debian/20210819T144544Z/pool/main/libt/libtirpc/libtirpc3_1.3.2-2_arm64.deb
 https://snapshot.debian.org/archive/debian/20210819T144544Z/pool/main/libt/libtool/libltdl7_2.4.6-15_arm64.deb
 https://snapshot.debian.org/archive/debian/20210819T144544Z/pool/main/libu/libunistring/libunistring2_0.9.10-6_arm64.deb
+https://snapshot.debian.org/archive/debian/20210819T144544Z/pool/main/libu/libutempter/libutempter0_1.2.1-2_arm64.deb
+https://snapshot.debian.org/archive/debian/20210819T144544Z/pool/main/libu/libutempter/libutempter-dev_1.2.1-2_arm64.deb
 https://snapshot.debian.org/archive/debian/20210819T144544Z/pool/main/libv/libva/libva2_2.12.0-2_arm64.deb
 https://snapshot.debian.org/archive/debian/20210819T144544Z/pool/main/libv/libva/libva-dev_2.12.0-2_arm64.deb
 https://snapshot.debian.org/archive/debian/20210819T144544Z/pool/main/libv/libva/libva-drm2_2.12.0-2_arm64.deb
diff --git a/build/linux/sysroot_scripts/generated_package_lists/sid.armel b/build/linux/sysroot_scripts/generated_package_lists/sid.armel
index b7962dd..ad8d0b7 100644
--- a/build/linux/sysroot_scripts/generated_package_lists/sid.armel
+++ b/build/linux/sysroot_scripts/generated_package_lists/sid.armel
@@ -186,6 +186,8 @@
 https://snapshot.debian.org/archive/debian/20210819T144544Z/pool/main/libt/libtirpc/libtirpc3_1.3.2-2_armel.deb
 https://snapshot.debian.org/archive/debian/20210819T144544Z/pool/main/libt/libtool/libltdl7_2.4.6-15_armel.deb
 https://snapshot.debian.org/archive/debian/20210819T144544Z/pool/main/libu/libunistring/libunistring2_0.9.10-6_armel.deb
+https://snapshot.debian.org/archive/debian/20210819T144544Z/pool/main/libu/libutempter/libutempter0_1.2.1-2_armel.deb
+https://snapshot.debian.org/archive/debian/20210819T144544Z/pool/main/libu/libutempter/libutempter-dev_1.2.1-2_armel.deb
 https://snapshot.debian.org/archive/debian/20210819T144544Z/pool/main/libv/libva/libva2_2.12.0-2_armel.deb
 https://snapshot.debian.org/archive/debian/20210819T144544Z/pool/main/libv/libva/libva-dev_2.12.0-2_armel.deb
 https://snapshot.debian.org/archive/debian/20210819T144544Z/pool/main/libv/libva/libva-drm2_2.12.0-2_armel.deb
diff --git a/build/linux/sysroot_scripts/generated_package_lists/sid.i386 b/build/linux/sysroot_scripts/generated_package_lists/sid.i386
index 0e1d82b..d8c0e9b 100644
--- a/build/linux/sysroot_scripts/generated_package_lists/sid.i386
+++ b/build/linux/sysroot_scripts/generated_package_lists/sid.i386
@@ -187,6 +187,8 @@
 https://snapshot.debian.org/archive/debian/20210819T144544Z/pool/main/libt/libtirpc/libtirpc3_1.3.2-2_i386.deb
 https://snapshot.debian.org/archive/debian/20210819T144544Z/pool/main/libt/libtool/libltdl7_2.4.6-15_i386.deb
 https://snapshot.debian.org/archive/debian/20210819T144544Z/pool/main/libu/libunistring/libunistring2_0.9.10-6_i386.deb
+https://snapshot.debian.org/archive/debian/20210819T144544Z/pool/main/libu/libutempter/libutempter0_1.2.1-2_i386.deb
+https://snapshot.debian.org/archive/debian/20210819T144544Z/pool/main/libu/libutempter/libutempter-dev_1.2.1-2_i386.deb
 https://snapshot.debian.org/archive/debian/20210819T144544Z/pool/main/libv/libva/libva2_2.12.0-2_i386.deb
 https://snapshot.debian.org/archive/debian/20210819T144544Z/pool/main/libv/libva/libva-dev_2.12.0-2_i386.deb
 https://snapshot.debian.org/archive/debian/20210819T144544Z/pool/main/libv/libva/libva-drm2_2.12.0-2_i386.deb
diff --git a/build/linux/sysroot_scripts/generated_package_lists/sid.mips64el b/build/linux/sysroot_scripts/generated_package_lists/sid.mips64el
index 5576555..aee8097e 100644
--- a/build/linux/sysroot_scripts/generated_package_lists/sid.mips64el
+++ b/build/linux/sysroot_scripts/generated_package_lists/sid.mips64el
@@ -178,6 +178,8 @@
 https://snapshot.debian.org/archive/debian/20210819T144544Z/pool/main/libt/libtirpc/libtirpc3_1.3.2-2_mips64el.deb
 https://snapshot.debian.org/archive/debian/20210819T144544Z/pool/main/libt/libtool/libltdl7_2.4.6-15_mips64el.deb
 https://snapshot.debian.org/archive/debian/20210819T144544Z/pool/main/libu/libunistring/libunistring2_0.9.10-6_mips64el.deb
+https://snapshot.debian.org/archive/debian/20210819T144544Z/pool/main/libu/libutempter/libutempter0_1.2.1-2_mips64el.deb
+https://snapshot.debian.org/archive/debian/20210819T144544Z/pool/main/libu/libutempter/libutempter-dev_1.2.1-2_mips64el.deb
 https://snapshot.debian.org/archive/debian/20210819T144544Z/pool/main/libv/libva/libva2_2.12.0-2_mips64el.deb
 https://snapshot.debian.org/archive/debian/20210819T144544Z/pool/main/libv/libva/libva-dev_2.12.0-2_mips64el.deb
 https://snapshot.debian.org/archive/debian/20210819T144544Z/pool/main/libv/libva/libva-drm2_2.12.0-2_mips64el.deb
diff --git a/build/linux/sysroot_scripts/generated_package_lists/sid.mipsel b/build/linux/sysroot_scripts/generated_package_lists/sid.mipsel
index 36ee872..626b7f2e 100644
--- a/build/linux/sysroot_scripts/generated_package_lists/sid.mipsel
+++ b/build/linux/sysroot_scripts/generated_package_lists/sid.mipsel
@@ -178,6 +178,8 @@
 https://snapshot.debian.org/archive/debian/20210819T144544Z/pool/main/libt/libtirpc/libtirpc3_1.3.2-2_mipsel.deb
 https://snapshot.debian.org/archive/debian/20210819T144544Z/pool/main/libt/libtool/libltdl7_2.4.6-15_mipsel.deb
 https://snapshot.debian.org/archive/debian/20210819T144544Z/pool/main/libu/libunistring/libunistring2_0.9.10-6_mipsel.deb
+https://snapshot.debian.org/archive/debian/20210819T144544Z/pool/main/libu/libutempter/libutempter0_1.2.1-2_mipsel.deb
+https://snapshot.debian.org/archive/debian/20210819T144544Z/pool/main/libu/libutempter/libutempter-dev_1.2.1-2_mipsel.deb
 https://snapshot.debian.org/archive/debian/20210819T144544Z/pool/main/libv/libva/libva2_2.12.0-2_mipsel.deb
 https://snapshot.debian.org/archive/debian/20210819T144544Z/pool/main/libv/libva/libva-dev_2.12.0-2_mipsel.deb
 https://snapshot.debian.org/archive/debian/20210819T144544Z/pool/main/libv/libva/libva-drm2_2.12.0-2_mipsel.deb
diff --git a/build/linux/sysroot_scripts/sysroot-creator-sid.sh b/build/linux/sysroot_scripts/sysroot-creator-sid.sh
index 824e911..9ff63ff 100755
--- a/build/linux/sysroot_scripts/sysroot-creator-sid.sh
+++ b/build/linux/sysroot_scripts/sysroot-creator-sid.sh
@@ -298,6 +298,8 @@
   libudev1
   libunbound8
   libunistring2
+  libutempter-dev
+  libutempter0
   libuuid1
   libva-dev
   libva-drm2
diff --git a/cc/paint/image_transfer_cache_entry.cc b/cc/paint/image_transfer_cache_entry.cc
index 1c963381..6f1ef12 100644
--- a/cc/paint/image_transfer_cache_entry.cc
+++ b/cc/paint/image_transfer_cache_entry.cc
@@ -26,10 +26,6 @@
 namespace cc {
 namespace {
 
-// TODO(https://crbug.com/1286076): Plumb the true parameters in here.
-constexpr float kTempMaxLuminanceNits = 100.f;
-constexpr float kTempHDRMaxLuminanceRelative = 1.f;
-
 struct Context {
   const std::vector<sk_sp<SkImage>> sk_planes_;
 };
@@ -199,6 +195,57 @@
   return true;
 }
 
+size_t TargetColorParamsSize(
+    const absl::optional<TargetColorParams>& target_color_params) {
+  // uint32 for whether or not there are going to be parameters.
+  size_t target_color_params_size = sizeof(uint32_t);
+  if (target_color_params) {
+    // The target color space.
+    target_color_params_size +=
+        sizeof(uint64_t) +
+        target_color_params->color_space.ToSkColorSpace()->writeToMemory(
+            nullptr);
+    // Floats for the SDR and HDR maximum luminance.
+    target_color_params_size += sizeof(float);
+    target_color_params_size += sizeof(float);
+  }
+  return target_color_params_size;
+}
+
+void WriteTargetColorParams(
+    PaintOpWriter& writer,
+    const absl::optional<TargetColorParams>& target_color_params) {
+  const uint32_t has_target_color_params = target_color_params ? 1 : 0;
+  writer.Write(has_target_color_params);
+  if (target_color_params) {
+    writer.Write(target_color_params->color_space.ToSkColorSpace().get());
+    writer.Write(target_color_params->sdr_max_luminance_nits);
+    writer.Write(target_color_params->hdr_max_luminance_relative);
+  }
+}
+
+bool ReadTargetColorParams(
+    PaintOpReader& reader,
+    absl::optional<TargetColorParams>& target_color_params) {
+  uint32_t has_target_color_params;
+  reader.Read(&has_target_color_params);
+  if (!has_target_color_params) {
+    target_color_params = absl::nullopt;
+    return true;
+  }
+
+  target_color_params = TargetColorParams();
+  sk_sp<SkColorSpace> target_color_space;
+  reader.Read(&target_color_space);
+  if (!target_color_space)
+    return false;
+
+  target_color_params->color_space = gfx::ColorSpace(*target_color_space);
+  reader.Read(&target_color_params->sdr_max_luminance_nits);
+  reader.Read(&target_color_params->hdr_max_luminance_relative);
+  return true;
+}
+
 }  // namespace
 
 size_t NumberOfPlanesForYUVDecodeFormat(YUVDecodeFormat format) {
@@ -217,15 +264,13 @@
 
 ClientImageTransferCacheEntry::ClientImageTransferCacheEntry(
     const SkPixmap* pixmap,
-    const SkColorSpace* target_color_space,
-    bool needs_mips)
+    bool needs_mips,
+    absl::optional<TargetColorParams> target_color_params)
     : needs_mips_(needs_mips),
+      target_color_params_(target_color_params),
       id_(GetNextId()),
       pixmap_(pixmap),
-      target_color_space_(target_color_space),
       decoded_color_space_(nullptr) {
-  size_t target_color_space_size =
-      target_color_space ? target_color_space->writeToMemory(nullptr) : 0u;
   size_t pixmap_color_space_size =
       pixmap_->colorSpace() ? pixmap_->colorSpace()->writeToMemory(nullptr)
                             : 0u;
@@ -244,7 +289,7 @@
   safe_size += sizeof(uint32_t);  // has mips
   safe_size += sizeof(uint64_t) + align;  // pixels size + alignment
   safe_size += sizeof(uint64_t) + align;  // row bytes + alignment
-  safe_size += target_color_space_size + sizeof(uint64_t) + align;
+  safe_size += TargetColorParamsSize(target_color_params_);
   safe_size += pixmap_color_space_size + sizeof(uint64_t) + align;
   // Include 4 bytes of padding so we can always align our data pointer to a
   // 4-byte boundary.
@@ -259,12 +304,13 @@
     SkYUVAInfo::Subsampling subsampling,
     const SkColorSpace* decoded_color_space,
     SkYUVColorSpace yuv_color_space,
-    bool needs_mips)
+    bool needs_mips,
+    absl::optional<TargetColorParams> target_color_params)
     : needs_mips_(needs_mips),
+      target_color_params_(target_color_params),
       plane_config_(plane_config),
       id_(GetNextId()),
       pixmap_(nullptr),
-      target_color_space_(nullptr),
       decoded_color_space_(decoded_color_space),
       subsampling_(subsampling),
       yuv_color_space_(yuv_color_space) {
@@ -290,6 +336,7 @@
 
   safe_size += sizeof(uint32_t);  // has mips
   safe_size += sizeof(uint64_t);  // target color space stub (is nullptr)
+  safe_size += TargetColorParamsSize(target_color_params_);
 
   safe_size += sizeof(uint32_t);  // plane_config
   safe_size += sizeof(uint32_t);  // subsampling
@@ -336,7 +383,7 @@
   PaintOpWriter writer(data.data(), data.size(), options);
 
   writer.Write(static_cast<uint32_t>(needs_mips_ ? 1 : 0));
-  writer.Write(target_color_space_);
+  WriteTargetColorParams(writer, target_color_params_);
   writer.Write(plane_config_);
 
   if (plane_config_ != SkYUVAInfo::PlaneConfig::kUnknown) {
@@ -440,16 +487,8 @@
   uint32_t needs_mips;
   reader.Read(&needs_mips);
   has_mips_ = needs_mips;
-  sk_sp<SkColorSpace> target_color_space;
-  reader.Read(&target_color_space);
   absl::optional<TargetColorParams> target_color_params;
-  if (target_color_space) {
-    target_color_params = TargetColorParams();
-    target_color_params->color_space = gfx::ColorSpace(*target_color_space);
-    target_color_params->sdr_max_luminance_nits = kTempMaxLuminanceNits;
-    target_color_params->hdr_max_luminance_relative =
-        kTempHDRMaxLuminanceRelative;
-  }
+  ReadTargetColorParams(reader, target_color_params);
   plane_config_ = SkYUVAInfo::PlaneConfig::kUnknown;
   reader.Read(&plane_config_);
 
diff --git a/cc/paint/image_transfer_cache_entry.h b/cc/paint/image_transfer_cache_entry.h
index 5e810fbd..f3232d6 100644
--- a/cc/paint/image_transfer_cache_entry.h
+++ b/cc/paint/image_transfer_cache_entry.h
@@ -47,16 +47,18 @@
 class CC_PAINT_EXPORT ClientImageTransferCacheEntry final
     : public ClientTransferCacheEntryBase<TransferCacheEntryType::kImage> {
  public:
-  explicit ClientImageTransferCacheEntry(const SkPixmap* pixmap,
-                                         const SkColorSpace* target_color_space,
-                                         bool needs_mips);
-  explicit ClientImageTransferCacheEntry(
+  ClientImageTransferCacheEntry(
+      const SkPixmap* pixmap,
+      bool needs_mips,
+      absl::optional<TargetColorParams> target_color_params);
+  ClientImageTransferCacheEntry(
       const SkPixmap yuva_pixmaps[],
       SkYUVAInfo::PlaneConfig plane_config,
       SkYUVAInfo::Subsampling subsampling,
       const SkColorSpace* decoded_color_space,
       SkYUVColorSpace yuv_color_space,
-      bool needs_mips);
+      bool needs_mips,
+      absl::optional<TargetColorParams> target_color_params);
   ~ClientImageTransferCacheEntry() final;
 
   uint32_t Id() const final;
@@ -71,6 +73,7 @@
 
  private:
   const bool needs_mips_ = false;
+  absl::optional<TargetColorParams> target_color_params_;
   SkYUVAInfo::PlaneConfig plane_config_ = SkYUVAInfo::PlaneConfig::kUnknown;
   uint32_t id_;
   uint32_t size_ = 0;
@@ -78,9 +81,6 @@
 
   // RGBX-only members.
   const raw_ptr<const SkPixmap> pixmap_;
-  const raw_ptr<const SkColorSpace>
-      target_color_space_;  // Unused for YUV because Skia handles colorspaces
-                            // at raster.
 
   // YUVA-only members.
   absl::optional<std::array<const SkPixmap*, SkYUVAInfo::kMaxPlanes>>
diff --git a/cc/paint/image_transfer_cache_entry_unittest.cc b/cc/paint/image_transfer_cache_entry_unittest.cc
index e57ab84..f952eb58 100644
--- a/cc/paint/image_transfer_cache_entry_unittest.cc
+++ b/cc/paint/image_transfer_cache_entry_unittest.cc
@@ -228,7 +228,7 @@
   auto client_entry(std::make_unique<ClientImageTransferCacheEntry>(
       yuva_pixmaps.planes().data(), yuva_info.planeConfig(),
       yuva_info.subsampling(), nullptr /* decoded color space*/,
-      yuva_info.yuvColorSpace(), true /* needs_mips */));
+      yuva_info.yuvColorSpace(), true /* needs_mips */, absl::nullopt));
   uint32_t size = client_entry->SerializedSize();
   std::vector<uint8_t> data(size);
   ASSERT_TRUE(client_entry->Serialize(
@@ -378,7 +378,8 @@
   SkBitmap bitmap;
   bitmap.allocPixels(
       SkImageInfo::MakeN32Premul(gr_context->maxTextureSize() + 1, 10));
-  ClientImageTransferCacheEntry client_entry(&bitmap.pixmap(), nullptr, true);
+  ClientImageTransferCacheEntry client_entry(&bitmap.pixmap(), true,
+                                             absl::nullopt);
   std::vector<uint8_t> storage(client_entry.SerializedSize());
   client_entry.Serialize(base::make_span(storage.data(), storage.size()));
 
@@ -404,7 +405,8 @@
   SkBitmap bitmap;
   bitmap.allocPixels(
       SkImageInfo::MakeN32Premul(gr_context->maxTextureSize() + 1, 10));
-  ClientImageTransferCacheEntry client_entry(&bitmap.pixmap(), nullptr, false);
+  ClientImageTransferCacheEntry client_entry(&bitmap.pixmap(), false,
+                                             absl::nullopt);
   std::vector<uint8_t> storage(client_entry.SerializedSize());
   client_entry.Serialize(base::make_span(storage.data(), storage.size()));
 
diff --git a/cc/test/lottie_test_data.cc b/cc/test/lottie_test_data.cc
index 022b282..14aa705 100644
--- a/cc/test/lottie_test_data.cc
+++ b/cc/test/lottie_test_data.cc
@@ -5,6 +5,7 @@
 #include "cc/test/lottie_test_data.h"
 
 #include "base/strings/string_util.h"
+#include "cc/test/skia_common.h"
 
 namespace cc {
 
@@ -21,4 +22,20 @@
   return output;
 }
 
+std::string CreateCustomLottieDataWith2TextNodes(
+    base::StringPiece custom_text_node_name_0,
+    base::StringPiece custom_text_node_name_1) {
+  CHECK(!custom_text_node_name_0.empty());
+  CHECK(!custom_text_node_name_1.empty());
+  std::string output =
+      LoadSkottieFileFromTestData(kLottieDataWith2TextFileName);
+  base::ReplaceSubstringsAfterOffset(&output, /*start_offset=*/0,
+                                     kLottieDataWith2TextNode1,
+                                     custom_text_node_name_0);
+  base::ReplaceSubstringsAfterOffset(&output, /*start_offset=*/0,
+                                     kLottieDataWith2TextNode2,
+                                     custom_text_node_name_1);
+  return output;
+}
+
 }  // namespace cc
diff --git a/cc/test/lottie_test_data.h b/cc/test/lottie_test_data.h
index f682bfc..973e69f9 100644
--- a/cc/test/lottie_test_data.h
+++ b/cc/test/lottie_test_data.h
@@ -296,6 +296,13 @@
 constexpr base::StringPiece kLottieDataWith2TextNode2 = "text_node_2";
 constexpr base::StringPiece kLottieDataWith2TextNode2Text = "test_text_2";
 
+// Returns an animation with the same structure as
+// |kLottieDataWith2TextFileName| except with text node names specified by the
+// caller.
+std::string CreateCustomLottieDataWith2TextNodes(
+    base::StringPiece custom_text_node_name_0,
+    base::StringPiece custom_text_node_name_1);
+
 }  // namespace cc
 
 #endif  // CC_TEST_LOTTIE_TEST_DATA_H_
diff --git a/cc/test/skia_common.cc b/cc/test/skia_common.cc
index 9a6f3f6..c594d34 100644
--- a/cc/test/skia_common.cc
+++ b/cc/test/skia_common.cc
@@ -255,7 +255,7 @@
       std::vector<uint8_t>(json_span.begin(), json_span.end()));
 }
 
-scoped_refptr<SkottieWrapper> CreateSkottieFromTestDataDir(
+std::string LoadSkottieFileFromTestData(
     base::FilePath::StringPieceType animation_file_name) {
   base::FilePath animation_path;
   CHECK(base::PathService::Get(base::DIR_SRC_TEST_DATA_ROOT, &animation_path));
@@ -264,7 +264,13 @@
   std::string animation_json;
   CHECK(base::ReadFileToString(animation_path, &animation_json))
       << animation_path;
-  return CreateSkottieFromString(animation_json);
+  return animation_json;
+}
+
+scoped_refptr<SkottieWrapper> CreateSkottieFromTestDataDir(
+    base::FilePath::StringPieceType animation_file_name) {
+  return CreateSkottieFromString(
+      LoadSkottieFileFromTestData(animation_file_name));
 }
 
 PaintImage CreateNonDiscardablePaintImage(const gfx::Size& size) {
diff --git a/cc/test/skia_common.h b/cc/test/skia_common.h
index 1f59d09..444e8a4 100644
--- a/cc/test/skia_common.h
+++ b/cc/test/skia_common.h
@@ -6,6 +6,7 @@
 #define CC_TEST_SKIA_COMMON_H_
 
 #include <memory>
+#include <string>
 #include <vector>
 
 #include "base/files/file_path.h"
@@ -79,6 +80,8 @@
 scoped_refptr<SkottieWrapper> CreateSkottie(const gfx::Size& size,
                                             int duration_secs);
 scoped_refptr<SkottieWrapper> CreateSkottieFromString(base::StringPiece json);
+std::string LoadSkottieFileFromTestData(
+    base::FilePath::StringPieceType animation_file_name);
 scoped_refptr<SkottieWrapper> CreateSkottieFromTestDataDir(
     base::FilePath::StringPieceType animation_file_name);
 
diff --git a/cc/test/test_options_provider.cc b/cc/test/test_options_provider.cc
index e239f9a..ba4bae1 100644
--- a/cc/test/test_options_provider.cc
+++ b/cc/test/test_options_provider.cc
@@ -100,9 +100,9 @@
       SkBitmap::kZeroPixels_AllocFlag);
 
   // Create a transfer cache entry for this image.
-  auto color_space = SkColorSpace::MakeSRGB();
-  ClientImageTransferCacheEntry cache_entry(&bitmap.pixmap(), color_space.get(),
-                                            false /* needs_mips */);
+  TargetColorParams target_color_params;
+  ClientImageTransferCacheEntry cache_entry(
+      &bitmap.pixmap(), false /* needs_mips */, target_color_params);
   std::vector<uint8_t> data;
   data.resize(cache_entry.SerializedSize());
   if (!cache_entry.Serialize(base::span<uint8_t>(data.data(), data.size()))) {
diff --git a/cc/tiles/gpu_image_decode_cache.cc b/cc/tiles/gpu_image_decode_cache.cc
index d54d1123..e9090ca0 100644
--- a/cc/tiles/gpu_image_decode_cache.cc
+++ b/cc/tiles/gpu_image_decode_cache.cc
@@ -2131,6 +2131,8 @@
     color_space = nullptr;
   }
 
+  absl::optional<TargetColorParams> target_color_params;
+
   // Will be nullptr for non-HDR images or when we're using the default level.
   const bool needs_adjusted_color_space =
       NeedsColorSpaceAdjustedForUpload(draw_image);
@@ -2144,11 +2146,16 @@
           draw_image, image_data, color_space);
     } else if (image_data->yuva_pixmap_info.has_value()) {
       UploadImageIfNecessary_TransferCache_SoftwareDecode_YUVA(
-          draw_image, image_data, decoded_target_colorspace);
+          draw_image, image_data, decoded_target_colorspace,
+          target_color_params);
     } else {
+      if (color_space) {
+        target_color_params = draw_image.target_color_params();
+        target_color_params->color_space = gfx::ColorSpace(*color_space);
+      }
       UploadImageIfNecessary_TransferCache_SoftwareDecode_RGBA(
           draw_image, image_data, needs_adjusted_color_space,
-          decoded_target_colorspace, color_space);
+          decoded_target_colorspace, target_color_params);
     }
   } else {
     // Grab a reference to our decoded image. For the kCpu path, we will use
@@ -2217,7 +2224,8 @@
     UploadImageIfNecessary_TransferCache_SoftwareDecode_YUVA(
         const DrawImage& draw_image,
         ImageData* image_data,
-        sk_sp<SkColorSpace> decoded_target_colorspace) {
+        sk_sp<SkColorSpace> decoded_target_colorspace,
+        absl::optional<TargetColorParams> target_color_params) {
   DCHECK_EQ(image_data->mode, DecodedDataMode::kTransferCache);
   DCHECK(use_transfer_cache_);
   DCHECK(!image_data->decode.do_hardware_accelerated_decode());
@@ -2234,7 +2242,7 @@
       image_data->yuva_pixmap_info->yuvaInfo().subsampling(),
       decoded_target_colorspace.get(),
       image_data->yuva_pixmap_info->yuvaInfo().yuvColorSpace(),
-      image_data->needs_mips);
+      image_data->needs_mips, target_color_params);
   if (!image_entry.IsValid())
     return;
   InsertTransferCacheEntry(image_entry, image_data);
@@ -2246,7 +2254,7 @@
         ImageData* image_data,
         bool needs_adjusted_color_space,
         sk_sp<SkColorSpace> decoded_target_colorspace,
-        sk_sp<SkColorSpace> color_space) {
+        absl::optional<TargetColorParams> target_color_params) {
   DCHECK_EQ(image_data->mode, DecodedDataMode::kTransferCache);
   DCHECK(use_transfer_cache_);
   DCHECK(!image_data->decode.do_hardware_accelerated_decode());
@@ -2258,8 +2266,8 @@
   if (needs_adjusted_color_space)
     pixmap.setColorSpace(decoded_target_colorspace);
 
-  ClientImageTransferCacheEntry image_entry(&pixmap, color_space.get(),
-                                            image_data->needs_mips);
+  ClientImageTransferCacheEntry image_entry(&pixmap, image_data->needs_mips,
+                                            target_color_params);
   if (!image_entry.IsValid())
     return;
   InsertTransferCacheEntry(image_entry, image_data);
diff --git a/cc/tiles/gpu_image_decode_cache.h b/cc/tiles/gpu_image_decode_cache.h
index 127723d..51d3a9b9 100644
--- a/cc/tiles/gpu_image_decode_cache.h
+++ b/cc/tiles/gpu_image_decode_cache.h
@@ -696,13 +696,14 @@
   void UploadImageIfNecessary_TransferCache_SoftwareDecode_YUVA(
       const DrawImage& draw_image,
       ImageData* image_data,
-      sk_sp<SkColorSpace> decoded_target_colorspace);
+      sk_sp<SkColorSpace> decoded_target_colorspace,
+      absl::optional<TargetColorParams> target_color_params);
   void UploadImageIfNecessary_TransferCache_SoftwareDecode_RGBA(
       const DrawImage& draw_image,
       ImageData* image_data,
       bool needs_adjusted_color_space,
       sk_sp<SkColorSpace> decoded_target_colorspace,
-      sk_sp<SkColorSpace> color_space);
+      absl::optional<TargetColorParams> target_color_params);
   void UploadImageIfNecessary_GpuCpu_YUVA(
       const DrawImage& draw_image,
       ImageData* image_data,
diff --git a/chrome/VERSION b/chrome/VERSION
index 93285ae..86d3c5b4 100644
--- a/chrome/VERSION
+++ b/chrome/VERSION
@@ -1,4 +1,4 @@
 MAJOR=101
 MINOR=0
-BUILD=4909
+BUILD=4910
 PATCH=0
diff --git a/chrome/android/features/autofill_assistant/BUILD.gn b/chrome/android/features/autofill_assistant/BUILD.gn
index a521543..ec3cb83 100644
--- a/chrome/android/features/autofill_assistant/BUILD.gn
+++ b/chrome/android/features/autofill_assistant/BUILD.gn
@@ -26,6 +26,7 @@
     "//chrome/browser/util:java",
     "//components/autofill/android:autofill_java",
     "//components/autofill_assistant/android:autofill_assistant_public_java",
+    "//components/autofill_assistant/android:java",
     "//components/autofill_assistant/android:java_resources",
     "//components/autofill_assistant/android:public_java",
     "//components/browser_ui/bottomsheet/android:java",
@@ -62,11 +63,8 @@
 
   sources = [
     "java/src/org/chromium/chrome/browser/autofill_assistant/AbstractListObserver.java",
-    "java/src/org/chromium/chrome/browser/autofill_assistant/AssistantAccessibilityUtils.java",
     "java/src/org/chromium/chrome/browser/autofill_assistant/AssistantBottomBarCoordinator.java",
-    "java/src/org/chromium/chrome/browser/autofill_assistant/AssistantBottomBarDelegate.java",
     "java/src/org/chromium/chrome/browser/autofill_assistant/AssistantBottomBarNativeDelegate.java",
-    "java/src/org/chromium/chrome/browser/autofill_assistant/AssistantBottomSheetContent.java",
     "java/src/org/chromium/chrome/browser/autofill_assistant/AssistantCoordinator.java",
     "java/src/org/chromium/chrome/browser/autofill_assistant/AssistantDialogButton.java",
     "java/src/org/chromium/chrome/browser/autofill_assistant/AssistantInfoPopup.java",
@@ -75,19 +73,12 @@
     "java/src/org/chromium/chrome/browser/autofill_assistant/AssistantOnboardingHelperImpl.java",
     "java/src/org/chromium/chrome/browser/autofill_assistant/AssistantPeekHeightCoordinator.java",
     "java/src/org/chromium/chrome/browser/autofill_assistant/AssistantRootViewContainer.java",
-    "java/src/org/chromium/chrome/browser/autofill_assistant/AssistantTagsForTesting.java",
-    "java/src/org/chromium/chrome/browser/autofill_assistant/AssistantTextUtils.java",
     "java/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantActionHandlerImpl.java",
     "java/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantClient.java",
     "java/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantDependencyInjector.java",
     "java/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantDirectActionImpl.java",
     "java/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantModuleEntryImpl.java",
     "java/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantUiController.java",
-    "java/src/org/chromium/chrome/browser/autofill_assistant/BottomSheetUtils.java",
-    "java/src/org/chromium/chrome/browser/autofill_assistant/FeedbackContext.java",
-    "java/src/org/chromium/chrome/browser/autofill_assistant/LayoutUtils.java",
-    "java/src/org/chromium/chrome/browser/autofill_assistant/ScrollToHideGestureListener.java",
-    "java/src/org/chromium/chrome/browser/autofill_assistant/SizeListenableLinearLayout.java",
     "java/src/org/chromium/chrome/browser/autofill_assistant/carousel/AssistantActionsCarouselCoordinator.java",
     "java/src/org/chromium/chrome/browser/autofill_assistant/carousel/AssistantActionsDecoration.java",
     "java/src/org/chromium/chrome/browser/autofill_assistant/carousel/AssistantCarouselModel.java",
@@ -331,6 +322,7 @@
     "//chrome/browser/ui/android/omnibox:java",
     "//chrome/browser/util:java",
     "//chrome/test/android:chrome_java_test_support",
+    "//components/autofill_assistant/android:java",
     "//components/autofill_assistant/android:public_java",
     "//components/autofill_assistant/browser:proto_java",
     "//components/browser_ui/bottomsheet/android:java",
diff --git a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/AssistantBottomBarCoordinator.java b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/AssistantBottomBarCoordinator.java
index ec463cc..219ab63 100644
--- a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/AssistantBottomBarCoordinator.java
+++ b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/AssistantBottomBarCoordinator.java
@@ -34,12 +34,17 @@
 import org.chromium.chrome.browser.autofill_assistant.overlay.AssistantOverlayCoordinator;
 import org.chromium.chrome.browser.autofill_assistant.user_data.AssistantCollectUserDataCoordinator;
 import org.chromium.chrome.browser.autofill_assistant.user_data.AssistantCollectUserDataModel;
+import org.chromium.components.autofill_assistant.AssistantBottomBarDelegate;
+import org.chromium.components.autofill_assistant.AssistantBottomSheetContent;
 import org.chromium.components.autofill_assistant.AssistantBrowserControlsFactory;
 import org.chromium.components.autofill_assistant.AssistantEditorFactory;
 import org.chromium.components.autofill_assistant.AssistantInfoPageUtil;
 import org.chromium.components.autofill_assistant.AssistantProfileImageUtil;
 import org.chromium.components.autofill_assistant.AssistantSettingsUtil;
 import org.chromium.components.autofill_assistant.AssistantTabObscuringUtil;
+import org.chromium.components.autofill_assistant.AssistantTagsForTesting;
+import org.chromium.components.autofill_assistant.BottomSheetUtils;
+import org.chromium.components.autofill_assistant.LayoutUtils;
 import org.chromium.components.browser_ui.bottomsheet.BottomSheetContent;
 import org.chromium.components.browser_ui.bottomsheet.BottomSheetController;
 import org.chromium.components.browser_ui.bottomsheet.BottomSheetController.SheetState;
diff --git a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/AssistantBottomBarNativeDelegate.java b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/AssistantBottomBarNativeDelegate.java
index 6c75215c..731081c 100644
--- a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/AssistantBottomBarNativeDelegate.java
+++ b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/AssistantBottomBarNativeDelegate.java
@@ -7,6 +7,7 @@
 import org.chromium.base.annotations.CalledByNative;
 import org.chromium.base.annotations.JNINamespace;
 import org.chromium.base.annotations.NativeMethods;
+import org.chromium.components.autofill_assistant.AssistantBottomBarDelegate;
 
 /** Delegate for the bottom bar which forwards events to a native counterpart. */
 @JNINamespace("autofill_assistant")
diff --git a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/AssistantModel.java b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/AssistantModel.java
index 13bc769..da5acdc 100644
--- a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/AssistantModel.java
+++ b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/AssistantModel.java
@@ -14,6 +14,7 @@
 import org.chromium.chrome.browser.autofill_assistant.infobox.AssistantInfoBoxModel;
 import org.chromium.chrome.browser.autofill_assistant.overlay.AssistantOverlayModel;
 import org.chromium.chrome.browser.autofill_assistant.user_data.AssistantCollectUserDataModel;
+import org.chromium.components.autofill_assistant.AssistantBottomBarDelegate;
 import org.chromium.content_public.browser.WebContents;
 import org.chromium.ui.modelutil.PropertyModel;
 
diff --git a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/carousel/AssistantActionsCarouselCoordinator.java b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/carousel/AssistantActionsCarouselCoordinator.java
index 62d64b1e..9b63bbb 100644
--- a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/carousel/AssistantActionsCarouselCoordinator.java
+++ b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/carousel/AssistantActionsCarouselCoordinator.java
@@ -4,7 +4,7 @@
 
 package org.chromium.chrome.browser.autofill_assistant.carousel;
 
-import static org.chromium.chrome.browser.autofill_assistant.AssistantTagsForTesting.RECYCLER_VIEW_TAG;
+import static org.chromium.components.autofill_assistant.AssistantTagsForTesting.RECYCLER_VIEW_TAG;
 
 import android.content.Context;
 import android.view.View;
diff --git a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/carousel/AssistantChipViewHolder.java b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/carousel/AssistantChipViewHolder.java
index dc4a3cc..ba582c7b 100644
--- a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/carousel/AssistantChipViewHolder.java
+++ b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/carousel/AssistantChipViewHolder.java
@@ -14,7 +14,7 @@
 import androidx.recyclerview.widget.RecyclerView.ViewHolder;
 
 import org.chromium.chrome.autofill_assistant.R;
-import org.chromium.chrome.browser.autofill_assistant.LayoutUtils;
+import org.chromium.components.autofill_assistant.LayoutUtils;
 
 /**
  * The {@link ViewHolder} responsible for reflecting an {@link AssistantChip} to a {@link
diff --git a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/details/AssistantDetailsAdapter.java b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/details/AssistantDetailsAdapter.java
index a03cf390..effa2f3e 100644
--- a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/details/AssistantDetailsAdapter.java
+++ b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/details/AssistantDetailsAdapter.java
@@ -4,7 +4,7 @@
 
 package org.chromium.chrome.browser.autofill_assistant.details;
 
-import static org.chromium.chrome.browser.autofill_assistant.AssistantAccessibilityUtils.setAccessibility;
+import static org.chromium.components.autofill_assistant.AssistantAccessibilityUtils.setAccessibility;
 
 import android.animation.ArgbEvaluator;
 import android.animation.ValueAnimator;
@@ -35,9 +35,9 @@
 import org.chromium.base.ApiCompatibilityUtils;
 import org.chromium.base.Callback;
 import org.chromium.chrome.autofill_assistant.R;
-import org.chromium.chrome.browser.autofill_assistant.AssistantTextUtils;
-import org.chromium.chrome.browser.autofill_assistant.LayoutUtils;
 import org.chromium.components.autofill_assistant.AssistantInfoPageUtil;
+import org.chromium.components.autofill_assistant.AssistantTextUtils;
+import org.chromium.components.autofill_assistant.LayoutUtils;
 import org.chromium.components.browser_ui.modaldialog.AppModalPresenter;
 import org.chromium.components.browser_ui.widget.animation.Interpolators;
 import org.chromium.components.image_fetcher.ImageFetcher;
diff --git a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/form/AssistantFormCoordinator.java b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/form/AssistantFormCoordinator.java
index f56d71b..bcadfc1f 100644
--- a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/form/AssistantFormCoordinator.java
+++ b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/form/AssistantFormCoordinator.java
@@ -11,8 +11,8 @@
 import android.widget.TextView;
 
 import org.chromium.chrome.autofill_assistant.R;
-import org.chromium.chrome.browser.autofill_assistant.AssistantTextUtils;
-import org.chromium.chrome.browser.autofill_assistant.LayoutUtils;
+import org.chromium.components.autofill_assistant.AssistantTextUtils;
+import org.chromium.components.autofill_assistant.LayoutUtils;
 
 /**
  * A coordinator responsible for showing a form to the user.
diff --git a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/form/AssistantFormCounterInput.java b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/form/AssistantFormCounterInput.java
index 66b7200..3eb4dd01 100644
--- a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/form/AssistantFormCounterInput.java
+++ b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/form/AssistantFormCounterInput.java
@@ -15,9 +15,9 @@
 import android.widget.TextView;
 
 import org.chromium.chrome.autofill_assistant.R;
-import org.chromium.chrome.browser.autofill_assistant.AssistantTextUtils;
-import org.chromium.chrome.browser.autofill_assistant.LayoutUtils;
 import org.chromium.components.autofill_assistant.AssistantStaticDependencies;
+import org.chromium.components.autofill_assistant.AssistantTextUtils;
+import org.chromium.components.autofill_assistant.LayoutUtils;
 
 import java.util.ArrayList;
 import java.util.List;
diff --git a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/form/AssistantFormSelectionInput.java b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/form/AssistantFormSelectionInput.java
index 3a23e4a1..ea2111ec 100644
--- a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/form/AssistantFormSelectionInput.java
+++ b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/form/AssistantFormSelectionInput.java
@@ -12,9 +12,9 @@
 import android.widget.TextView;
 
 import org.chromium.chrome.autofill_assistant.R;
-import org.chromium.chrome.browser.autofill_assistant.AssistantTextUtils;
-import org.chromium.chrome.browser.autofill_assistant.LayoutUtils;
 import org.chromium.chrome.browser.autofill_assistant.user_data.AssistantChoiceList;
+import org.chromium.components.autofill_assistant.AssistantTextUtils;
+import org.chromium.components.autofill_assistant.LayoutUtils;
 
 import java.util.ArrayList;
 import java.util.List;
diff --git a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/generic_ui/AssistantViewFactory.java b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/generic_ui/AssistantViewFactory.java
index 702fa180..55975888 100644
--- a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/generic_ui/AssistantViewFactory.java
+++ b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/generic_ui/AssistantViewFactory.java
@@ -4,7 +4,7 @@
 
 package org.chromium.chrome.browser.autofill_assistant.generic_ui;
 
-import static org.chromium.chrome.browser.autofill_assistant.AssistantAccessibilityUtils.setAccessibility;
+import static org.chromium.components.autofill_assistant.AssistantAccessibilityUtils.setAccessibility;
 
 import android.content.Context;
 import android.text.Editable;
@@ -24,9 +24,9 @@
 import org.chromium.chrome.browser.autofill.prefeditor.EditorFieldModel;
 import org.chromium.chrome.browser.autofill.prefeditor.EditorTextField;
 import org.chromium.chrome.browser.autofill_assistant.AssistantChevronStyle;
-import org.chromium.chrome.browser.autofill_assistant.LayoutUtils;
 import org.chromium.chrome.browser.autofill_assistant.user_data.AssistantVerticalExpander;
 import org.chromium.chrome.browser.autofill_assistant.user_data.AssistantVerticalExpanderAccordion;
+import org.chromium.components.autofill_assistant.LayoutUtils;
 import org.chromium.ui.widget.ChromeImageView;
 
 /** Generic view factory. */
diff --git a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/generic_ui/AssistantViewInteractions.java b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/generic_ui/AssistantViewInteractions.java
index 7de07e5..d38ff1b6 100644
--- a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/generic_ui/AssistantViewInteractions.java
+++ b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/generic_ui/AssistantViewInteractions.java
@@ -18,8 +18,8 @@
 import org.chromium.base.annotations.CalledByNative;
 import org.chromium.base.annotations.JNINamespace;
 import org.chromium.chrome.browser.autofill.prefeditor.EditorTextField;
-import org.chromium.chrome.browser.autofill_assistant.AssistantTextUtils;
 import org.chromium.chrome.browser.autofill_assistant.user_data.AssistantDateTime;
+import org.chromium.components.autofill_assistant.AssistantTextUtils;
 import org.chromium.content.browser.input.PopupItemType;
 import org.chromium.content.browser.input.SelectPopupDialog;
 import org.chromium.content.browser.input.SelectPopupItem;
diff --git a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/header/AssistantHeaderCoordinator.java b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/header/AssistantHeaderCoordinator.java
index da36da1..7b22bef 100644
--- a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/header/AssistantHeaderCoordinator.java
+++ b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/header/AssistantHeaderCoordinator.java
@@ -17,11 +17,11 @@
 import androidx.recyclerview.widget.RecyclerView;
 
 import org.chromium.chrome.autofill_assistant.R;
-import org.chromium.chrome.browser.autofill_assistant.LayoutUtils;
 import org.chromium.chrome.browser.autofill_assistant.carousel.AssistantChipAdapter;
 import org.chromium.chrome.browser.autofill_assistant.header.AssistantHeaderViewBinder.ViewHolder;
 import org.chromium.components.autofill_assistant.AssistantProfileImageUtil;
 import org.chromium.components.autofill_assistant.AssistantSettingsUtil;
+import org.chromium.components.autofill_assistant.LayoutUtils;
 import org.chromium.ui.modelutil.PropertyModelChangeProcessor;
 import org.chromium.ui.util.AccessibilityUtil;
 
diff --git a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/header/AssistantHeaderViewBinder.java b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/header/AssistantHeaderViewBinder.java
index 9de1d64..2eafdf97 100644
--- a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/header/AssistantHeaderViewBinder.java
+++ b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/header/AssistantHeaderViewBinder.java
@@ -18,10 +18,10 @@
 
 import org.chromium.base.task.PostTask;
 import org.chromium.chrome.autofill_assistant.R;
-import org.chromium.chrome.browser.autofill_assistant.AssistantTagsForTesting;
-import org.chromium.chrome.browser.autofill_assistant.AssistantTextUtils;
 import org.chromium.chrome.browser.autofill_assistant.carousel.AssistantChipAdapter;
 import org.chromium.components.autofill_assistant.AssistantSettingsUtil;
+import org.chromium.components.autofill_assistant.AssistantTagsForTesting;
+import org.chromium.components.autofill_assistant.AssistantTextUtils;
 import org.chromium.components.browser_ui.widget.textbubble.TextBubble;
 import org.chromium.content_public.browser.UiThreadTaskTraits;
 import org.chromium.ui.modelutil.PropertyKey;
diff --git a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/header/AssistantStepProgressBar.java b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/header/AssistantStepProgressBar.java
index fed13a3..e3bd7ee0 100644
--- a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/header/AssistantStepProgressBar.java
+++ b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/header/AssistantStepProgressBar.java
@@ -18,9 +18,9 @@
 
 import org.chromium.base.ApiCompatibilityUtils;
 import org.chromium.chrome.autofill_assistant.R;
-import org.chromium.chrome.browser.autofill_assistant.AssistantTagsForTesting;
 import org.chromium.chrome.browser.autofill_assistant.drawable.AssistantDrawableIcon;
 import org.chromium.chrome.browser.autofill_assistant.generic_ui.AssistantDrawable;
+import org.chromium.components.autofill_assistant.AssistantTagsForTesting;
 import org.chromium.components.browser_ui.styles.SemanticColorUtils;
 import org.chromium.components.browser_ui.widget.animation.Interpolators;
 import org.chromium.ui.widget.ChromeImageView;
diff --git a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/infobox/AssistantInfoBoxCoordinator.java b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/infobox/AssistantInfoBoxCoordinator.java
index cfa741c..5bb3081 100644
--- a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/infobox/AssistantInfoBoxCoordinator.java
+++ b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/infobox/AssistantInfoBoxCoordinator.java
@@ -8,8 +8,8 @@
 import android.view.View;
 
 import org.chromium.chrome.autofill_assistant.R;
-import org.chromium.chrome.browser.autofill_assistant.LayoutUtils;
 import org.chromium.chrome.browser.autofill_assistant.infobox.AssistantInfoBoxViewBinder.ViewHolder;
+import org.chromium.components.autofill_assistant.LayoutUtils;
 import org.chromium.components.image_fetcher.ImageFetcher;
 import org.chromium.ui.modelutil.PropertyModelChangeProcessor;
 
diff --git a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/infobox/AssistantInfoBoxViewBinder.java b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/infobox/AssistantInfoBoxViewBinder.java
index 53d2e803..092c964a 100644
--- a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/infobox/AssistantInfoBoxViewBinder.java
+++ b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/infobox/AssistantInfoBoxViewBinder.java
@@ -11,7 +11,7 @@
 import android.widget.TextView;
 
 import org.chromium.chrome.autofill_assistant.R;
-import org.chromium.chrome.browser.autofill_assistant.AssistantTextUtils;
+import org.chromium.components.autofill_assistant.AssistantTextUtils;
 import org.chromium.components.image_fetcher.ImageFetcher;
 import org.chromium.ui.modelutil.PropertyKey;
 import org.chromium.ui.modelutil.PropertyModelChangeProcessor;
diff --git a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/onboarding/BottomSheetOnboardingCoordinator.java b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/onboarding/BottomSheetOnboardingCoordinator.java
index 85dd164d..7392159 100644
--- a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/onboarding/BottomSheetOnboardingCoordinator.java
+++ b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/onboarding/BottomSheetOnboardingCoordinator.java
@@ -17,15 +17,15 @@
 
 import org.chromium.base.Callback;
 import org.chromium.chrome.autofill_assistant.R;
-import org.chromium.chrome.browser.autofill_assistant.AssistantBottomBarDelegate;
-import org.chromium.chrome.browser.autofill_assistant.AssistantBottomSheetContent;
-import org.chromium.chrome.browser.autofill_assistant.BottomSheetUtils;
-import org.chromium.chrome.browser.autofill_assistant.LayoutUtils;
 import org.chromium.chrome.browser.autofill_assistant.overlay.AssistantOverlayCoordinator;
 import org.chromium.chrome.browser.autofill_assistant.overlay.AssistantOverlayModel;
 import org.chromium.chrome.browser.autofill_assistant.overlay.AssistantOverlayState;
+import org.chromium.components.autofill_assistant.AssistantBottomBarDelegate;
+import org.chromium.components.autofill_assistant.AssistantBottomSheetContent;
 import org.chromium.components.autofill_assistant.AssistantBrowserControlsFactory;
 import org.chromium.components.autofill_assistant.AssistantInfoPageUtil;
+import org.chromium.components.autofill_assistant.BottomSheetUtils;
+import org.chromium.components.autofill_assistant.LayoutUtils;
 import org.chromium.components.browser_ui.bottomsheet.BottomSheetContent;
 import org.chromium.components.browser_ui.bottomsheet.BottomSheetController;
 import org.chromium.components.browser_ui.bottomsheet.BottomSheetController.SheetState;
diff --git a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/onboarding/DialogOnboardingCoordinator.java b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/onboarding/DialogOnboardingCoordinator.java
index b70d433..500b52d 100644
--- a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/onboarding/DialogOnboardingCoordinator.java
+++ b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/onboarding/DialogOnboardingCoordinator.java
@@ -17,8 +17,8 @@
 
 import org.chromium.base.Callback;
 import org.chromium.chrome.autofill_assistant.R;
-import org.chromium.chrome.browser.autofill_assistant.LayoutUtils;
 import org.chromium.components.autofill_assistant.AssistantInfoPageUtil;
+import org.chromium.components.autofill_assistant.LayoutUtils;
 import org.chromium.content_public.browser.BrowserContextHandle;
 
 import java.util.Map;
diff --git a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/trigger_scripts/AssistantTriggerScript.java b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/trigger_scripts/AssistantTriggerScript.java
index 27b8ebe6..211a3c0 100644
--- a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/trigger_scripts/AssistantTriggerScript.java
+++ b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/trigger_scripts/AssistantTriggerScript.java
@@ -15,20 +15,20 @@
 
 import org.chromium.base.supplier.ObservableSupplierImpl;
 import org.chromium.chrome.autofill_assistant.R;
-import org.chromium.chrome.browser.autofill_assistant.AssistantBottomBarDelegate;
-import org.chromium.chrome.browser.autofill_assistant.AssistantBottomSheetContent;
 import org.chromium.chrome.browser.autofill_assistant.AssistantRootViewContainer;
 import org.chromium.chrome.browser.autofill_assistant.AutofillAssistantDependencyInjector;
-import org.chromium.chrome.browser.autofill_assistant.BottomSheetUtils;
-import org.chromium.chrome.browser.autofill_assistant.LayoutUtils;
-import org.chromium.chrome.browser.autofill_assistant.ScrollToHideGestureListener;
 import org.chromium.chrome.browser.autofill_assistant.carousel.AssistantChip;
 import org.chromium.chrome.browser.autofill_assistant.carousel.AssistantChipViewHolder;
 import org.chromium.chrome.browser.autofill_assistant.generic_ui.AssistantDimension;
 import org.chromium.chrome.browser.autofill_assistant.header.AssistantHeaderCoordinator;
 import org.chromium.chrome.browser.autofill_assistant.header.AssistantHeaderModel;
+import org.chromium.components.autofill_assistant.AssistantBottomBarDelegate;
+import org.chromium.components.autofill_assistant.AssistantBottomSheetContent;
 import org.chromium.components.autofill_assistant.AssistantProfileImageUtil;
 import org.chromium.components.autofill_assistant.AssistantSettingsUtil;
+import org.chromium.components.autofill_assistant.BottomSheetUtils;
+import org.chromium.components.autofill_assistant.LayoutUtils;
+import org.chromium.components.autofill_assistant.ScrollToHideGestureListener;
 import org.chromium.components.browser_ui.bottomsheet.BottomSheetContent;
 import org.chromium.components.browser_ui.bottomsheet.BottomSheetController;
 import org.chromium.components.browser_ui.bottomsheet.BottomSheetController.StateChangeReason;
diff --git a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/user_data/AssistantChoiceList.java b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/user_data/AssistantChoiceList.java
index d81d336..b17dbf2 100644
--- a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/user_data/AssistantChoiceList.java
+++ b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/user_data/AssistantChoiceList.java
@@ -25,7 +25,7 @@
 import org.chromium.base.ApiCompatibilityUtils;
 import org.chromium.base.Callback;
 import org.chromium.chrome.autofill_assistant.R;
-import org.chromium.chrome.browser.autofill_assistant.AssistantTagsForTesting;
+import org.chromium.components.autofill_assistant.AssistantTagsForTesting;
 import org.chromium.components.browser_ui.widget.TintedDrawable;
 import org.chromium.ui.widget.ChromeImageView;
 
diff --git a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/user_data/AssistantCollectUserDataBinder.java b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/user_data/AssistantCollectUserDataBinder.java
index 9d6db01..3cb3c6fa 100644
--- a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/user_data/AssistantCollectUserDataBinder.java
+++ b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/user_data/AssistantCollectUserDataBinder.java
@@ -13,6 +13,7 @@
 import org.chromium.chrome.browser.autofill_assistant.user_data.AssistantCollectUserDataModel.LoginChoiceModel;
 import org.chromium.chrome.browser.autofill_assistant.user_data.additional_sections.AssistantAdditionalSection.Delegate;
 import org.chromium.chrome.browser.autofill_assistant.user_data.additional_sections.AssistantAdditionalSectionContainer;
+import org.chromium.components.autofill_assistant.AssistantEditor.AssistantPaymentInstrumentEditor;
 import org.chromium.components.autofill_assistant.AssistantEditorFactory;
 import org.chromium.components.autofill_assistant.AssistantOptionModel.AddressModel;
 import org.chromium.components.autofill_assistant.AssistantOptionModel.ContactModel;
@@ -186,6 +187,10 @@
         return model.get(AssistantCollectUserDataModel.REQUEST_PHONE_NUMBER_SEPARATELY);
     }
 
+    private boolean shouldShowPaymentInstruments(AssistantCollectUserDataModel model) {
+        return model.get(AssistantCollectUserDataModel.REQUEST_PAYMENT);
+    }
+
     private boolean updateSectionTitles(
             AssistantCollectUserDataModel model, PropertyKey propertyKey, ViewHolder view) {
         if (propertyKey == AssistantCollectUserDataModel.CONTACT_SECTION_TITLE) {
@@ -215,7 +220,7 @@
     private boolean updateSectionContents(
             AssistantCollectUserDataModel model, PropertyKey propertyKey, ViewHolder view) {
         if (propertyKey == AssistantCollectUserDataModel.AVAILABLE_PAYMENT_INSTRUMENTS) {
-            if (model.get(AssistantCollectUserDataModel.REQUEST_PAYMENT)) {
+            if (shouldShowPaymentInstruments(model)) {
                 view.mPaymentMethodSection.onAvailablePaymentMethodsChanged(
                         model.get(AssistantCollectUserDataModel.AVAILABLE_PAYMENT_INSTRUMENTS));
             }
@@ -239,7 +244,7 @@
             }
             return true;
         } else if (propertyKey == AssistantCollectUserDataModel.AVAILABLE_BILLING_ADDRESSES) {
-            if (model.get(AssistantCollectUserDataModel.REQUEST_PAYMENT)) {
+            if (shouldShowPaymentInstruments(model)) {
                 view.mPaymentMethodSection.onAddressesChanged(
                         model.get(AssistantCollectUserDataModel.AVAILABLE_BILLING_ADDRESSES));
             }
@@ -324,9 +329,9 @@
      */
     private boolean updateSectionVisibility(
             AssistantCollectUserDataModel model, PropertyKey propertyKey, ViewHolder view) {
-        if ((propertyKey == AssistantCollectUserDataModel.REQUEST_NAME)
-                || (propertyKey == AssistantCollectUserDataModel.REQUEST_EMAIL)
-                || (propertyKey == AssistantCollectUserDataModel.REQUEST_PHONE)) {
+        if (propertyKey == AssistantCollectUserDataModel.REQUEST_NAME
+                || propertyKey == AssistantCollectUserDataModel.REQUEST_EMAIL
+                || propertyKey == AssistantCollectUserDataModel.REQUEST_PHONE) {
             view.mContactDetailsSection.setVisible(shouldShowContactDetails(model));
             return true;
         } else if (propertyKey == AssistantCollectUserDataModel.REQUEST_PHONE_NUMBER_SEPARATELY) {
@@ -338,8 +343,7 @@
                     model.get(AssistantCollectUserDataModel.REQUEST_SHIPPING_ADDRESS));
             return true;
         } else if (propertyKey == AssistantCollectUserDataModel.REQUEST_PAYMENT) {
-            view.mPaymentMethodSection.setVisible(
-                    (model.get(AssistantCollectUserDataModel.REQUEST_PAYMENT)));
+            view.mPaymentMethodSection.setVisible(shouldShowPaymentInstruments(model));
             return true;
         } else if (propertyKey == AssistantCollectUserDataModel.SHOW_TERMS_AS_CHECKBOX) {
             if (model.get(AssistantCollectUserDataModel.SHOW_TERMS_AS_CHECKBOX)) {
@@ -396,7 +400,7 @@
             // No need to reset selection if null, this will be handled by setItems().
             return true;
         } else if (propertyKey == AssistantCollectUserDataModel.SELECTED_PAYMENT_INSTRUMENT) {
-            if (!model.get(AssistantCollectUserDataModel.REQUEST_PAYMENT)) {
+            if (!shouldShowPaymentInstruments(model)) {
                 return true;
             }
             PaymentInstrumentModel paymentInstrument =
@@ -519,7 +523,7 @@
                     view.mSectionToSectionPadding, view.mSectionToSectionPadding);
             view.mAppendedSections.setPaddings(view.mSectionToSectionPadding,
                     view.mSectionToSectionPadding, view.mSectionToSectionPadding);
-        } else if (model.get(AssistantCollectUserDataModel.REQUEST_PAYMENT)) {
+        } else if (shouldShowPaymentInstruments(model)) {
             view.mPaymentMethodSection.setPaddings(0, view.mSectionToSectionPadding);
             view.mShippingAddressSection.setPaddings(
                     view.mSectionToSectionPadding, view.mSectionToSectionPadding);
@@ -568,7 +572,9 @@
                 && propertyKey != AssistantCollectUserDataModel.SUPPORTED_BASIC_CARD_NETWORKS
                 && propertyKey != AssistantCollectUserDataModel.SHOULD_STORE_USER_DATA_CHANGES
                 && propertyKey != AssistantCollectUserDataModel.USE_GMS_CORE_EDIT_DIALOGS
-                && propertyKey != AssistantCollectUserDataModel.ACCOUNT_EMAIL) {
+                && propertyKey != AssistantCollectUserDataModel.ACCOUNT_EMAIL
+                && propertyKey
+                        != AssistantCollectUserDataModel.ADD_PAYMENT_INSTRUMENT_ACTION_TOKEN) {
             return false;
         }
 
@@ -586,16 +592,13 @@
 
         if (shouldShowContactDetails(model)) {
             updateContactEditor(model, view, webContents, shouldStoreChanges);
-            updatePhoneNumberEditor(model, view, webContents, shouldStoreChanges);
+            updatePhoneNumberEditor(model, view);
         }
 
         view.mShippingAddressSection.setEditor(view.mEditorFactory.createAddressEditor(
                 webContents, view.mActivity, shouldStoreChanges));
 
-        view.mPaymentMethodSection.setEditor(
-                view.mEditorFactory.createPaymentInstrumentEditor(webContents, view.mActivity,
-                        model.get(AssistantCollectUserDataModel.SUPPORTED_BASIC_CARD_NETWORKS),
-                        shouldStoreChanges));
+        updatePaymentEditor(model, view, webContents, shouldStoreChanges);
 
         return true;
     }
@@ -619,8 +622,7 @@
         }
     }
 
-    private void updatePhoneNumberEditor(AssistantCollectUserDataModel model, ViewHolder view,
-            WebContents webContents, boolean shouldStoreChanges) {
+    private void updatePhoneNumberEditor(AssistantCollectUserDataModel model, ViewHolder view) {
         if (model.get(AssistantCollectUserDataModel.USE_GMS_CORE_EDIT_DIALOGS)) {
             view.mPhoneNumberSection.setRequestReloadOnChange(true);
             view.mPhoneNumberSection.setEditor(
@@ -634,4 +636,24 @@
             view.mPhoneNumberSection.setEditor(null);
         }
     }
+
+    private void updatePaymentEditor(AssistantCollectUserDataModel model, ViewHolder view,
+            WebContents webContents, boolean shouldStoreChanges) {
+        AssistantPaymentInstrumentEditor editor = null;
+        if (model.get(AssistantCollectUserDataModel.USE_GMS_CORE_EDIT_DIALOGS)) {
+            byte[] addInstrumentActionToken =
+                    model.get(AssistantCollectUserDataModel.ADD_PAYMENT_INSTRUMENT_ACTION_TOKEN);
+            if (addInstrumentActionToken != null) {
+                editor = view.mEditorFactory.createGmsPaymentInstrumentEditor(view.mActivity,
+                        view.mWindowAndroid, model.get(AssistantCollectUserDataModel.ACCOUNT_EMAIL),
+                        addInstrumentActionToken);
+            }
+        } else {
+            editor = view.mEditorFactory.createPaymentInstrumentEditor(webContents, view.mActivity,
+                    model.get(AssistantCollectUserDataModel.SUPPORTED_BASIC_CARD_NETWORKS),
+                    shouldStoreChanges);
+        }
+
+        view.mPaymentMethodSection.setEditor(editor);
+    }
 }
diff --git a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/user_data/AssistantCollectUserDataCoordinator.java b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/user_data/AssistantCollectUserDataCoordinator.java
index 6cdae482..b0d12ed 100644
--- a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/user_data/AssistantCollectUserDataCoordinator.java
+++ b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/user_data/AssistantCollectUserDataCoordinator.java
@@ -10,10 +10,10 @@
 import android.widget.LinearLayout;
 
 import org.chromium.chrome.autofill_assistant.R;
-import org.chromium.chrome.browser.autofill_assistant.AssistantTagsForTesting;
-import org.chromium.chrome.browser.autofill_assistant.LayoutUtils;
 import org.chromium.chrome.browser.autofill_assistant.user_data.additional_sections.AssistantAdditionalSectionContainer;
 import org.chromium.components.autofill_assistant.AssistantEditorFactory;
+import org.chromium.components.autofill_assistant.AssistantTagsForTesting;
+import org.chromium.components.autofill_assistant.LayoutUtils;
 import org.chromium.ui.base.WindowAndroid;
 import org.chromium.ui.modelutil.PropertyModelChangeProcessor;
 
diff --git a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/user_data/AssistantCollectUserDataModel.java b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/user_data/AssistantCollectUserDataModel.java
index 1dcc387e..70f9897 100644
--- a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/user_data/AssistantCollectUserDataModel.java
+++ b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/user_data/AssistantCollectUserDataModel.java
@@ -186,6 +186,9 @@
     public static final WritableBooleanPropertyKey USE_GMS_CORE_EDIT_DIALOGS =
             new WritableBooleanPropertyKey();
 
+    public static final WritableObjectPropertyKey<byte[]> ADD_PAYMENT_INSTRUMENT_ACTION_TOKEN =
+            new WritableObjectPropertyKey<>();
+
     public AssistantCollectUserDataModel() {
         super(DELEGATE, WEB_CONTENTS, VISIBLE, SELECTED_SHIPPING_ADDRESS,
                 SELECTED_PAYMENT_INSTRUMENT, SELECTED_CONTACT_DETAILS, SELECTED_PHONE_NUMBER,
@@ -200,7 +203,8 @@
                 PRIVACY_NOTICE_TEXT, INFO_SECTION_TEXT, INFO_SECTION_TEXT_CENTER,
                 GENERIC_USER_INTERFACE_PREPENDED, GENERIC_USER_INTERFACE_APPENDED,
                 CONTACT_SUMMARY_DESCRIPTION_OPTIONS, CONTACT_FULL_DESCRIPTION_OPTIONS,
-                SHOULD_STORE_USER_DATA_CHANGES, USE_GMS_CORE_EDIT_DIALOGS, ACCOUNT_EMAIL);
+                SHOULD_STORE_USER_DATA_CHANGES, USE_GMS_CORE_EDIT_DIALOGS, ACCOUNT_EMAIL,
+                ADD_PAYMENT_INSTRUMENT_ACTION_TOKEN);
 
         /*
          * Set initial state for basic type properties (others are implicitly null).
@@ -581,4 +585,9 @@
     private void setAccountEmail(String accountEmail) {
         set(ACCOUNT_EMAIL, accountEmail);
     }
+
+    @CalledByNative
+    private void setAddPaymentInstrumentActionToken(byte[] actionToken) {
+        set(ADD_PAYMENT_INSTRUMENT_ACTION_TOKEN, actionToken);
+    }
 }
diff --git a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/user_data/AssistantCollectUserDataSection.java b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/user_data/AssistantCollectUserDataSection.java
index 768ebee..ba8878c 100644
--- a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/user_data/AssistantCollectUserDataSection.java
+++ b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/user_data/AssistantCollectUserDataSection.java
@@ -16,10 +16,10 @@
 
 import org.chromium.base.ApiCompatibilityUtils;
 import org.chromium.chrome.autofill_assistant.R;
-import org.chromium.chrome.browser.autofill_assistant.AssistantTagsForTesting;
-import org.chromium.chrome.browser.autofill_assistant.AssistantTextUtils;
-import org.chromium.chrome.browser.autofill_assistant.LayoutUtils;
 import org.chromium.components.autofill_assistant.AssistantOptionModel;
+import org.chromium.components.autofill_assistant.AssistantTagsForTesting;
+import org.chromium.components.autofill_assistant.AssistantTextUtils;
+import org.chromium.components.autofill_assistant.LayoutUtils;
 
 import java.util.ArrayList;
 import java.util.List;
diff --git a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/user_data/AssistantInfoSection.java b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/user_data/AssistantInfoSection.java
index 12b2a45..2b6b01d 100644
--- a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/user_data/AssistantInfoSection.java
+++ b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/user_data/AssistantInfoSection.java
@@ -16,7 +16,7 @@
 import org.chromium.base.ApiCompatibilityUtils;
 import org.chromium.base.Callback;
 import org.chromium.chrome.autofill_assistant.R;
-import org.chromium.chrome.browser.autofill_assistant.AssistantTextUtils;
+import org.chromium.components.autofill_assistant.AssistantTextUtils;
 
 /**
  * A section to display a text string.
diff --git a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/user_data/AssistantLoginSection.java b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/user_data/AssistantLoginSection.java
index 7fac4b9..24db2ea 100644
--- a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/user_data/AssistantLoginSection.java
+++ b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/user_data/AssistantLoginSection.java
@@ -4,7 +4,7 @@
 
 package org.chromium.chrome.browser.autofill_assistant.user_data;
 
-import static org.chromium.chrome.browser.autofill_assistant.AssistantAccessibilityUtils.setAccessibility;
+import static org.chromium.components.autofill_assistant.AssistantAccessibilityUtils.setAccessibility;
 
 import android.content.Context;
 import android.text.TextUtils;
diff --git a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/user_data/AssistantTermsSection.java b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/user_data/AssistantTermsSection.java
index 1998dce..172cfe46 100644
--- a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/user_data/AssistantTermsSection.java
+++ b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/user_data/AssistantTermsSection.java
@@ -16,9 +16,9 @@
 
 import org.chromium.base.ApiCompatibilityUtils;
 import org.chromium.chrome.autofill_assistant.R;
-import org.chromium.chrome.browser.autofill_assistant.AssistantTagsForTesting;
-import org.chromium.chrome.browser.autofill_assistant.AssistantTextUtils;
-import org.chromium.chrome.browser.autofill_assistant.LayoutUtils;
+import org.chromium.components.autofill_assistant.AssistantTagsForTesting;
+import org.chromium.components.autofill_assistant.AssistantTextUtils;
+import org.chromium.components.autofill_assistant.LayoutUtils;
 
 /**
  * The third party terms and conditions section of the Autofill Assistant payment request.
diff --git a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/user_data/AssistantVerticalExpander.java b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/user_data/AssistantVerticalExpander.java
index 7632da98..9fdab1f 100644
--- a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/user_data/AssistantVerticalExpander.java
+++ b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/user_data/AssistantVerticalExpander.java
@@ -4,7 +4,7 @@
 
 package org.chromium.chrome.browser.autofill_assistant.user_data;
 
-import static org.chromium.chrome.browser.autofill_assistant.AssistantTagsForTesting.VERTICAL_EXPANDER_CHEVRON;
+import static org.chromium.components.autofill_assistant.AssistantTagsForTesting.VERTICAL_EXPANDER_CHEVRON;
 
 import android.content.Context;
 import android.util.AttributeSet;
diff --git a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/user_data/additional_sections/AssistantAdditionalSectionContainer.java b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/user_data/additional_sections/AssistantAdditionalSectionContainer.java
index 10a637fe..49a5466 100644
--- a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/user_data/additional_sections/AssistantAdditionalSectionContainer.java
+++ b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/user_data/additional_sections/AssistantAdditionalSectionContainer.java
@@ -11,9 +11,9 @@
 import android.widget.Space;
 
 import org.chromium.chrome.autofill_assistant.R;
-import org.chromium.chrome.browser.autofill_assistant.LayoutUtils;
 import org.chromium.chrome.browser.autofill_assistant.user_data.AssistantCollectUserDataCoordinator;
 import org.chromium.chrome.browser.autofill_assistant.user_data.additional_sections.AssistantAdditionalSection.Delegate;
+import org.chromium.components.autofill_assistant.LayoutUtils;
 
 import java.util.ArrayList;
 import java.util.Collections;
diff --git a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/user_data/additional_sections/AssistantPopupListSection.java b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/user_data/additional_sections/AssistantPopupListSection.java
index 89c1329..f02053c9 100644
--- a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/user_data/additional_sections/AssistantPopupListSection.java
+++ b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/user_data/additional_sections/AssistantPopupListSection.java
@@ -15,10 +15,10 @@
 
 import org.chromium.chrome.autofill_assistant.R;
 import org.chromium.chrome.browser.autofill_assistant.AssistantChevronStyle;
-import org.chromium.chrome.browser.autofill_assistant.AssistantTextUtils;
-import org.chromium.chrome.browser.autofill_assistant.LayoutUtils;
 import org.chromium.chrome.browser.autofill_assistant.generic_ui.AssistantValue;
 import org.chromium.chrome.browser.autofill_assistant.user_data.AssistantVerticalExpander;
+import org.chromium.components.autofill_assistant.AssistantTextUtils;
+import org.chromium.components.autofill_assistant.LayoutUtils;
 import org.chromium.content.browser.input.PopupItemType;
 import org.chromium.content.browser.input.SelectPopupDialog;
 import org.chromium.content.browser.input.SelectPopupItem;
diff --git a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/user_data/additional_sections/AssistantStaticTextSection.java b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/user_data/additional_sections/AssistantStaticTextSection.java
index bf0ebd6..88f7892f 100644
--- a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/user_data/additional_sections/AssistantStaticTextSection.java
+++ b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/user_data/additional_sections/AssistantStaticTextSection.java
@@ -12,8 +12,8 @@
 import android.widget.TextView;
 
 import org.chromium.chrome.autofill_assistant.R;
-import org.chromium.chrome.browser.autofill_assistant.AssistantTextUtils;
-import org.chromium.chrome.browser.autofill_assistant.LayoutUtils;
+import org.chromium.components.autofill_assistant.AssistantTextUtils;
+import org.chromium.components.autofill_assistant.LayoutUtils;
 
 /** A section which displays a simple static text message. */
 public class AssistantStaticTextSection implements AssistantAdditionalSection {
diff --git a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/user_data/additional_sections/AssistantTextInputSection.java b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/user_data/additional_sections/AssistantTextInputSection.java
index efe506c..b667967e 100644
--- a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/user_data/additional_sections/AssistantTextInputSection.java
+++ b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/user_data/additional_sections/AssistantTextInputSection.java
@@ -24,10 +24,10 @@
 import org.chromium.chrome.autofill_assistant.R;
 import org.chromium.chrome.browser.autofill.prefeditor.EditorFieldModel;
 import org.chromium.chrome.browser.autofill.prefeditor.EditorTextField;
-import org.chromium.chrome.browser.autofill_assistant.AssistantTextUtils;
-import org.chromium.chrome.browser.autofill_assistant.LayoutUtils;
 import org.chromium.chrome.browser.autofill_assistant.generic_ui.AssistantValue;
 import org.chromium.chrome.browser.autofill_assistant.user_data.AssistantVerticalExpander;
+import org.chromium.components.autofill_assistant.AssistantTextUtils;
+import org.chromium.components.autofill_assistant.LayoutUtils;
 
 import java.util.ArrayList;
 import java.util.List;
diff --git a/chrome/android/features/autofill_assistant/javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantBottomsheetTest.java b/chrome/android/features/autofill_assistant/javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantBottomsheetTest.java
index d5800fbd..6ddbff0 100644
--- a/chrome/android/features/autofill_assistant/javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantBottomsheetTest.java
+++ b/chrome/android/features/autofill_assistant/javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantBottomsheetTest.java
@@ -22,7 +22,6 @@
 import static org.hamcrest.Matchers.is;
 import static org.hamcrest.Matchers.not;
 
-import static org.chromium.chrome.browser.autofill_assistant.AssistantTagsForTesting.RECYCLER_VIEW_TAG;
 import static org.chromium.chrome.browser.autofill_assistant.AutofillAssistantUiTestUtil.getAbsoluteBoundingRect;
 import static org.chromium.chrome.browser.autofill_assistant.AutofillAssistantUiTestUtil.startAutofillAssistant;
 import static org.chromium.chrome.browser.autofill_assistant.AutofillAssistantUiTestUtil.tapElement;
@@ -35,6 +34,7 @@
 import static org.chromium.chrome.browser.autofill_assistant.proto.ConfigureBottomSheetProto.ViewportResizing.NO_RESIZE;
 import static org.chromium.chrome.browser.autofill_assistant.proto.ConfigureBottomSheetProto.ViewportResizing.RESIZE_LAYOUT_VIEWPORT;
 import static org.chromium.chrome.browser.autofill_assistant.proto.ConfigureBottomSheetProto.ViewportResizing.RESIZE_VISUAL_VIEWPORT;
+import static org.chromium.components.autofill_assistant.AssistantTagsForTesting.RECYCLER_VIEW_TAG;
 
 import android.graphics.Rect;
 
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 a22b64d..646f11d1 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
@@ -106,6 +106,7 @@
 import org.chromium.chrome.browser.customtabs.CustomTabActivityTestRule;
 import org.chromium.chrome.browser.flags.ChromeSwitches;
 import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
+import org.chromium.components.autofill_assistant.AssistantTagsForTesting;
 import org.chromium.content_public.browser.WebContents;
 
 import java.util.ArrayList;
diff --git a/chrome/android/features/autofill_assistant/javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantCollectUserDataTestHelper.java b/chrome/android/features/autofill_assistant/javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantCollectUserDataTestHelper.java
index 71d42bb..45949f5 100644
--- a/chrome/android/features/autofill_assistant/javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantCollectUserDataTestHelper.java
+++ b/chrome/android/features/autofill_assistant/javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantCollectUserDataTestHelper.java
@@ -4,9 +4,9 @@
 
 package org.chromium.chrome.browser.autofill_assistant;
 
-import static org.chromium.chrome.browser.autofill_assistant.AssistantTagsForTesting.COLLECT_USER_DATA_CHOICE_LIST;
 import static org.chromium.chrome.browser.autofill_assistant.AutofillAssistantUiTestUtil.findViewsWithTag;
 import static org.chromium.chrome.browser.autofill_assistant.user_data.AssistantCollectUserDataCoordinator.DIVIDER_TAG;
+import static org.chromium.components.autofill_assistant.AssistantTagsForTesting.COLLECT_USER_DATA_CHOICE_LIST;
 
 import android.view.View;
 import android.widget.LinearLayout;
@@ -31,6 +31,7 @@
 import org.chromium.components.autofill_assistant.AssistantAutofillProfile;
 import org.chromium.components.autofill_assistant.AssistantOptionModel;
 import org.chromium.components.autofill_assistant.AssistantPaymentInstrument;
+import org.chromium.components.autofill_assistant.AssistantTagsForTesting;
 import org.chromium.content_public.browser.test.util.TestThreadUtils;
 
 import java.util.HashMap;
diff --git a/chrome/android/features/autofill_assistant/javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantCollectUserDataUiTest.java b/chrome/android/features/autofill_assistant/javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantCollectUserDataUiTest.java
index ea75787..5b3d3a5b 100644
--- a/chrome/android/features/autofill_assistant/javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantCollectUserDataUiTest.java
+++ b/chrome/android/features/autofill_assistant/javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantCollectUserDataUiTest.java
@@ -29,9 +29,9 @@
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertThat;
 
-import static org.chromium.chrome.browser.autofill_assistant.AssistantTagsForTesting.COLLECT_USER_DATA_CHOICE_LIST;
-import static org.chromium.chrome.browser.autofill_assistant.AssistantTagsForTesting.COLLECT_USER_DATA_TERMS_REQUIRE_REVIEW;
-import static org.chromium.chrome.browser.autofill_assistant.AssistantTagsForTesting.VERTICAL_EXPANDER_CHEVRON;
+import static org.chromium.components.autofill_assistant.AssistantTagsForTesting.COLLECT_USER_DATA_CHOICE_LIST;
+import static org.chromium.components.autofill_assistant.AssistantTagsForTesting.COLLECT_USER_DATA_TERMS_REQUIRE_REVIEW;
+import static org.chromium.components.autofill_assistant.AssistantTagsForTesting.VERTICAL_EXPANDER_CHEVRON;
 
 import android.support.test.InstrumentationRegistry;
 import android.view.View;
diff --git a/chrome/android/features/autofill_assistant/javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantHeaderUiTest.java b/chrome/android/features/autofill_assistant/javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantHeaderUiTest.java
index 1d16be3e..44bbc78e 100644
--- a/chrome/android/features/autofill_assistant/javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantHeaderUiTest.java
+++ b/chrome/android/features/autofill_assistant/javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantHeaderUiTest.java
@@ -53,6 +53,7 @@
 import org.chromium.chrome.browser.flags.ChromeSwitches;
 import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
 import org.chromium.components.autofill_assistant.AssistantDependencies;
+import org.chromium.components.autofill_assistant.AssistantTagsForTesting;
 import org.chromium.content_public.browser.test.util.TestThreadUtils;
 
 import java.util.ArrayList;
diff --git a/chrome/android/features/autofill_assistant/javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantProgressBarIntegrationTest.java b/chrome/android/features/autofill_assistant/javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantProgressBarIntegrationTest.java
index 63b80d94..41acbc3 100644
--- a/chrome/android/features/autofill_assistant/javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantProgressBarIntegrationTest.java
+++ b/chrome/android/features/autofill_assistant/javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantProgressBarIntegrationTest.java
@@ -48,6 +48,7 @@
 import org.chromium.chrome.browser.customtabs.CustomTabActivityTestRule;
 import org.chromium.chrome.browser.flags.ChromeSwitches;
 import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
+import org.chromium.components.autofill_assistant.AssistantTagsForTesting;
 import org.chromium.ui.widget.ChromeImageView;
 
 import java.util.ArrayList;
diff --git a/chrome/android/features/autofill_assistant/javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantTextUtilsTest.java b/chrome/android/features/autofill_assistant/javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantTextUtilsTest.java
index 7c52ef8..4d63531 100644
--- a/chrome/android/features/autofill_assistant/javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantTextUtilsTest.java
+++ b/chrome/android/features/autofill_assistant/javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantTextUtilsTest.java
@@ -36,6 +36,7 @@
 import org.chromium.chrome.browser.customtabs.CustomTabsTestUtils;
 import org.chromium.chrome.browser.flags.ChromeSwitches;
 import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
+import org.chromium.components.autofill_assistant.AssistantTextUtils;
 
 /** Tests for {@code AssistantTextUtils}. */
 @CommandLineFlags.Add({ChromeSwitches.DISABLE_FIRST_RUN_EXPERIENCE})
diff --git a/chrome/android/features/autofill_assistant/javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantTtsIntegrationTest.java b/chrome/android/features/autofill_assistant/javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantTtsIntegrationTest.java
index d54436d8..4824350 100644
--- a/chrome/android/features/autofill_assistant/javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantTtsIntegrationTest.java
+++ b/chrome/android/features/autofill_assistant/javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantTtsIntegrationTest.java
@@ -56,6 +56,7 @@
 import org.chromium.chrome.browser.customtabs.CustomTabActivityTestRule;
 import org.chromium.chrome.browser.flags.ChromeSwitches;
 import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
+import org.chromium.components.autofill_assistant.AssistantTagsForTesting;
 import org.chromium.content.browser.accessibility.BrowserAccessibilityState;
 import org.chromium.content_public.browser.test.util.TestThreadUtils;
 
diff --git a/chrome/android/features/autofill_assistant/javatests/src/org/chromium/chrome/browser/autofill_assistant/BottomSheetOnboardingCoordinatorTest.java b/chrome/android/features/autofill_assistant/javatests/src/org/chromium/chrome/browser/autofill_assistant/BottomSheetOnboardingCoordinatorTest.java
index 7704371..cea0b22 100644
--- a/chrome/android/features/autofill_assistant/javatests/src/org/chromium/chrome/browser/autofill_assistant/BottomSheetOnboardingCoordinatorTest.java
+++ b/chrome/android/features/autofill_assistant/javatests/src/org/chromium/chrome/browser/autofill_assistant/BottomSheetOnboardingCoordinatorTest.java
@@ -67,6 +67,7 @@
 import org.chromium.chrome.browser.customtabs.CustomTabsTestUtils;
 import org.chromium.chrome.browser.flags.ChromeSwitches;
 import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
+import org.chromium.components.autofill_assistant.AssistantBottomSheetContent;
 import org.chromium.components.autofill_assistant.AssistantStaticDependencies;
 import org.chromium.components.autofill_assistant.AutofillAssistantPreferencesUtil;
 import org.chromium.components.browser_ui.bottomsheet.BottomSheetController;
diff --git a/chrome/android/features/autofill_assistant/public/java/src/org/chromium/chrome/browser/autofill_assistant/AssistantEditorFactoryChrome.java b/chrome/android/features/autofill_assistant/public/java/src/org/chromium/chrome/browser/autofill_assistant/AssistantEditorFactoryChrome.java
index 3cd1977..ed0bd34 100644
--- a/chrome/android/features/autofill_assistant/public/java/src/org/chromium/chrome/browser/autofill_assistant/AssistantEditorFactoryChrome.java
+++ b/chrome/android/features/autofill_assistant/public/java/src/org/chromium/chrome/browser/autofill_assistant/AssistantEditorFactoryChrome.java
@@ -11,6 +11,7 @@
 import org.chromium.components.autofill_assistant.AssistantEditor.AssistantContactEditor;
 import org.chromium.components.autofill_assistant.AssistantEditor.AssistantPaymentInstrumentEditor;
 import org.chromium.components.autofill_assistant.AssistantEditorFactory;
+import org.chromium.components.autofill_assistant.AssistantPaymentInstrumentEditorGms;
 import org.chromium.content_public.browser.WebContents;
 import org.chromium.ui.base.WindowAndroid;
 
@@ -48,4 +49,11 @@
         return new AssistantPaymentInstrumentEditorAutofill(
                 webContents, activity, supportedCardNetworks, shouldStoreChanges);
     }
+
+    @Override
+    public AssistantPaymentInstrumentEditor createGmsPaymentInstrumentEditor(Activity activity,
+            WindowAndroid windowAndroid, String accountEmail, byte[] addInstrumentactionToken) {
+        return new AssistantPaymentInstrumentEditorGms(
+                activity, windowAndroid, accountEmail, addInstrumentactionToken);
+    }
 }
diff --git a/chrome/android/java/res/values-sw600dp/dimens.xml b/chrome/android/java/res/values-sw600dp/dimens.xml
index 544254a6..7b9a3826 100644
--- a/chrome/android/java/res/values-sw600dp/dimens.xml
+++ b/chrome/android/java/res/values-sw600dp/dimens.xml
@@ -39,7 +39,4 @@
 
     <!-- Preferences dimensions -->
     <dimen name="pref_spinner_padding_end">16dp</dimen>
-
-    <!-- Chrome Management dimensions -->
-    <dimen name="cm_padding">48dp</dimen>
 </resources>
diff --git a/chrome/android/java/res/values/dimens.xml b/chrome/android/java/res/values/dimens.xml
index 1761a23..d4d9ae2c 100644
--- a/chrome/android/java/res/values/dimens.xml
+++ b/chrome/android/java/res/values/dimens.xml
@@ -128,18 +128,6 @@
 
     <dimen name="fre_button_vertical_margin_small">16dp</dimen>
 
-    <!-- Chrome Management dimensions -->
-    <dimen name="cm_max_width">600dp</dimen>
-    <dimen name="cm_padding">32dp</dimen>
-    <dimen name="cm_logo_width">40dp</dimen>
-    <dimen name="cm_logo_height">36dp</dimen>
-    <dimen name="cm_title_margin_top">30dp</dimen>
-    <dimen name="cm_description_margin_top">24dp</dimen>
-    <dimen name="cm_learn_more_margin_top">22dp</dimen>
-    <dimen name="cm_browser_margin_top">42dp</dimen>
-    <dimen name="cm_browser_item_margin_top">20dp</dimen>
-    <dimen name="cm_browser_item_drawable_padding">18dp</dimen>
-
     <!-- Account Signin dimensions -->
     <!-- The Account Signin page appears in the First Run Experience (amongst other places), so uses
     a lot of the fre_* dimensions for consistency. -->
diff --git a/chrome/android/profiles/newest.txt b/chrome/android/profiles/newest.txt
index 43369b7..7340dab 100644
--- a/chrome/android/profiles/newest.txt
+++ b/chrome/android/profiles/newest.txt
@@ -1 +1 @@
-chromeos-chrome-amd64-100.0.4896.9_rc-r1-merged.afdo.bz2
+chromeos-chrome-amd64-100.0.4896.12_rc-r1-merged.afdo.bz2
diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn
index 151965a..ecd1ee3 100644
--- a/chrome/browser/BUILD.gn
+++ b/chrome/browser/BUILD.gn
@@ -923,13 +923,8 @@
     "optimization_guide/optimization_guide_web_contents_observer.h",
     "optimization_guide/page_content_annotations_service_factory.cc",
     "optimization_guide/page_content_annotations_service_factory.h",
-    "optimization_guide/prediction/prediction_manager.cc",
-    "optimization_guide/prediction/prediction_manager.h",
     "optimization_guide/prediction/prediction_model_download_client.cc",
     "optimization_guide/prediction/prediction_model_download_client.h",
-    "optimization_guide/prediction/prediction_model_download_manager.cc",
-    "optimization_guide/prediction/prediction_model_download_manager.h",
-    "optimization_guide/prediction/prediction_model_download_observer.h",
     "page_info/about_this_site_service_factory.cc",
     "page_info/about_this_site_service_factory.h",
     "page_info/chrome_about_this_site_service_client.cc",
@@ -2124,6 +2119,7 @@
     "//components/optimization_guide/content/browser",
     "//components/optimization_guide/core",
     "//components/optimization_guide/core:bloomfilter",
+    "//components/optimization_guide/core:prediction",
     "//components/optimization_guide/optimization_guide_internals/webui",
     "//components/os_crypt",
     "//components/page_load_metrics/browser",
@@ -2454,7 +2450,7 @@
       "//chrome/browser/ui/webui/chromeos/parent_access:mojo_bindings",
       "//chrome/browser/ui/webui/chromeos/vm:mojo_bindings",
       "//chrome/browser/ui/webui/settings/ash/os_apps_page/mojom",
-      "//chrome/browser/ui/webui/settings/ash/search:mojo_bindings",
+      "//chrome/browser/ui/webui/settings/chromeos/search:mojo_bindings",
       "//chrome/common/chromeos/extensions",
       "//chromeos/components/chromebox_for_meetings/buildflags",
       "//chromeos/components/local_search_service",
@@ -5187,6 +5183,8 @@
       "lacros/chrome_browser_main_extra_parts_lacros.h",
       "lacros/client_cert_store_lacros.cc",
       "lacros/client_cert_store_lacros.h",
+      "lacros/crosapi_pref_observer.cc",
+      "lacros/crosapi_pref_observer.h",
       "lacros/device_settings_lacros.cc",
       "lacros/device_settings_lacros.h",
       "lacros/download_controller_client_lacros.cc",
diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc
index 189acbd..0b2a8dd 100644
--- a/chrome/browser/about_flags.cc
+++ b/chrome/browser/about_flags.cc
@@ -1450,6 +1450,22 @@
      base::size(kNtpChromeCartModuleRBDAndCouponDiscount), nullptr},
 };
 
+// The following are consent v2 variations in the Chrome Cart module.
+const flags_ui::FeatureEntry::FeatureParam kDiscountConsentNtpStringChange[] = {
+    {ntp_features::kNtpChromeCartModuleDiscountConsentNtpVariationParam, "1"}};
+const flags_ui::FeatureEntry::FeatureParam kDiscountConsentNtpInline[] = {
+    {ntp_features::kNtpChromeCartModuleDiscountConsentNtpVariationParam, "2"}};
+const flags_ui::FeatureEntry::FeatureParam kDiscountConsentNtpDialog[] = {
+    {ntp_features::kNtpChromeCartModuleDiscountConsentNtpVariationParam, "3"}};
+const FeatureEntry::FeatureVariation kDiscountConsentV2Variations[] = {
+    {"Changing string", kDiscountConsentNtpStringChange,
+     base::size(kDiscountConsentNtpStringChange), nullptr},
+    {"Inline Consent", kDiscountConsentNtpInline,
+     base::size(kDiscountConsentNtpInline), nullptr},
+    {"Dialog Consent", kDiscountConsentNtpDialog,
+     base::size(kDiscountConsentNtpDialog), nullptr},
+};
+
 const FeatureEntry::FeatureParam kNtpRecipeTasksModuleFakeData[] = {
     {ntp_features::kNtpRecipeTasksModuleDataParam, "fake"}};
 const FeatureEntry::FeatureVariation kNtpRecipeTasksModuleVariations[] = {
@@ -2689,8 +2705,8 @@
     {"Comprehensive", kSnoopingProtectionRecall,
      base::size(kSnoopingProtectionRecall), nullptr}};
 
-const FeatureEntry::FeatureParam kQuickDim120s[] = {
-    {"QuickDim_quick_dim_ms", "120000"},
+const FeatureEntry::FeatureParam kQuickDim6s[] = {
+    {"QuickDim_quick_dim_ms", "6000"},
     {"QuickDim_filter_config_case", "2"},
     {"QuickDim_positive_count_threshold", "1"},
     {"QuickDim_negative_count_threshold", "2"},
@@ -2699,8 +2715,31 @@
     {"QuickDim_negative_score_threshold", "0"},
 };
 
-const FeatureEntry::FeatureParam kQuickDim45s[] = {
+const FeatureEntry::FeatureParam kQuickDim6sQuickLock66s[] = {
+    {"QuickDim_quick_dim_ms", "6000"},
+    {"QuickDim_quick_lock_ms", "66000"},
+    {"QuickDim_filter_config_case", "2"},
+    {"QuickDim_positive_count_threshold", "1"},
+    {"QuickDim_negative_count_threshold", "2"},
+    {"QuickDim_uncertain_count_threshold", "2"},
+    {"QuickDim_positive_score_threshold", "0"},
+    {"QuickDim_negative_score_threshold", "0"},
+};
+
+const FeatureEntry::FeatureParam kQuickDim6sQuickLock126s[] = {
+    {"QuickDim_quick_dim_ms", "6000"},
+    {"QuickDim_quick_lock_ms", "126000"},
+    {"QuickDim_filter_config_case", "2"},
+    {"QuickDim_positive_count_threshold", "1"},
+    {"QuickDim_negative_count_threshold", "2"},
+    {"QuickDim_uncertain_count_threshold", "2"},
+    {"QuickDim_positive_score_threshold", "0"},
+    {"QuickDim_negative_score_threshold", "0"},
+};
+
+const FeatureEntry::FeatureParam kQuickDim45sQuickLock105s[] = {
     {"QuickDim_quick_dim_ms", "45000"},
+    {"QuickDim_quick_lock_ms", "105000"},
     {"QuickDim_filter_config_case", "2"},
     {"QuickDim_positive_count_threshold", "1"},
     {"QuickDim_negative_count_threshold", "2"},
@@ -2709,51 +2748,9 @@
     {"QuickDim_negative_score_threshold", "0"},
 };
 
-const FeatureEntry::FeatureParam kQuickDim10s[] = {
-    {"QuickDim_quick_dim_ms", "10000"},
-    {"QuickDim_filter_config_case", "2"},
-    {"QuickDim_positive_count_threshold", "1"},
-    {"QuickDim_negative_count_threshold", "2"},
-    {"QuickDim_uncertain_count_threshold", "2"},
-    {"QuickDim_positive_score_threshold", "0"},
-    {"QuickDim_negative_score_threshold", "0"},
-};
-
-const FeatureEntry::FeatureParam kQuickDimInstantly[] = {
-    {"QuickDim_quick_dim_ms", "1000"},
-    {"QuickDim_filter_config_case", "2"},
-    {"QuickDim_positive_count_threshold", "1"},
-    {"QuickDim_negative_count_threshold", "1"},
-    {"QuickDim_uncertain_count_threshold", "2"},
-    {"QuickDim_positive_score_threshold", "0"},
-    {"QuickDim_negative_score_threshold", "0"},
-};
-
-const FeatureEntry::FeatureParam kQuickDim10sQuickLock40s[] = {
-    {"QuickDim_quick_dim_ms", "10000"},
-    {"QuickDim_quick_lock_ms", "40000"},
-    {"QuickDim_filter_config_case", "2"},
-    {"QuickDim_positive_count_threshold", "1"},
-    {"QuickDim_negative_count_threshold", "2"},
-    {"QuickDim_uncertain_count_threshold", "2"},
-    {"QuickDim_positive_score_threshold", "0"},
-    {"QuickDim_negative_score_threshold", "0"},
-};
-
-const FeatureEntry::FeatureParam kQuickDim10sQuickLock70s[] = {
-    {"QuickDim_quick_dim_ms", "10000"},
-    {"QuickDim_quick_lock_ms", "70000"},
-    {"QuickDim_filter_config_case", "2"},
-    {"QuickDim_positive_count_threshold", "1"},
-    {"QuickDim_negative_count_threshold", "2"},
-    {"QuickDim_uncertain_count_threshold", "2"},
-    {"QuickDim_positive_score_threshold", "0"},
-    {"QuickDim_negative_score_threshold", "0"},
-};
-
-const FeatureEntry::FeatureParam kQuickDim45sQuickLock135s[] = {
+const FeatureEntry::FeatureParam kQuickDim45sQuickLock165s[] = {
     {"QuickDim_quick_dim_ms", "45000"},
-    {"QuickDim_quick_lock_ms", "135000"},
+    {"QuickDim_quick_lock_ms", "165000"},
     {"QuickDim_filter_config_case", "2"},
     {"QuickDim_positive_count_threshold", "1"},
     {"QuickDim_negative_count_threshold", "2"},
@@ -2774,19 +2771,18 @@
 };
 
 const FeatureEntry::FeatureVariation kQuickDimVariations[] = {
-    {"QuickDim120s", kQuickDim120s, base::size(kQuickDim120s), nullptr},
-    {"QuickDim45s", kQuickDim45s, base::size(kQuickDim45s), nullptr},
-    {"QuickDim10s", kQuickDim10s, base::size(kQuickDim10s), nullptr},
-    {"QuickDimInstantly", kQuickDimInstantly, base::size(kQuickDimInstantly),
-     nullptr},
-    {"kQuickDim10sQuickLock40s", kQuickDim10sQuickLock40s,
-     base::size(kQuickDim10sQuickLock40s), nullptr},
-    {"kQuickDim10sQuickLock70s", kQuickDim10sQuickLock70s,
-     base::size(kQuickDim10sQuickLock70s), nullptr},
-    {"kQuickDim45sQuickLock135s", kQuickDim45sQuickLock135s,
-     base::size(kQuickDim45sQuickLock135s), nullptr},
-    {"kQuickDim120sQuickLock240s", kQuickDim120sQuickLock240s,
-     base::size(kQuickDim120sQuickLock240s), nullptr}};
+    {"Dim6sLock66s", kQuickDim6sQuickLock66s,
+     base::size(kQuickDim6sQuickLock66s), nullptr},
+    {"Dim6sLock126s", kQuickDim6sQuickLock126s,
+     base::size(kQuickDim6sQuickLock126s), nullptr},
+    {"Dim45sLock105s", kQuickDim45sQuickLock105s,
+     base::size(kQuickDim45sQuickLock105s), nullptr},
+    {"Dim45sLock165s", kQuickDim45sQuickLock165s,
+     base::size(kQuickDim45sQuickLock165s), nullptr},
+    {"Dim120sLock240s", kQuickDim120sQuickLock240s,
+     base::size(kQuickDim120sQuickLock240s), nullptr},
+    {"Dim6sNoLock", kQuickDim6s, base::size(kQuickDim6s), nullptr},
+};
 
 #endif  // BUILDFLAG(IS_CHROMEOS_ASH)
 
@@ -8105,7 +8101,9 @@
 #if !BUILDFLAG(IS_ANDROID)
     {"enable-discount-consent-v2", flag_descriptions::kDiscountConsentV2Name,
      flag_descriptions::kDiscountConsentV2Description, kOsDesktop,
-     FEATURE_VALUE_TYPE(commerce::kDiscountConsentV2)},
+     FEATURE_WITH_PARAMS_VALUE_TYPE(commerce::kDiscountConsentV2,
+                                    kDiscountConsentV2Variations,
+                                    "DiscountConsentV2")},
 #endif
 
     {"autofill-enable-unmask-card-request-set-instrument-id",
diff --git a/chrome/browser/android/autofill_assistant/ui_controller_android.cc b/chrome/browser/android/autofill_assistant/ui_controller_android.cc
index 075975d9..3a2f42b 100644
--- a/chrome/browser/android/autofill_assistant/ui_controller_android.cc
+++ b/chrome/browser/android/autofill_assistant/ui_controller_android.cc
@@ -65,6 +65,7 @@
 using ::base::android::ScopedJavaGlobalRef;
 using ::base::android::ScopedJavaLocalRef;
 using ::base::android::ToJavaArrayOfStrings;
+using ::base::android::ToJavaByteArray;
 
 namespace autofill_assistant {
 
@@ -1344,6 +1345,14 @@
             ? collect_user_data_appended_generic_ui_controller_->GetRootView()
             : nullptr);
   }
+  if (collect_user_data_options->add_payment_instrument_action_token
+          .has_value()) {
+    Java_AssistantCollectUserDataModel_setAddPaymentInstrumentActionToken(
+        env, jmodel,
+        ToJavaByteArray(
+            env,
+            *collect_user_data_options->add_payment_instrument_action_token));
+  }
 
   Java_AssistantCollectUserDataModel_setVisible(env, jmodel, true);
 }
diff --git a/chrome/browser/apps/app_service/app_icon/app_icon_factory.h b/chrome/browser/apps/app_service/app_icon/app_icon_factory.h
index b00f48f..f33b6df3 100644
--- a/chrome/browser/apps/app_service/app_icon/app_icon_factory.h
+++ b/chrome/browser/apps/app_service/app_icon/app_icon_factory.h
@@ -127,8 +127,8 @@
     const std::map<SquareSizePx, SkBitmap>& icon_bitmaps,
     int size_hint_in_dip);
 
-// Modifies |iv| to apply icon post-processing effects like badging and
-// desaturation to gray.
+// Modifies |iv| to apply icon post-processing effects (like badging and
+// desaturation to gray) to an uncompressed icon.
 void ApplyIconEffects(IconEffects icon_effects,
                       int size_hint_in_dip,
                       IconValuePtr iv,
diff --git a/chrome/browser/apps/app_service/metrics/app_platform_metrics.cc b/chrome/browser/apps/app_service/metrics/app_platform_metrics.cc
index e98b633..9c8316f 100644
--- a/chrome/browser/apps/app_service/metrics/app_platform_metrics.cc
+++ b/chrome/browser/apps/app_service/metrics/app_platform_metrics.cc
@@ -234,22 +234,6 @@
 constexpr char kAppLaunchPerAppTypeV2HistogramName[] =
     "Apps.AppLaunchPerAppTypeV2";
 
-constexpr char kArcHistogramName[] = "Arc";
-constexpr char kBuiltInHistogramName[] = "BuiltIn";
-constexpr char kCrostiniHistogramName[] = "Crostini";
-constexpr char kChromeAppHistogramName[] = "ChromeApp";
-constexpr char kWebAppHistogramName[] = "WebApp";
-constexpr char kMacOsHistogramName[] = "MacOs";
-constexpr char kPluginVmHistogramName[] = "PluginVm";
-constexpr char kStandaloneBrowserHistogramName[] = "StandaloneBrowser";
-constexpr char kRemoteHistogramName[] = "RemoteApp";
-constexpr char kBorealisHistogramName[] = "Borealis";
-constexpr char kSystemWebAppHistogramName[] = "SystemWebApp";
-constexpr char kChromeBrowserHistogramName[] = "ChromeBrowser";
-constexpr char kStandaloneBrowserChromeAppHistogramName[] =
-    "StandaloneBrowserChromeApp";
-constexpr char kExtensionHistogramName[] = "Extension";
-
 constexpr char kChromeAppTabHistogramName[] = "ChromeAppTab";
 constexpr char kChromeAppWindowHistogramName[] = "ChromeAppWindow";
 constexpr char kWebAppTabHistogramName[] = "WebAppTab";
@@ -259,41 +243,6 @@
 constexpr char kUsageTimeAppTypeKey[] = "app_type";
 constexpr char kUsageTimeDurationKey[] = "time";
 
-std::string GetAppTypeHistogramName(apps::AppTypeName app_type_name) {
-  switch (app_type_name) {
-    case apps::AppTypeName::kUnknown:
-      return std::string();
-    case apps::AppTypeName::kArc:
-      return kArcHistogramName;
-    case apps::AppTypeName::kBuiltIn:
-      return kBuiltInHistogramName;
-    case apps::AppTypeName::kCrostini:
-      return kCrostiniHistogramName;
-    case apps::AppTypeName::kChromeApp:
-      return kChromeAppHistogramName;
-    case apps::AppTypeName::kWeb:
-      return kWebAppHistogramName;
-    case apps::AppTypeName::kMacOs:
-      return kMacOsHistogramName;
-    case apps::AppTypeName::kPluginVm:
-      return kPluginVmHistogramName;
-    case apps::AppTypeName::kStandaloneBrowser:
-      return kStandaloneBrowserHistogramName;
-    case apps::AppTypeName::kRemote:
-      return kRemoteHistogramName;
-    case apps::AppTypeName::kBorealis:
-      return kBorealisHistogramName;
-    case apps::AppTypeName::kSystemWeb:
-      return kSystemWebAppHistogramName;
-    case apps::AppTypeName::kChromeBrowser:
-      return kChromeBrowserHistogramName;
-    case apps::AppTypeName::kStandaloneBrowserChromeApp:
-      return kStandaloneBrowserChromeAppHistogramName;
-    case apps::AppTypeName::kExtension:
-      return kExtensionHistogramName;
-  }
-}
-
 std::string GetAppTypeHistogramNameV2(apps::AppTypeNameV2 app_type_name) {
   switch (app_type_name) {
     case apps::AppTypeNameV2::kUnknown:
@@ -359,6 +308,35 @@
   }
 }
 
+AppPlatformMetrics::UsageTime::UsageTime(const base::Value& value) {
+  const base::Value::Dict* data_dict = value.GetIfDict();
+  if (!data_dict) {
+    return;
+  }
+
+  const std::string* app_id_value = data_dict->FindString(kUsageTimeAppIdKey);
+  if (!app_id_value) {
+    return;
+  }
+
+  const std::string* app_type_value =
+      data_dict->FindString(kUsageTimeAppTypeKey);
+  if (!app_type_value) {
+    return;
+  }
+
+  absl::optional<base::TimeDelta> running_time_value =
+      base::ValueToTimeDelta(data_dict->Find(kUsageTimeDurationKey));
+  if (!running_time_value.has_value() || running_time_value.value().is_zero()) {
+    return;
+  }
+
+  app_id = *app_id_value;
+  app_type_name = GetAppTypeNameFromString(*app_type_value);
+  running_time = running_time_value.value();
+  window_is_closed = true;
+}
+
 base::Value AppPlatformMetrics::UsageTime::ConvertToValue() const {
   base::Value usage_time_dict(base::Value::Type::DICTIONARY);
   usage_time_dict.SetStringKey(kUsageTimeAppIdKey, app_id);
@@ -378,6 +356,7 @@
   apps::InstanceRegistry::Observer::Observe(&instance_registry);
   user_type_by_device_type_ = GetUserTypeByDeviceTypeMetrics();
   InitRunningDuration();
+  LoadAppsUsageTimeUkmFromPref();
 }
 
 AppPlatformMetrics::~AppPlatformMetrics() {
@@ -605,16 +584,18 @@
 }
 
 void AppPlatformMetrics::OnFiveMinutes() {
+  // If there is app usage time loaded from the user pref for previous login,
+  // record the AppKM.
+  if (!usage_times_from_pref_.empty()) {
+    RecordAppsUsageTimeUkmFromPref();
+    usage_times_from_pref_.clear();
+  }
   RecordAppsUsageTime();
   SaveUsageTime();
 }
 
 void AppPlatformMetrics::OnTwoHours() {
-  // TODO(crbug.com/1299978): Record the app usage time AppKM.
-
-  DictionaryPrefUpdate usage_time_update(profile_->GetPrefs(), kAppUsageTime);
-  usage_time_update->GetDict().clear();
-  usage_time_per_two_hours_.clear();
+  RecordAppsUsageTimeUkm();
 }
 
 void AppPlatformMetrics::RecordAppLaunchUkm(
@@ -847,11 +828,6 @@
     const base::UnguessableToken& instance_id,
     apps::InstanceState state) {
   bool is_close = state & apps::InstanceState::kDestroyed;
-  auto usage_time_it = usage_time_per_five_minutes_.find(instance_id);
-  if (is_close && usage_time_it != usage_time_per_five_minutes_.end()) {
-    usage_time_it->second.window_is_closed = true;
-  }
-
   auto two_hours_it = usage_time_per_two_hours_.find(instance_id);
   if (is_close && two_hours_it != usage_time_per_two_hours_.end()) {
     two_hours_it->second.window_is_closed = true;
@@ -874,20 +850,6 @@
   app_type_running_time_per_five_minutes_[app_type_name] += running_time;
   app_type_v2_running_time_per_five_minutes_[app_type_name_v2] += running_time;
 
-  // TODO(crbug.com/1299978): Remove `usage_time_per_five_minutes_` related
-  // code.
-  if (usage_time_it == usage_time_per_five_minutes_.end()) {
-    auto source_id = GetSourceId(profile_, app_id);
-    if (source_id != ukm::kInvalidSourceId) {
-      usage_time_per_five_minutes_[it->first].source_id = source_id;
-      usage_time_it = usage_time_per_five_minutes_.find(it->first);
-    }
-  }
-  if (usage_time_it != usage_time_per_five_minutes_.end()) {
-    usage_time_it->second.app_type_name = app_type_name;
-    usage_time_it->second.running_time += running_time;
-  }
-
   UpdateUsageTime(instance_id, app_id, app_type_name, running_time);
 
   running_start_time_.erase(it);
@@ -1020,20 +982,6 @@
         running_time;
     app_type_v2_running_time_per_five_minutes_[it.second.app_type_name_v2] +=
         running_time;
-
-    auto usage_time_it = usage_time_per_five_minutes_.find(it.first);
-    if (usage_time_it == usage_time_per_five_minutes_.end()) {
-      auto source_id = GetSourceId(profile_, it.second.app_id);
-      if (source_id != ukm::kInvalidSourceId) {
-        usage_time_per_five_minutes_[it.first].source_id = source_id;
-        usage_time_it = usage_time_per_five_minutes_.find(it.first);
-      }
-    }
-    if (usage_time_it != usage_time_per_five_minutes_.end()) {
-      usage_time_it->second.app_type_name = it.second.app_type_name;
-      usage_time_it->second.running_time += running_time;
-    }
-
     UpdateUsageTime(it.first, it.second.app_id, it.second.app_type_name,
                     running_time);
     it.second.start_time = base::TimeTicks::Now();
@@ -1053,8 +1001,6 @@
 
   app_type_running_time_per_five_minutes_.clear();
   app_type_v2_running_time_per_five_minutes_.clear();
-
-  RecordAppsUsageTimeUkm();
 }
 
 void AppPlatformMetrics::RecordAppsUsageTimeUkm() {
@@ -1063,7 +1009,7 @@
   }
 
   std::vector<base::UnguessableToken> closed_instance_ids;
-  for (auto& it : usage_time_per_five_minutes_) {
+  for (auto& it : usage_time_per_two_hours_) {
     apps::AppTypeName app_type_name = it.second.app_type_name;
     ukm::SourceId source_id = it.second.source_id;
     DCHECK_NE(source_id, ukm::kInvalidSourceId);
@@ -1082,9 +1028,17 @@
     }
   }
 
+  // `usage_time_per_two_hours_` can't be cleared to reuse the source id for
+  // open windows. So only closed window records can be deleted from
+  // `usage_time_per_two_hours_`.
   for (const auto& instance_id : closed_instance_ids) {
-    usage_time_per_five_minutes_.erase(instance_id);
+    usage_time_per_two_hours_.erase(instance_id);
   }
+
+  // The app usage time AppKMs have been recorded, so clear the saved usage time
+  // in the user pref.
+  DictionaryPrefUpdate usage_time_update(profile_->GetPrefs(), kAppUsageTime);
+  usage_time_update->GetDict().clear();
 }
 
 void AppPlatformMetrics::RecordAppsInstallUkm(const apps::AppUpdate& update,
@@ -1115,7 +1069,9 @@
     const base::TimeDelta& running_time) {
   auto usage_time_it = usage_time_per_two_hours_.find(instance_id);
   if (usage_time_it == usage_time_per_two_hours_.end()) {
-    if (ShouldRecordUkmForAppTypeName(GetAppType(profile_, app_id))) {
+    auto source_id = GetSourceId(profile_, app_id);
+    if (source_id != ukm::kInvalidSourceId) {
+      usage_time_per_two_hours_[instance_id].source_id = source_id;
       usage_time_per_two_hours_[instance_id].app_id = app_id;
       usage_time_it = usage_time_per_two_hours_.find(instance_id);
     }
@@ -1128,9 +1084,44 @@
 
 void AppPlatformMetrics::SaveUsageTime() {
   DictionaryPrefUpdate usage_time_update(profile_->GetPrefs(), kAppUsageTime);
+  usage_time_update->GetDict().clear();
   for (auto it : usage_time_per_two_hours_) {
     usage_time_update->SetPath(it.first.ToString(), it.second.ConvertToValue());
   }
 }
 
+void AppPlatformMetrics::LoadAppsUsageTimeUkmFromPref() {
+  DictionaryPrefUpdate usage_time_update(profile_->GetPrefs(), kAppUsageTime);
+  if (!usage_time_update->is_dict()) {
+    return;
+  }
+
+  for (auto it : usage_time_update->GetDict()) {
+    std::unique_ptr<UsageTime> usage_time =
+        std::make_unique<UsageTime>(it.second);
+    if (!usage_time->running_time.is_zero()) {
+      usage_times_from_pref_.push_back(std::move(usage_time));
+    }
+  }
+}
+
+void AppPlatformMetrics::RecordAppsUsageTimeUkmFromPref() {
+  if (!ShouldRecordUkm(profile_) || usage_times_from_pref_.empty()) {
+    return;
+  }
+
+  for (auto& it : usage_times_from_pref_) {
+    auto source_id = GetSourceId(profile_, it->app_id);
+    if (source_id == ukm::kInvalidSourceId) {
+      continue;
+    }
+    ukm::builders::ChromeOSApp_UsageTime builder(source_id);
+    builder.SetAppType((int)it->app_type_name)
+        .SetDuration(it->running_time.InMilliseconds())
+        .SetUserDeviceMatrix(user_type_by_device_type_)
+        .Record(ukm::UkmRecorder::Get());
+    RemoveSourceId(source_id);
+  }
+}
+
 }  // namespace apps
diff --git a/chrome/browser/apps/app_service/metrics/app_platform_metrics.h b/chrome/browser/apps/app_service/metrics/app_platform_metrics.h
index d7564f36..cdd75ff 100644
--- a/chrome/browser/apps/app_service/metrics/app_platform_metrics.h
+++ b/chrome/browser/apps/app_service/metrics/app_platform_metrics.h
@@ -10,6 +10,7 @@
 #include <string>
 
 #include "base/time/time.h"
+#include "base/unguessable_token.h"
 #include "chrome/browser/apps/app_service/metrics/app_platform_metrics_utils.h"
 #include "chrome/browser/apps/app_service/metrics/browser_to_tab_list.h"
 #include "components/services/app_service/public/cpp/app_registry_cache.h"
@@ -41,19 +42,6 @@
 extern const char kAppLaunchPerAppTypeHistogramName[];
 extern const char kAppLaunchPerAppTypeV2HistogramName[];
 
-extern const char kArcHistogramName[];
-extern const char kBuiltInHistogramName[];
-extern const char kCrostiniHistogramName[];
-extern const char kChromeAppHistogramName[];
-extern const char kWebAppHistogramName[];
-extern const char kMacOsHistogramName[];
-extern const char kPluginVmHistogramName[];
-extern const char kStandaloneBrowserHistogramName[];
-extern const char kRemoteHistogramName[];
-extern const char kBorealisHistogramName[];
-extern const char kSystemWebAppHistogramName[];
-extern const char kChromeBrowserHistogramName[];
-
 extern const char kChromeAppTabHistogramName[];
 extern const char kChromeAppWindowHistogramName[];
 extern const char kWebAppTabHistogramName[];
@@ -63,7 +51,6 @@
 extern const char kUsageTimeAppTypeKey[];
 extern const char kUsageTimeDurationKey[];
 
-std::string GetAppTypeHistogramName(apps::AppTypeName app_type_name);
 std::string GetAppTypeHistogramNameV2(apps::AppTypeNameV2 app_type_name);
 
 const std::set<apps::AppTypeName>& GetAppTypeNameSet();
@@ -156,6 +143,8 @@
   };
 
   struct UsageTime {
+    UsageTime() = default;
+    explicit UsageTime(const base::Value& value);
     base::TimeDelta running_time;
     ukm::SourceId source_id = ukm::kInvalidSourceId;
     std::string app_id;
@@ -224,13 +213,25 @@
   void RecordAppsInstallUkm(const apps::AppUpdate& update,
                             InstallTime install_time);
 
+  // Updates `usage_time_per_two_hours_` each 5 minutes or when the app window
+  // is inactivated.
   void UpdateUsageTime(const base::UnguessableToken& instance_id,
                        const std::string& app_id,
                        AppTypeName app_type_name,
                        const base::TimeDelta& running_time);
 
+  // Saves the app window usage time in `usage_time_per_two_hours_` to the user
+  // pref each 5 minutes.
   void SaveUsageTime();
 
+  // Reads the app platform metrics saved in the user pref to
+  // `usage_times_from_pref_`.
+  void LoadAppsUsageTimeUkmFromPref();
+
+  // Records the app usage time UKM based on the usage time saved in
+  // `usage_times_from_pref_`.
+  void RecordAppsUsageTimeUkmFromPref();
+
   Profile* const profile_ = nullptr;
 
   AppRegistryCache& app_registry_cache_;
@@ -251,8 +252,7 @@
   std::map<AppTypeName, int> activated_count_;
 
   // |start_time_per_five_minutes_|, |app_type_running_time_per_five_minutes_|,
-  // |app_type_v2_running_time_per_five_minutes_|, and
-  // |usage_time_per_five_minutes_| are used for accumulating app
+  // |app_type_v2_running_time_per_five_minutes_| are used for accumulating app
   // running duration per 5 minutes interval.
   std::map<const base::UnguessableToken, RunningStartTime>
       start_time_per_five_minutes_;
@@ -261,12 +261,11 @@
   std::map<AppTypeNameV2, base::TimeDelta>
       app_type_v2_running_time_per_five_minutes_;
 
-  // TODO(crbug.com/1299978): Remove `usage_time_per_five_minutes_`.
-  std::map<const base::UnguessableToken, UsageTime>
-      usage_time_per_five_minutes_;
-
-  // Record the app window running duration for the app usage AppKM.
+  // Records the app window running duration for the app usage AppKM.
   std::map<const base::UnguessableToken, UsageTime> usage_time_per_two_hours_;
+
+  // The app usage time loaded from the user pref during the init phase.
+  std::vector<std::unique_ptr<UsageTime>> usage_times_from_pref_;
 };
 
 }  // namespace apps
diff --git a/chrome/browser/apps/app_service/metrics/app_platform_metrics_service_unittest.cc b/chrome/browser/apps/app_service/metrics/app_platform_metrics_service_unittest.cc
index 4ad47d9b..8a090aa 100644
--- a/chrome/browser/apps/app_service/metrics/app_platform_metrics_service_unittest.cc
+++ b/chrome/browser/apps/app_service/metrics/app_platform_metrics_service_unittest.cc
@@ -211,6 +211,18 @@
     sync_service_->SetFirstSetupComplete(true);
   }
 
+  void ResetAppPlatformMetricsService() {
+    app_platform_metrics_service_.reset();
+    app_platform_metrics_service_ =
+        std::make_unique<AppPlatformMetricsService>(testing_profile_.get());
+
+    app_platform_metrics_service_->Start(
+        apps::AppServiceProxyFactory::GetForProfile(testing_profile_.get())
+            ->AppRegistryCache(),
+        apps::AppServiceProxyFactory::GetForProfile(testing_profile_.get())
+            ->InstanceRegistry());
+  }
+
   void InstallApps() {
     auto* proxy =
         apps::AppServiceProxyFactory::GetForProfile(testing_profile_.get());
@@ -659,6 +671,12 @@
     ASSERT_EQ(usage_time, duration);
   }
 
+  void VerifyNoAppUsageTimeUkm() {
+    auto entries =
+        test_ukm_recorder()->GetEntriesByName("ChromeOSApp.UsageTime");
+    ASSERT_EQ(0U, entries.size());
+  }
+
   void VerifyInstalledAppsUkm(const std::string& app_info,
                               AppTypeName app_type_name,
                               apps::mojom::InstallReason install_reason,
@@ -1157,8 +1175,7 @@
   VerifyAppUsageTimeHistogram(base::Minutes(3),
                               /*expected_count=*/1,
                               AppTypeName::kChromeBrowser);
-  VerifyAppUsageTimeUkm(app_constants::kChromeAppId, /*duration=*/180000,
-                        AppTypeName::kChromeBrowser);
+  VerifyNoAppUsageTimeUkm();
 
   task_environment_.FastForwardBy(base::Minutes(15));
   VerifyAppUsageTimeCountHistogram(/*expected_count=*/2, AppTypeName::kArc);
@@ -1170,6 +1187,17 @@
   VerifyAppUsageTimeHistogram(base::Minutes(5),
                               /*expected_count=*/3,
                               AppTypeName::kChromeBrowser);
+  VerifyNoAppUsageTimeUkm();
+
+  // Set the browser window inactive.
+  ModifyInstance(app_constants::kChromeAppId,
+                 browser->window()->GetNativeWindow(), kInactiveInstanceState);
+
+  // Set time passed 2 hours to record the usage time AppKM with duration = 18
+  // minutes.
+  task_environment_.FastForwardBy(base::Minutes(95));
+  VerifyAppUsageTimeUkm(app_constants::kChromeAppId, /*duration=*/1080000,
+                        AppTypeName::kChromeBrowser);
 }
 
 TEST_F(AppPlatformMetricsServiceTest, UsageTimeUkm) {
@@ -1187,20 +1215,96 @@
   sync_service()->SetDisableReasons(
       syncer::SyncService::DISABLE_REASON_ENTERPRISE_POLICY);
 
-  task_environment_.FastForwardBy(base::Minutes(5));
+  task_environment_.FastForwardBy(base::Hours(2));
 
-  // Verify UKM is not reported.
-  const auto entries =
-      test_ukm_recorder()->GetEntriesByName("ChromeOSApp.UsageTime");
-  ASSERT_EQ(0U, entries.size());
+  VerifyNoAppUsageTimeUkm();
 
   // Set sync is allowed by setting an empty disable reason set.
   sync_service()->SetDisableReasons(syncer::SyncService::DisableReasonSet());
-  task_environment_.FastForwardBy(base::Minutes(5));
-  VerifyAppUsageTimeUkm(app_constants::kChromeAppId, /*duration=*/600000,
+
+  task_environment_.FastForwardBy(base::Hours(1));
+  ModifyInstance(app_constants::kChromeAppId,
+                 browser->window()->GetNativeWindow(), kInactiveInstanceState);
+
+  task_environment_.FastForwardBy(base::Hours(1));
+  VerifyAppUsageTimeUkm(app_constants::kChromeAppId, /*duration=*/10800000,
                         AppTypeName::kChromeBrowser);
 }
 
+TEST_F(AppPlatformMetricsServiceTest, UsageTimeUkmReportAfterReboot) {
+  // Create a browser window.
+  InstallOneApp(app_constants::kChromeAppId, AppType::kChromeApp, "Chrome",
+                Readiness::kReady, InstallSource::kSystem);
+  std::unique_ptr<Browser> browser = CreateBrowserWithAuraWindow1();
+  EXPECT_EQ(1U, BrowserList::GetInstance()->size());
+
+  // Set the browser window active.
+  ModifyInstance(app_constants::kChromeAppId,
+                 browser->window()->GetNativeWindow(), kActiveInstanceState);
+
+  task_environment_.FastForwardBy(base::Minutes(30));
+
+  // Create a web app tab.
+  const std::string web_app_id = "w";
+  const GURL url = GURL("https://foo.com");
+  auto web_app_window =
+      CreateWebAppWindow(browser->window()->GetNativeWindow());
+
+  // Set the web app tab as activated.
+  ModifyWebAppInstance(web_app_id, web_app_window.get(), kActiveInstanceState);
+
+  task_environment_.FastForwardBy(base::Minutes(20));
+  ModifyInstance(app_constants::kChromeAppId,
+                 browser->window()->GetNativeWindow(), kInactiveInstanceState);
+  ModifyWebAppInstance(web_app_id, web_app_window.get(),
+                       kInactiveInstanceState);
+
+  VerifyNoAppUsageTimeUkm();
+
+  // Reset PlatformMetricsService to simulate the system reboot, and verify
+  // AppKM is restored from the user pref and reported after 5 minutes after
+  // reboot.
+  ResetAppPlatformMetricsService();
+  VerifyNoAppUsageTimeUkm();
+
+  task_environment_.FastForwardBy(base::Minutes(5));
+  VerifyAppUsageTimeUkm(app_constants::kChromeAppId, /*duration=*/1800000,
+                        AppTypeName::kChromeBrowser);
+  VerifyAppUsageTimeUkm(url, /*duration=*/1200000, AppTypeName::kChromeBrowser);
+
+  // Set the browser window as activated.
+  ModifyInstance(app_constants::kChromeAppId,
+                 browser->window()->GetNativeWindow(), kActiveInstanceState);
+  task_environment_.FastForwardBy(base::Minutes(10));
+  ModifyInstance(app_constants::kChromeAppId,
+                 browser->window()->GetNativeWindow(), kInactiveInstanceState);
+
+  // Verify UKM is not reported.
+  auto entries = test_ukm_recorder()->GetEntriesByName("ChromeOSApp.UsageTime");
+  ASSERT_EQ(2U, entries.size());
+
+  // Reset PlatformMetricsService to simulate the system reboot, and verify
+  // only the new AppKM is reported.
+  ResetAppPlatformMetricsService();
+  task_environment_.FastForwardBy(base::Minutes(5));
+
+  entries = test_ukm_recorder()->GetEntriesByName("ChromeOSApp.UsageTime");
+  ASSERT_EQ(3U, entries.size());
+  VerifyAppUsageTimeUkm(app_constants::kChromeAppId, /*duration=*/2400000,
+                        AppTypeName::kChromeBrowser);
+  VerifyAppUsageTimeUkm(url, /*duration=*/1200000, AppTypeName::kChromeBrowser);
+
+  // Reset PlatformMetricsService to simulate the system reboot, and verify no
+  // more AppKM is reported.
+  ResetAppPlatformMetricsService();
+  task_environment_.FastForwardBy(base::Minutes(5));
+  entries = test_ukm_recorder()->GetEntriesByName("ChromeOSApp.UsageTime");
+  ASSERT_EQ(3U, entries.size());
+  VerifyAppUsageTimeUkm(app_constants::kChromeAppId, /*duration=*/2400000,
+                        AppTypeName::kChromeBrowser);
+  VerifyAppUsageTimeUkm(url, /*duration=*/1200000, AppTypeName::kChromeBrowser);
+}
+
 TEST_F(AppPlatformMetricsServiceTest, UsageTimeUkmWithMultipleWindows) {
   // Create a browser window.
   InstallOneApp(app_constants::kChromeAppId, AppType::kChromeApp, "Chrome",
@@ -1212,8 +1316,6 @@
   ModifyInstance(app_constants::kChromeAppId,
                  browser1->window()->GetNativeWindow(), kActiveInstanceState);
   task_environment_.FastForwardBy(base::Minutes(5));
-  VerifyAppUsageTimeUkm(app_constants::kChromeAppId, /*duration=*/300000,
-                        AppTypeName::kChromeBrowser);
 
   // Set the browser window1 inactive.
   ModifyInstance(app_constants::kChromeAppId,
@@ -1226,12 +1328,7 @@
   // Set the browser window2 active.
   ModifyInstance(app_constants::kChromeAppId,
                  browser2->window()->GetNativeWindow(), kActiveInstanceState);
-  task_environment_.FastForwardBy(base::Minutes(4));
-
-  VerifyAppUsageTimeUkm(app_constants::kChromeAppId, /*duration=*/540000,
-                        AppTypeName::kChromeBrowser);
-
-  task_environment_.FastForwardBy(base::Minutes(3));
+  task_environment_.FastForwardBy(base::Minutes(7));
 
   // Close windows.
   ModifyInstance(app_constants::kChromeAppId,
@@ -1241,7 +1338,10 @@
                  browser2->window()->GetNativeWindow(),
                  apps::InstanceState::kDestroyed);
 
-  task_environment_.FastForwardBy(base::Minutes(2));
+  VerifyNoAppUsageTimeUkm();
+
+  // Verify UKM is reported after 2hours.
+  task_environment_.FastForwardBy(base::Minutes(107));
   VerifyAppUsageTimeUkm(app_constants::kChromeAppId, /*duration=*/720000,
                         AppTypeName::kChromeBrowser);
 }
@@ -1260,7 +1360,7 @@
   auto web_app_window =
       CreateWebAppWindow(browser->window()->GetNativeWindow());
 
-  // Set the browser window as activated.
+  // Set the browser window as inactivated.
   ModifyInstance(app_constants::kChromeAppId,
                  browser->window()->GetNativeWindow(), kInactiveInstanceState);
 
@@ -1268,11 +1368,7 @@
   ModifyWebAppInstance(web_app_id, web_app_window.get(), kActiveInstanceState);
 
   task_environment_.FastForwardBy(base::Minutes(5));
-
-  // Verify only the web app UKM is reported.
-  auto entries = test_ukm_recorder()->GetEntriesByName("ChromeOSApp.UsageTime");
-  ASSERT_EQ(1U, entries.size());
-  VerifyAppUsageTimeUkm(url, /*duration=*/300000, AppTypeName::kChromeBrowser);
+  VerifyNoAppUsageTimeUkm();
 
   // Set the browser window and web app tabs as inactivated.
   ModifyInstance(app_constants::kChromeAppId,
@@ -1288,11 +1384,7 @@
   ModifyInstance(app_constants::kChromeAppId,
                  browser->window()->GetNativeWindow(), kActiveInstanceState);
   task_environment_.FastForwardBy(base::Minutes(3));
-
-  // Verify only the web app UKM is reported.
-  entries = test_ukm_recorder()->GetEntriesByName("ChromeOSApp.UsageTime");
-  ASSERT_EQ(2U, entries.size());
-  VerifyAppUsageTimeUkm(url, /*duration=*/480000, AppTypeName::kChromeBrowser);
+  VerifyNoAppUsageTimeUkm();
 
   // Set the web app tab as inactivated.
   ModifyWebAppInstance(web_app_id, web_app_window.get(),
@@ -1307,12 +1399,13 @@
   ModifyInstance(app_constants::kChromeAppId,
                  browser->window()->GetNativeWindow(),
                  apps::InstanceState::kDestroyed);
+  VerifyNoAppUsageTimeUkm();
 
-  task_environment_.FastForwardBy(base::Minutes(4));
+  task_environment_.FastForwardBy(base::Minutes(109));
 
-  // Verify only the browser UKM is reported.
-  entries = test_ukm_recorder()->GetEntriesByName("ChromeOSApp.UsageTime");
-  ASSERT_EQ(3U, entries.size());
+  // Verify the app usage time AppKM for the web app and browser window.
+  auto entries = test_ukm_recorder()->GetEntriesByName("ChromeOSApp.UsageTime");
+  ASSERT_EQ(2U, entries.size());
   VerifyAppUsageTimeUkm(url, /*duration=*/480000, AppTypeName::kChromeBrowser);
   VerifyAppUsageTimeUkm(app_constants::kChromeAppId, /*duration=*/60000,
                         AppTypeName::kChromeBrowser);
@@ -1340,11 +1433,7 @@
                  browser->window()->GetNativeWindow(), kActiveInstanceState);
 
   task_environment_.FastForwardBy(base::Minutes(5));
-
-  // Verify only the web app UKM is reported.
-  auto entries = test_ukm_recorder()->GetEntriesByName("ChromeOSApp.UsageTime");
-  ASSERT_EQ(1U, entries.size());
-  VerifyAppUsageTimeUkm(url, /*duration=*/300000, AppTypeName::kChromeBrowser);
+  VerifyNoAppUsageTimeUkm();
 
   // Set the web app tab as inactivated.
   ModifyWebAppInstance(web_app_id, web_app_window.get(),
@@ -1354,11 +1443,13 @@
   // Set the browser window as inactivated.
   ModifyInstance(app_constants::kChromeAppId,
                  browser->window()->GetNativeWindow(), kInactiveInstanceState);
-  task_environment_.FastForwardBy(base::Minutes(2));
+  VerifyNoAppUsageTimeUkm();
+  task_environment_.FastForwardBy(base::Minutes(112));
 
-  // Verify the browser UKM is reported.
-  entries = test_ukm_recorder()->GetEntriesByName("ChromeOSApp.UsageTime");
+  // Verify the app usage time AppKM.
+  auto entries = test_ukm_recorder()->GetEntriesByName("ChromeOSApp.UsageTime");
   ASSERT_EQ(2U, entries.size());
+  VerifyAppUsageTimeUkm(url, /*duration=*/300000, AppTypeName::kChromeBrowser);
   VerifyAppUsageTimeUkm(app_constants::kChromeAppId, /*duration=*/180000,
                         AppTypeName::kChromeBrowser);
 
@@ -1374,11 +1465,15 @@
   ModifyInstance(app_constants::kChromeAppId,
                  browser->window()->GetNativeWindow(), kInactiveInstanceState);
 
+  // Verify no more app usage time AppKM is recorded.
+  entries = test_ukm_recorder()->GetEntriesByName("ChromeOSApp.UsageTime");
+  ASSERT_EQ(2U, entries.size());
+
   // Set the web app tab as inactivated.
   ModifyWebAppInstance(web_app_id, web_app_window.get(),
                        kInactiveInstanceState);
 
-  task_environment_.FastForwardBy(base::Minutes(3));
+  task_environment_.FastForwardBy(base::Minutes(118));
 
   // Verify only the web app UKM is reported.
   entries = test_ukm_recorder()->GetEntriesByName("ChromeOSApp.UsageTime");
@@ -1400,7 +1495,12 @@
   // Set the web app tab as destroyed.
   ModifyWebAppInstance(web_app_id, web_app_window.get(),
                        apps::InstanceState::kDestroyed);
-  task_environment_.FastForwardBy(base::Minutes(4));
+
+  // Verify no more app usage time AppKM is recorded.
+  entries = test_ukm_recorder()->GetEntriesByName("ChromeOSApp.UsageTime");
+  ASSERT_EQ(3U, entries.size());
+
+  task_environment_.FastForwardBy(base::Minutes(119));
 
   entries = test_ukm_recorder()->GetEntriesByName("ChromeOSApp.UsageTime");
   ASSERT_EQ(4U, entries.size());
@@ -1438,42 +1538,25 @@
 
   task_environment_.FastForwardBy(base::Minutes(5));
 
-  // Verify only the web app1 UKM is reported.
-  auto entries = test_ukm_recorder()->GetEntriesByName("ChromeOSApp.UsageTime");
-  ASSERT_EQ(1U, entries.size());
-  VerifyAppUsageTimeUkm(url1, /*duration=*/300000, AppTypeName::kChromeBrowser);
-
   // Set the web app tab 2 as activated.
   ModifyWebAppInstance(web_app_id2, web_app_window2.get(),
                        kActiveInstanceState);
   ModifyWebAppInstance(web_app_id1, web_app_window1.get(),
                        kInactiveInstanceState);
-  task_environment_.FastForwardBy(base::Minutes(5));
-
-  // Verify only the web app2 UKM is reported.
-  entries = test_ukm_recorder()->GetEntriesByName("ChromeOSApp.UsageTime");
-  ASSERT_EQ(2U, entries.size());
-  VerifyAppUsageTimeUkm(url2, /*duration=*/300000, AppTypeName::kChromeBrowser);
+  task_environment_.FastForwardBy(base::Minutes(4));
 
   // Set the web app tabs as inactivated.
   ModifyWebAppInstance(web_app_id1, web_app_window1.get(),
                        kInactiveInstanceState);
   ModifyWebAppInstance(web_app_id2, web_app_window2.get(),
                        kInactiveInstanceState);
-  task_environment_.FastForwardBy(base::Minutes(5));
 
-  // Verify the browser UKM is reported.
-  entries = test_ukm_recorder()->GetEntriesByName("ChromeOSApp.UsageTime");
-  ASSERT_EQ(3U, entries.size());
-  VerifyAppUsageTimeUkm(app_constants::kChromeAppId, /*duration=*/300000,
-                        AppTypeName::kChromeBrowser);
-
-  task_environment_.FastForwardBy(base::Minutes(2));
+  task_environment_.FastForwardBy(base::Minutes(3));
+  VerifyNoAppUsageTimeUkm();
 
   // Set the browser window as activated.
   ModifyInstance(app_constants::kChromeAppId,
                  browser->window()->GetNativeWindow(), kInactiveInstanceState);
-  task_environment_.FastForwardBy(base::Minutes(3));
 
   // Destroy the browser windows, and web app tabs.
   ModifyWebAppInstance(web_app_id1, web_app_window1.get(),
@@ -1484,10 +1567,14 @@
                  browser->window()->GetNativeWindow(),
                  apps::InstanceState::kDestroyed);
 
-  // Verify the browser UKM is reported.
-  entries = test_ukm_recorder()->GetEntriesByName("ChromeOSApp.UsageTime");
-  ASSERT_EQ(4U, entries.size());
-  VerifyAppUsageTimeUkm(app_constants::kChromeAppId, /*duration=*/420000,
+  task_environment_.FastForwardBy(base::Minutes(108));
+
+  // Verify the app usage time AppKM for the web apps and browser window.
+  auto entries = test_ukm_recorder()->GetEntriesByName("ChromeOSApp.UsageTime");
+  ASSERT_EQ(3U, entries.size());
+  VerifyAppUsageTimeUkm(url1, /*duration=*/300000, AppTypeName::kChromeBrowser);
+  VerifyAppUsageTimeUkm(url2, /*duration=*/240000, AppTypeName::kChromeBrowser);
+  VerifyAppUsageTimeUkm(app_constants::kChromeAppId, /*duration=*/180000,
                         AppTypeName::kChromeBrowser);
 }
 
diff --git a/chrome/browser/apps/app_service/metrics/app_platform_metrics_utils.cc b/chrome/browser/apps/app_service/metrics/app_platform_metrics_utils.cc
index 03489d80..7293730 100644
--- a/chrome/browser/apps/app_service/metrics/app_platform_metrics_utils.cc
+++ b/chrome/browser/apps/app_service/metrics/app_platform_metrics_utils.cc
@@ -4,6 +4,7 @@
 
 #include "chrome/browser/apps/app_service/metrics/app_platform_metrics_utils.h"
 
+#include "base/containers/flat_map.h"
 #include "base/metrics/histogram_functions.h"
 #include "chrome/browser/apps/app_service/app_service_proxy.h"
 #include "chrome/browser/apps/app_service/app_service_proxy_factory.h"
@@ -33,6 +34,32 @@
 
 namespace {
 
+base::flat_map<std::string, apps::AppTypeName>& GetAppTypeNameMap() {
+  static base::NoDestructor<base::flat_map<std::string, apps::AppTypeName>>
+      app_type_name_map;
+  if (app_type_name_map->empty()) {
+    *app_type_name_map = {
+        {apps::kArcHistogramName, apps::AppTypeName::kArc},
+        {apps::kBuiltInHistogramName, apps::AppTypeName::kBuiltIn},
+        {apps::kCrostiniHistogramName, apps::AppTypeName::kCrostini},
+        {apps::kChromeAppHistogramName, apps::AppTypeName::kChromeApp},
+        {apps::kWebAppHistogramName, apps::AppTypeName::kWeb},
+        {apps::kMacOsHistogramName, apps::AppTypeName::kMacOs},
+        {apps::kPluginVmHistogramName, apps::AppTypeName::kPluginVm},
+        {apps::kStandaloneBrowserHistogramName,
+         apps::AppTypeName::kStandaloneBrowser},
+        {apps::kRemoteHistogramName, apps::AppTypeName::kRemote},
+        {apps::kBorealisHistogramName, apps::AppTypeName::kBorealis},
+        {apps::kSystemWebAppHistogramName, apps::AppTypeName::kSystemWeb},
+        {apps::kChromeBrowserHistogramName, apps::AppTypeName::kChromeBrowser},
+        {apps::kStandaloneBrowserChromeAppHistogramName,
+         apps::AppTypeName::kStandaloneBrowserChromeApp},
+        {apps::kExtensionHistogramName, apps::AppTypeName::kExtension},
+    };
+  }
+  return *app_type_name_map;
+}
+
 // Determines what app type a Chrome App should be logged as based on its launch
 // container and app id. In particular, Chrome apps in tabs are logged as part
 // of Chrome browser.
@@ -87,6 +114,22 @@
 constexpr int kDurationBuckets = 100;
 constexpr int kUsageTimeBuckets = 50;
 
+constexpr char kArcHistogramName[] = "Arc";
+constexpr char kBuiltInHistogramName[] = "BuiltIn";
+constexpr char kCrostiniHistogramName[] = "Crostini";
+constexpr char kChromeAppHistogramName[] = "ChromeApp";
+constexpr char kWebAppHistogramName[] = "WebApp";
+constexpr char kMacOsHistogramName[] = "MacOs";
+constexpr char kPluginVmHistogramName[] = "PluginVm";
+constexpr char kStandaloneBrowserHistogramName[] = "StandaloneBrowser";
+constexpr char kRemoteHistogramName[] = "RemoteApp";
+constexpr char kBorealisHistogramName[] = "Borealis";
+constexpr char kSystemWebAppHistogramName[] = "SystemWebApp";
+constexpr char kChromeBrowserHistogramName[] = "ChromeBrowser";
+constexpr char kStandaloneBrowserChromeAppHistogramName[] =
+    "StandaloneBrowserChromeApp";
+constexpr char kExtensionHistogramName[] = "Extension";
+
 AppTypeName GetAppTypeNameForWebApp(Profile* profile,
                                     const std::string& app_id,
                                     apps::mojom::LaunchContainer container) {
@@ -218,6 +261,47 @@
   }
 }
 
+std::string GetAppTypeHistogramName(apps::AppTypeName app_type_name) {
+  switch (app_type_name) {
+    case apps::AppTypeName::kUnknown:
+      return std::string();
+    case apps::AppTypeName::kArc:
+      return kArcHistogramName;
+    case apps::AppTypeName::kBuiltIn:
+      return kBuiltInHistogramName;
+    case apps::AppTypeName::kCrostini:
+      return kCrostiniHistogramName;
+    case apps::AppTypeName::kChromeApp:
+      return kChromeAppHistogramName;
+    case apps::AppTypeName::kWeb:
+      return kWebAppHistogramName;
+    case apps::AppTypeName::kMacOs:
+      return kMacOsHistogramName;
+    case apps::AppTypeName::kPluginVm:
+      return kPluginVmHistogramName;
+    case apps::AppTypeName::kStandaloneBrowser:
+      return kStandaloneBrowserHistogramName;
+    case apps::AppTypeName::kRemote:
+      return kRemoteHistogramName;
+    case apps::AppTypeName::kBorealis:
+      return kBorealisHistogramName;
+    case apps::AppTypeName::kSystemWeb:
+      return kSystemWebAppHistogramName;
+    case apps::AppTypeName::kChromeBrowser:
+      return kChromeBrowserHistogramName;
+    case apps::AppTypeName::kStandaloneBrowserChromeApp:
+      return kStandaloneBrowserChromeAppHistogramName;
+    case apps::AppTypeName::kExtension:
+      return kExtensionHistogramName;
+  }
+}
+
+AppTypeName GetAppTypeNameFromString(const std::string& app_type_name) {
+  const auto& app_type_map = GetAppTypeNameMap();
+  auto it = app_type_map.find(app_type_name);
+  return (it != app_type_map.end()) ? it->second : apps::AppTypeName::kUnknown;
+}
+
 bool ShouldRecordUkm(Profile* profile) {
   switch (syncer::GetUploadToGoogleState(
       SyncServiceFactory::GetForProfile(profile), syncer::ModelType::APPS)) {
diff --git a/chrome/browser/apps/app_service/metrics/app_platform_metrics_utils.h b/chrome/browser/apps/app_service/metrics/app_platform_metrics_utils.h
index ef48f7d..e617c6c1 100644
--- a/chrome/browser/apps/app_service/metrics/app_platform_metrics_utils.h
+++ b/chrome/browser/apps/app_service/metrics/app_platform_metrics_utils.h
@@ -77,6 +77,21 @@
 extern const int kDurationBuckets;
 extern const int kUsageTimeBuckets;
 
+extern const char kArcHistogramName[];
+extern const char kBuiltInHistogramName[];
+extern const char kCrostiniHistogramName[];
+extern const char kChromeAppHistogramName[];
+extern const char kWebAppHistogramName[];
+extern const char kMacOsHistogramName[];
+extern const char kPluginVmHistogramName[];
+extern const char kStandaloneBrowserHistogramName[];
+extern const char kRemoteHistogramName[];
+extern const char kBorealisHistogramName[];
+extern const char kSystemWebAppHistogramName[];
+extern const char kChromeBrowserHistogramName[];
+extern const char kStandaloneBrowserChromeAppHistogramName[];
+extern const char kExtensionHistogramName[];
+
 // Determines what app type a web app should be logged as based on its launch
 // container and app id. In particular, web apps in tabs are logged as part of
 // Chrome browser.
@@ -110,6 +125,13 @@
                                     const std::string& app_id,
                                     aura::Window* window);
 
+// Returns the string for `app_type_name` to present the histogram name and save
+// the app type in the user pref for the usage time and input event metrics.
+std::string GetAppTypeHistogramName(apps::AppTypeName app_type_name);
+
+// Returns AppTypeName for the given `app_type_name` string.
+AppTypeName GetAppTypeNameFromString(const std::string& app_type_name);
+
 // Returns true if we are allowed to record UKM for `profile`. Otherwise,
 // returns false.
 bool ShouldRecordUkm(Profile* profile);
diff --git a/chrome/browser/apps/app_service/publishers/standalone_browser_extension_apps.cc b/chrome/browser/apps/app_service/publishers/standalone_browser_extension_apps.cc
index c239e40..dd40280 100644
--- a/chrome/browser/apps/app_service/publishers/standalone_browser_extension_apps.cc
+++ b/chrome/browser/apps/app_service/publishers/standalone_browser_extension_apps.cc
@@ -71,12 +71,29 @@
     return;
   }
 
+  IconType crosapi_icon_type = icon_type;
+  IconKeyPtr crosapi_icon_key = std::make_unique<IconKey>(
+      icon_key.timeline, icon_key.resource_id, icon_key.icon_effects);
+  if (crosapi_icon_type == apps::IconType::kCompressed) {
+    // If the request is for a compressed icon, modify request so that
+    // uncompressed icon is sent over crosapi.
+    crosapi_icon_type = apps::IconType::kUncompressed;
+    crosapi_icon_key->icon_effects = apps::IconEffects::kNone;
+
+    // To compensate for the above, wrap |callback| icon recompression. This is
+    // applied after OnLoadIcon() runs, which is appropriate since OnLoadIcon()
+    // needs an uncompressed icon for ApplyIconEffects().
+    callback = base::BindOnce(
+        [](apps::LoadIconCallback wrapped_callback, IconValuePtr icon_value) {
+          ConvertUncompressedIconToCompressedIcon(std::move(icon_value),
+                                                  std::move(wrapped_callback));
+        },
+        std::move(callback));
+  }
+
   const uint32_t icon_effects = icon_key.icon_effects;
   controller_->LoadIcon(
-      app_id,
-      std::make_unique<IconKey>(icon_key.timeline, icon_key.resource_id,
-                                icon_key.icon_effects),
-      icon_type, size_hint_in_dip,
+      app_id, std::move(crosapi_icon_key), crosapi_icon_type, size_hint_in_dip,
       base::BindOnce(&StandaloneBrowserExtensionApps::OnLoadIcon,
                      weak_factory_.GetWeakPtr(), icon_effects, size_hint_in_dip,
                      std::move(callback)));
@@ -308,7 +325,7 @@
                                                 int size_hint_in_dip,
                                                 apps::LoadIconCallback callback,
                                                 IconValuePtr icon_value) {
-  // We apply the masking effect here, as masking is not implemented in Lacros.
+  // Apply masking effects here since masking is unimplemented in Lacros.
   ApplyIconEffects(static_cast<IconEffects>(icon_effects), size_hint_in_dip,
                    std::move(icon_value), std::move(callback));
 }
diff --git a/chrome/browser/apps/guest_view/web_view_browsertest.cc b/chrome/browser/apps/guest_view/web_view_browsertest.cc
index b11e7b5..aa31e187 100644
--- a/chrome/browser/apps/guest_view/web_view_browsertest.cc
+++ b/chrome/browser/apps/guest_view/web_view_browsertest.cc
@@ -3524,9 +3524,8 @@
   for (auto* download : saved_downloads) {
     const std::string port_string =
         base::NumberToString(embedded_test_server()->port());
-    url::Replacements<char> replacements;
-    replacements.SetPort(port_string.c_str(),
-                         url::Component(0, port_string.size()));
+    GURL::Replacements replacements;
+    replacements.SetPortStr(port_string);
     std::vector<GURL> url_chain;
     url_chain.push_back(download->GetURL().ReplaceComponents(replacements));
 
diff --git a/chrome/browser/ash/arc/net/OWNERS b/chrome/browser/ash/arc/net/OWNERS
new file mode 100644
index 0000000..9107e3e
--- /dev/null
+++ b/chrome/browser/ash/arc/net/OWNERS
@@ -0,0 +1 @@
+file://ash/components/arc/net/OWNERS
diff --git a/chrome/browser/ash/arc/net/cert_manager_impl.cc b/chrome/browser/ash/arc/net/cert_manager_impl.cc
new file mode 100644
index 0000000..7c6f45f
--- /dev/null
+++ b/chrome/browser/ash/arc/net/cert_manager_impl.cc
@@ -0,0 +1,182 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/ash/arc/net/cert_manager_impl.h"
+
+#include <pk11priv.h>
+#include <pk11pub.h>
+
+#include <utility>
+
+#include "base/strings/string_number_conversions.h"
+#include "base/task/bind_post_task.h"
+#include "chrome/browser/net/nss_service.h"
+#include "chrome/browser/net/nss_service_factory.h"
+#include "chrome/browser/profiles/profile.h"
+#include "components/device_event_log/device_event_log.h"
+#include "content/public/browser/browser_task_traits.h"
+#include "content/public/browser/browser_thread.h"
+#include "crypto/nss_key_util.h"
+#include "net/cert/nss_cert_database.h"
+#include "net/cert/pem.h"
+#include "net/cert/scoped_nss_types.h"
+#include "net/cert/x509_util_nss.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
+
+namespace {
+
+void GetCertDBOnIOThread(
+    NssCertDatabaseGetter database_getter,
+    base::OnceCallback<void(net::NSSCertDatabase*)> callback) {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
+
+  auto split_callback = base::SplitOnceCallback(std::move(callback));
+  net::NSSCertDatabase* cert_db =
+      std::move(database_getter).Run(std::move(split_callback.first));
+  // If the NSS database was already available, |cert_db| is non-null and
+  // the callback has not been called. Explicitly call the callback.
+  if (cert_db) {
+    std::move(split_callback.second).Run(cert_db);
+  }
+}
+
+}  // namespace
+
+namespace arc {
+
+CertManagerImpl::CertManagerImpl(Profile* profile) : profile_(profile) {}
+
+CertManagerImpl::~CertManagerImpl() = default;
+
+std::string CertManagerImpl::ImportPrivateKey(const std::string& key_pem,
+                                              net::NSSCertDatabase* database) {
+  if (!database) {
+    NET_LOG(ERROR) << "Certificate database is not initialized";
+    return std::string();
+  }
+
+  net::PEMTokenizer tokenizer(key_pem, {kPrivateKeyPEMHeader});
+  if (!tokenizer.GetNext()) {
+    NET_LOG(ERROR) << "Failed to get private key data";
+    return std::string();
+  }
+  std::vector<uint8_t> key_der(tokenizer.data().begin(),
+                               tokenizer.data().end());
+
+  crypto::ScopedPK11Slot private_slot = database->GetPrivateSlot();
+  if (!private_slot) {
+    NET_LOG(ERROR) << "Failed to get PK11 slot";
+    return std::string();
+  }
+
+  crypto::ScopedSECKEYPrivateKey key(crypto::ImportNSSKeyFromPrivateKeyInfo(
+      private_slot.get(), key_der, true /* permanent */));
+  if (!key) {
+    NET_LOG(ERROR) << "Failed to import private key";
+    return std::string();
+  }
+
+  crypto::ScopedSECItem sec_item(PK11_GetLowLevelKeyIDForPrivateKey(key.get()));
+  if (!sec_item) {
+    NET_LOG(ERROR) << "Failed to get private key ID";
+    return std::string();
+  }
+  return base::HexEncode(sec_item->data, sec_item->len);
+}
+
+std::string CertManagerImpl::ImportUserCert(const std::string& cert_pem,
+                                            net::NSSCertDatabase* database) {
+  if (!database) {
+    NET_LOG(ERROR) << "Certificate database is not initialized";
+    return std::string();
+  }
+
+  net::PEMTokenizer tokenizer(cert_pem, {kCertificatePEMHeader});
+  if (!tokenizer.GetNext()) {
+    NET_LOG(ERROR) << "Failed to get certificate data";
+    return std::string();
+  }
+
+  int status = database->ImportUserCert(tokenizer.data());
+  if (status != net::OK) {
+    NET_LOG(ERROR) << "Failed to import user certificate with status code "
+                   << status;
+    return std::string();
+  }
+
+  std::vector<uint8_t> cert_der(tokenizer.data().begin(),
+                                tokenizer.data().end());
+  net::ScopedCERTCertificate cert(
+      net::x509_util::CreateCERTCertificateFromBytes(cert_der.data(),
+                                                     cert_der.size()));
+  crypto::ScopedSECItem sec_item(
+      PK11_GetLowLevelKeyIDForCert(nullptr, cert.get(), nullptr));
+  if (!sec_item) {
+    NET_LOG(ERROR) << "Failed to get certificate ID";
+    return std::string();
+  }
+
+  return base::HexEncode(sec_item->data, sec_item->len);
+}
+
+int CertManagerImpl::GetSlotID(net::NSSCertDatabase* database) {
+  if (!database) {
+    NET_LOG(ERROR) << "Certificate database is not initialized";
+    return -1;
+  }
+
+  crypto::ScopedPK11Slot private_slot = database->GetPrivateSlot();
+  if (!private_slot) {
+    NET_LOG(ERROR) << "Failed to get PK11 slot";
+    return -1;
+  }
+
+  return PK11_GetSlotID(private_slot.get());
+}
+
+void CertManagerImpl::ImportPrivateKeyAndCertWithDB(
+    const std::string& key_pem,
+    const std::string& cert_pem,
+    ImportPrivateKeyAndCertCallback callback,
+    net::NSSCertDatabase* database) {
+  std::string key_id = ImportPrivateKey(key_pem, database);
+  if (key_id.empty()) {
+    NET_LOG(ERROR) << "Failed to import private key";
+    std::move(callback).Run(/*cert_id=*/absl::nullopt,
+                            /*slot_id=*/absl::nullopt);
+    return;
+  }
+  std::string cert_id = ImportUserCert(cert_pem, database);
+  if (cert_id.empty()) {
+    NET_LOG(ERROR) << "Failed to import client certificate";
+    std::move(callback).Run(/*cert_id=*/absl::nullopt,
+                            /*slot_id=*/absl::nullopt);
+    return;
+  }
+  int slot_id = GetSlotID(database);
+
+  // The ID of imported user certificate and private key is the same, use one
+  // of them.
+  DCHECK(key_id == cert_id);
+  std::move(callback).Run(cert_id, slot_id);
+}
+
+void CertManagerImpl::ImportPrivateKeyAndCert(
+    const std::string& key_pem,
+    const std::string& cert_pem,
+    ImportPrivateKeyAndCertCallback callback) {
+  content::GetIOThreadTaskRunner({})->PostTask(
+      FROM_HERE,
+      base::BindOnce(
+          &GetCertDBOnIOThread,
+          NssServiceFactory::GetForContext(profile_)
+              ->CreateNSSCertDatabaseGetterForIOThread(),
+          base::BindPostTask(
+              base::SequencedTaskRunnerHandle::Get(),
+              base::BindOnce(&CertManagerImpl::ImportPrivateKeyAndCertWithDB,
+                             weak_factory_.GetWeakPtr(), key_pem, cert_pem,
+                             std::move(callback)))));
+}
+
+}  // namespace arc
diff --git a/chrome/browser/ash/arc/net/cert_manager_impl.h b/chrome/browser/ash/arc/net/cert_manager_impl.h
new file mode 100644
index 0000000..6cc2304f
--- /dev/null
+++ b/chrome/browser/ash/arc/net/cert_manager_impl.h
@@ -0,0 +1,77 @@
+// Copyright (c) 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_ASH_ARC_NET_CERT_MANAGER_IMPL_H_
+#define CHROME_BROWSER_ASH_ARC_NET_CERT_MANAGER_IMPL_H_
+
+#include <string>
+
+#include "ash/components/arc/net/cert_manager.h"
+
+#include "chrome/browser/net/nss_service.h"
+#include "chrome/browser/profiles/profile.h"
+#include "net/cert/nss_cert_database.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
+
+namespace arc {
+
+// Certificate and private key PKCS #8 PEM headers as described in section 5 and
+// 10 respectively of RFC7468.
+constexpr char kCertificatePEMHeader[] = "CERTIFICATE";
+constexpr char kPrivateKeyPEMHeader[] = "PRIVATE KEY";
+
+// CertManager imports plain-text certificates and private keys into Chrome OS'
+// key store (chaps).
+class CertManagerImpl : public CertManager {
+ public:
+  explicit CertManagerImpl(Profile* profile);
+
+  CertManagerImpl(const CertManagerImpl&) = delete;
+  CertManagerImpl& operator=(const CertManagerImpl&) = delete;
+
+  ~CertManagerImpl() override;
+
+  // Asynchronously import a PEM-formatted private and user certificate into
+  // the NSS certificate database. Calls a callback with its ID and the slot
+  // ID of the database. This method will asynchronously fetch the database.
+  void ImportPrivateKeyAndCert(
+      const std::string& key_pem,
+      const std::string& cert_pem,
+      ImportPrivateKeyAndCertCallback callback) override;
+
+ private:
+  // Imports a PEM-formatted private key into the NSS certificate database and
+  // return its ID or empty string if it fails.
+  std::string ImportPrivateKey(const std::string& key_pem,
+                               net::NSSCertDatabase* database);
+
+  // Imports a PEM-formatted user certificate into the NSS certificate database
+  // and return its ID or empty string if it fails.
+  std::string ImportUserCert(const std::string& cert_pem,
+                             net::NSSCertDatabase* database);
+
+  // Get the private slot ID used by this class.
+  int GetSlotID(net::NSSCertDatabase* database);
+
+  // Import a PEM-formatted private and user certificate into the NSS
+  // certificate database. Calls a callback with its ID and the slot ID of the
+  // database.
+  void ImportPrivateKeyAndCertWithDB(const std::string& key_pem,
+                                     const std::string& cert_pem,
+                                     ImportPrivateKeyAndCertCallback callback,
+                                     net::NSSCertDatabase* database);
+  Profile* profile_;
+  base::WeakPtrFactory<CertManagerImpl> weak_factory_{this};
+
+  FRIEND_TEST_ALL_PREFIXES(CertManagerImplTest, ImportKeyAndCertTest);
+  FRIEND_TEST_ALL_PREFIXES(CertManagerImplTest, ImportKeyAndCertSameIDTest);
+  FRIEND_TEST_ALL_PREFIXES(CertManagerImplTest, ImportCertWithWrongKeyTest);
+  FRIEND_TEST_ALL_PREFIXES(CertManagerImplTest,
+                           ImportKeyAndCertWithInvalidDBTest);
+  FRIEND_TEST_ALL_PREFIXES(CertManagerImplTest, ImportInvalidDataTest);
+};
+
+}  // namespace arc
+
+#endif  // CHROME_BROWSER_ASH_ARC_NET_CERT_MANAGER_IMPL_H_
diff --git a/chrome/browser/ash/arc/net/cert_manager_impl_unittest.cc b/chrome/browser/ash/arc/net/cert_manager_impl_unittest.cc
new file mode 100644
index 0000000..5ef64ec
--- /dev/null
+++ b/chrome/browser/ash/arc/net/cert_manager_impl_unittest.cc
@@ -0,0 +1,240 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/ash/arc/net/cert_manager_impl.h"
+
+#include <string>
+
+#include "base/callback.h"
+#include "base/run_loop.h"
+#include "base/task/bind_post_task.h"
+#include "base/test/test_future.h"
+#include "chrome/test/base/testing_profile.h"
+#include "content/public/test/browser_task_environment.h"
+#include "crypto/scoped_test_nss_db.h"
+#include "net/cert/nss_cert_database.h"
+#include "net/cert/pem.h"
+#include "net/cert/scoped_nss_types.h"
+#include "net/cert/x509_util_nss.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
+
+namespace arc {
+
+namespace {
+
+constexpr char kPrivateKey[] =
+    "-----BEGIN PRIVATE KEY-----\n"
+    "MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDXt3N1RS4Ntj7o\n"
+    "HucHcqO+nRuf/3dK1zDxhtnyrjBFBuhTTvNOL2Njm/LvB5EaaIc2UhavIPnnQEAt\n"
+    "OmhvEsi8A3HB4EU+Pu6UJnUtmEPMC/9WTZnLYAA/gMKYZ8KPZQ1FqNi3pkeWcxTN\n"
+    "twnTDEK4qtd6+1veqWTYuxU6IUNrE4GX1yhSV4fAq6PKqdTz7VLIZ/wasADMMZ/S\n"
+    "o3/MDR7rHH9hVBPby6liEunXUjzT7L5t+ZN3vUejOlcqdfikuB73oPitZ1vfQ3Ux\n"
+    "2+67hWMLXFrw+4JkBMxyHL2fLfGwczRMy82UVIgqJMYAOM5iTQeQcxVlqwAyEhFU\n"
+    "XSXSF4PFAgMBAAECggEBAMF/spa39oagOq92wOACanVacmREARrmCuYsg6ZXr77L\n"
+    "Ym0QPdmdUncQdYsKa5OXvenxGp3/Y4uXK7omUXWJEPztzgYOCa67PsEv+h5rHi2T\n"
+    "eXhN5a3zsGVGN8gEExcTmyMoQTYDduWy1y9sh+iDb/o8bUvI23DQ3EA5GOJq4hHR\n"
+    "6OODdEJiyDNXsE4cQ8lJ4/mGF+RS1cBkNHRWpZzWT3fZNSUQtMgYMrz72UR+usYY\n"
+    "OzCQPLNPCYgUXUp3caR16qKxOMQgZKEoAAxxVooASnvmuQiMUEY81rJlCeFXa+h9\n"
+    "/eN2eKUnwNac0kWf1SjFQEiFutdbyuVTDIg22UPnFsUCgYEA7mcLHBts1jmBp405\n"
+    "UoMGmckfJecaX1VE2HcYUXrNOjOCD7LEQRo6yOYF7VfgM0LzzYOit7LN7ouZ6Hgt\n"
+    "3zTuMVdvnyiD3BjgogjtppQ7LV/ALThSN+bApOAMtX6yF3QSVGPd7TpfrBz0/ROj\n"
+    "8bcJ+3hpBIq44LS2Si3Mo3QWcecCgYEA56O5lJUxC6cDyZHM+k+AiLTCAA2A+L/P\n"
+    "ToixW9xyssxsrVNtXXr+YYowGf/cgR3kAJ6NcYwFJfsJ27IFgbpX3pCFVAJF4bSb\n"
+    "feE5e7qnYF4NSJspOEr/17VoFAxk7INO6yW7fNSirpY402L1ldQRa7lk5Qmvucj5\n"
+    "TSBXB4fhv3MCgYBezEKyroUcuklAIvwEP23EgSENpVPrTLDPkqvs2nP5DLpPG7rG\n"
+    "WHO/pxf8RNE2EQ15TzrI6STSEljlA8TZ2OZOYIJWO3oTbyEDzaESeCb/5+83DApF\n"
+    "iFBaP21OTk7q3JDdVcjNqESa3/jbGZA7cZlakYrQ74iMcc96t7OD24mBSQKBgQCm\n"
+    "14J/xsXAwtczhFTDpifKT4e8Sf2vLVjAFCzLIYlrx1ovrXuEbWZ0Evh6gZPtW/4x\n"
+    "hAIU2umKZbrABwV4XyOTJz0hOVHkNBYbIPIqcFLGUnf25+tUpJCKahtA9Xxr7lgV\n"
+    "fuQAEZfrcEAV4Z1KAalakfpeDhAIHP2T08tbnT+4iQKBgEI4YfylyTsMAc5RVVE7\n"
+    "CIW3Gn3JSuWm3RRrDSeX3hJIZ8R7pF2rKYx7QYZi4p0fGny4Xw4bD1RWJwt9mRuE\n"
+    "WYG/EA9Q2PLsfyd8SmUTbnXU6JYUalu3apECNGg2EoXHk9OODhqYphlzSNEHP0LT\n"
+    "fuHaboQVbHNmCQR8DRZgzGH/\n"
+    "-----END PRIVATE KEY-----";
+
+constexpr char kWrongPrivateKey[] =
+    "-----BEGIN PRIVATE KEY-----\n"
+    "MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDZmo7mtBcxsvMp\n"
+    "T5a/p3L/1byrgyAd4zIDR3ivjf6anNlMVNTGSxdtrvCCRxMZPIrEOlq8kIzFqvXn\n"
+    "kZNzZms/ofrQ0c0K7aXhaH+zH3x7eMMZLCyl/gBjhThZac6BtlP6+muHCTL3EKLO\n"
+    "qpPFpkYjnYI3g2qj5dgDJcfhqF0RL5CXHSdHWc2OSK2C5krqT4nPu8tKhHiAOLRW\n"
+    "BBJQ3jpl7rlH1SqKtba3ppu4AioOTtlHfCcR0XZ3ym55QGufIci2yr9pvCwVRwXO\n"
+    "VwisNEyRmfEOXuSUyx6ZZy/vDeqtwLSuSL19gMsTk+HK1dpijnwzlowOwo+7ixZ4\n"
+    "G9g9jjivAgMBAAECggEAdcS1ZFzBVM+B1LDTaIRqs9Vsl/KOlj5Y2fd7dJ/H1Mvg\n"
+    "uvQKeAs58c3FMuzehEEE5TCj3PvqhCyDi8F46PLcRoMW6J8zdp+psDXLLxlyWKzC\n"
+    "AkSrIWc3tKTsG1AtSHxyNRoEyf+LirWBN5KQCV91BF+BkyPXuj5xyzpOVG23eM2i\n"
+    "QDdvvhqetGT0coM0ZrxyTO8hOqtwuBIV+b+PUDXHfiGbIhkt+9ZJL8T0WXOoHNII\n"
+    "lrf9t06FNFiFnSwEGhBIi/BiZ83wgJV67IH4yUO3VHYqRSfQCNWNfzqeOUc3X7VU\n"
+    "eiMyQw//shq9Wmx9R70crLDP6B/8voKMb/CCUK8DmQKBgQD85rtWx1IzdkDaDhNX\n"
+    "samV7JKppeUijTc/jv6K18EAyWa7iICWwcUw18eRWSYq4JbWF3936OX4RLeZmUoI\n"
+    "4jqRAH9Xlnfg7ceygKunKXLlm+TFpyBBtlrGsYhUb1kUskYdPgvY/5NpI9HsE1hy\n"
+    "xPZBqoQ3u3wGJxTdQPsh35moDQKBgQDcRRwOkxTa0ijkDapyGXDJrR6fF9Tm2HJJ\n"
+    "wTNLnLtxjFseHs1ZKefY5dbTeISNRlCye3xhrVTsbV1YlVItQVBfi1Qv1lMUhU3j\n"
+    "e06Ra1aKsVUDBMgv8jJBZU3Jx8ytkltq42HSbKk1BzCheNtfV7YXi8vcJV9fwCIa\n"
+    "ZI45BKDYqwKBgDYHUQSEBqKp48bx9N3qPbGi3d5Sa7ZK9v+kG+srlrcFT+ZGjjom\n"
+    "4WrC3obFxeqpGnBYisniPqcgfxzYa8GkGyD5OztKEQhDpEMVTBalOz+kY2Z6guCn\n"
+    "BZOnP9nSA/Tw9RuwMrXEPAjdNy65H089luKGfEKv0ho6ZTGzfTNKYrhNAoGARlDF\n"
+    "gR2Qxb3bEdoO9DeM2sSqBs17yGmGKmdDcbrJ15ifqcDZesI24fWVG5LYdaThs+hZ\n"
+    "r3C+sG7FIrcgMZQtDSMUL+UyRlW7pIfDcAac7M9pPPp00WF2i4vERkrC2xHinv+R\n"
+    "RbQsW+I8sv86wHfmiCO3Y0KG7LEP8e7xu9/vXNsCgYEA7/EI7EklUB4jIpEJ7oiq\n"
+    "IWIaEaUp6kcopTAKinDpTfVkKZRxtCu70SQL+55ovINA8UvrArGXiCwRfMIW/+Vv\n"
+    "vO6JLBdJU9XKpN+/OP6kdAivlQLuZNIlrMG8eP80KoV4ckQh0Gp2yNoLC+Vuyfjc\n"
+    "iow7aHtm4m4vjWVETAHFviw=\n"
+    "-----END PRIVATE KEY-----";
+
+constexpr char kUserCert[] =
+    "-----BEGIN CERTIFICATE-----\n"
+    "MIIDazCCAlOgAwIBAgIUVLGPFfkgeQbMUT0k/FX3sL101dAwDQYJKoZIhvcNAQEL\n"
+    "BQAwRTELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoM\n"
+    "GEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDAeFw0yMjAyMTYxMzIwMjdaFw0yMjAz\n"
+    "MTgxMzIwMjdaMEUxCzAJBgNVBAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEw\n"
+    "HwYDVQQKDBhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQwggEiMA0GCSqGSIb3DQEB\n"
+    "AQUAA4IBDwAwggEKAoIBAQDXt3N1RS4Ntj7oHucHcqO+nRuf/3dK1zDxhtnyrjBF\n"
+    "BuhTTvNOL2Njm/LvB5EaaIc2UhavIPnnQEAtOmhvEsi8A3HB4EU+Pu6UJnUtmEPM\n"
+    "C/9WTZnLYAA/gMKYZ8KPZQ1FqNi3pkeWcxTNtwnTDEK4qtd6+1veqWTYuxU6IUNr\n"
+    "E4GX1yhSV4fAq6PKqdTz7VLIZ/wasADMMZ/So3/MDR7rHH9hVBPby6liEunXUjzT\n"
+    "7L5t+ZN3vUejOlcqdfikuB73oPitZ1vfQ3Ux2+67hWMLXFrw+4JkBMxyHL2fLfGw\n"
+    "czRMy82UVIgqJMYAOM5iTQeQcxVlqwAyEhFUXSXSF4PFAgMBAAGjUzBRMB0GA1Ud\n"
+    "DgQWBBS1UScRUjNDA88mUXUuzwOJ35aCnDAfBgNVHSMEGDAWgBS1UScRUjNDA88m\n"
+    "UXUuzwOJ35aCnDAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQCf\n"
+    "gcfXjLfaOots4AFtd4sQglWA0RaIfaZl1QTAp6QvJQY7jFyyeNuYV3DfT2DCzJOY\n"
+    "NwRluNCfUZG/YbpTtMUDODpDjASF0z9kQc1bVg3NdcscI+LFtMivuvG7v3Bp7G3I\n"
+    "bhnbhDmWRU9Wss4P3F7x2EULX6NwUzcmyHtEQ+A9xjm6BpshCx3qZNEOYFqV7U82\n"
+    "ioxoifZ5JDF7fIkF22rsI+Ufo3mMvjYz5vSRxc0OVmbpfgmnhB5ValEAopwP5FN3\n"
+    "Jn8C5u+DExI0s93xzNMmJL0ONVeQORY8YkV+7E8wuD+VLSuoo4S/VZ108DRl+kxx\n"
+    "e8AkIK+5yjLpD/0P4TN0\n"
+    "-----END CERTIFICATE-----";
+
+class ImportDoneWaiter
+    : public base::test::TestFuture<absl::optional<std::string>,
+                                    absl::optional<int>> {
+ public:
+  CertManager::ImportPrivateKeyAndCertCallback GetCallback() {
+    return TestFuture::GetCallback<const absl::optional<std::string>&,
+                                   const absl::optional<int>&>();
+  }
+
+  absl::optional<std::string> imported_cert_id() { return Get<0>(); }
+  absl::optional<int> imported_slot_id() { return Get<1>(); }
+};
+
+}  // namespace
+
+class CertManagerImplTest : public testing::Test {
+ public:
+  CertManagerImplTest() = default;
+
+  CertManagerImplTest(const CertManagerImplTest&) = delete;
+  CertManagerImplTest& operator=(const CertManagerImplTest&) = delete;
+
+  ~CertManagerImplTest() override = default;
+
+  void SetUp() override {
+    profile_ = std::make_unique<TestingProfile>();
+    cert_manager_ = std::make_unique<CertManagerImpl>(profile());
+    nss_db_ = std::make_unique<crypto::ScopedTestNSSDB>();
+    cert_db_ = std::make_unique<net::NSSCertDatabase>(
+        crypto::ScopedPK11Slot(PK11_ReferenceSlot(nss_db_->slot())),
+        crypto::ScopedPK11Slot(PK11_ReferenceSlot(nss_db_->slot())));
+  }
+
+  void TearDown() override {
+    // Ensure that nothing is running before tearing down.
+    base::RunLoop().RunUntilIdle();
+
+    profile_.reset();
+    cert_manager_.reset();
+    cert_db_.reset();
+    nss_db_.reset();
+  }
+
+  Profile* profile() { return profile_.get(); }
+  CertManagerImpl* cert_manager() { return cert_manager_.get(); }
+  net::NSSCertDatabase* cert_db() { return cert_db_.get(); }
+
+ private:
+  std::unique_ptr<TestingProfile> profile_;
+  std::unique_ptr<CertManagerImpl> cert_manager_;
+
+  std::unique_ptr<crypto::ScopedTestNSSDB> nss_db_;
+  std::unique_ptr<net::NSSCertDatabase> cert_db_;
+
+  content::BrowserTaskEnvironment task_environment_;
+};
+
+// Imports with a valid certificate and key succeed.
+TEST_F(CertManagerImplTest, ImportKeyAndCertTest) {
+  ImportDoneWaiter import_future;
+  cert_manager()->ImportPrivateKeyAndCertWithDB(
+      kPrivateKey, kUserCert, import_future.GetCallback(), cert_db());
+  EXPECT_TRUE(import_future.Wait());
+  EXPECT_TRUE(import_future.imported_cert_id().has_value());
+  EXPECT_TRUE(import_future.imported_slot_id().has_value());
+
+  // Assert that the imported key and certificate have the same ID.
+  net::PEMTokenizer tokenizer(kUserCert, {kCertificatePEMHeader});
+  EXPECT_TRUE(tokenizer.GetNext());
+  std::vector<uint8_t> cert_der(tokenizer.data().begin(),
+                                tokenizer.data().end());
+  net::ScopedCERTCertificate cert(
+      net::x509_util::CreateCERTCertificateFromBytes(cert_der.data(),
+                                                     cert_der.size()));
+
+  // Get the certificate ID.
+  crypto::ScopedSECItem cert_sec_item(
+      PK11_GetLowLevelKeyIDForCert(nullptr, cert.get(), nullptr));
+  EXPECT_TRUE(cert_sec_item);
+  std::string cert_id =
+      base::HexEncode(cert_sec_item->data, cert_sec_item->len);
+
+  // Get the key ID.
+  crypto::ScopedPK11Slot private_slot = cert_db()->GetPrivateSlot();
+  EXPECT_TRUE(private_slot);
+  crypto::ScopedSECKEYPrivateKey key(
+      PK11_FindPrivateKeyFromCert(private_slot.get(), cert.get(), nullptr));
+  EXPECT_TRUE(key);
+  crypto::ScopedSECItem key_sec_item(
+      PK11_GetLowLevelKeyIDForPrivateKey(key.get()));
+  EXPECT_TRUE(key_sec_item);
+  std::string key_id = base::HexEncode(key_sec_item->data, key_sec_item->len);
+
+  EXPECT_EQ(key_id, cert_id);
+  EXPECT_EQ(import_future.imported_cert_id().value(), cert_id);
+  EXPECT_EQ(import_future.imported_slot_id().value(),
+            PK11_GetSlotID(private_slot.get()));
+}
+
+// Importing a certificate with the wrong key fail.
+TEST_F(CertManagerImplTest, ImportCertWithWrongKeyTest) {
+  ImportDoneWaiter import_future;
+  cert_manager()->ImportPrivateKeyAndCertWithDB(
+      kWrongPrivateKey, kUserCert, import_future.GetCallback(), cert_db());
+  EXPECT_TRUE(import_future.Wait());
+  EXPECT_FALSE(import_future.imported_cert_id().has_value());
+  EXPECT_FALSE(import_future.imported_slot_id().has_value());
+}
+
+// Imports with invalid certificate database fail.
+TEST_F(CertManagerImplTest, ImportKeyAndCertWithInvalidDBTest) {
+  ImportDoneWaiter import_future;
+  cert_manager()->ImportPrivateKeyAndCertWithDB(kPrivateKey, kUserCert,
+                                                import_future.GetCallback(),
+                                                /*database=*/nullptr);
+  EXPECT_TRUE(import_future.Wait());
+  EXPECT_FALSE(import_future.imported_cert_id().has_value());
+  EXPECT_FALSE(import_future.imported_slot_id().has_value());
+}
+
+// Imports with invalid certificates or keys fail.
+TEST_F(CertManagerImplTest, ImportInvalidDataTest) {
+  ImportDoneWaiter import_future;
+  cert_manager()->ImportPrivateKeyAndCertWithDB(
+      /*key_pem=*/"", /*cert_pem=*/"", import_future.GetCallback(), cert_db());
+  EXPECT_TRUE(import_future.Wait());
+  EXPECT_FALSE(import_future.imported_cert_id().has_value());
+  EXPECT_FALSE(import_future.imported_slot_id().has_value());
+}
+
+}  // namespace arc
diff --git a/chrome/browser/ash/arc/session/arc_service_launcher.cc b/chrome/browser/ash/arc/session/arc_service_launcher.cc
index 47ecc437..ed06771 100644
--- a/chrome/browser/ash/arc/session/arc_service_launcher.cc
+++ b/chrome/browser/ash/arc/session/arc_service_launcher.cc
@@ -68,6 +68,7 @@
 #include "chrome/browser/ash/arc/kiosk/arc_kiosk_bridge.h"
 #include "chrome/browser/ash/arc/metrics/arc_metrics_service_proxy.h"
 #include "chrome/browser/ash/arc/nearby_share/arc_nearby_share_bridge.h"
+#include "chrome/browser/ash/arc/net/cert_manager_impl.h"
 #include "chrome/browser/ash/arc/notification/arc_boot_error_notification.h"
 #include "chrome/browser/ash/arc/notification/arc_provision_notification_service.h"
 #include "chrome/browser/ash/arc/oemcrypto/arc_oemcrypto_bridge.h"
@@ -261,8 +262,12 @@
   ArcMetricsServiceProxy::GetForBrowserContext(profile);
   ArcMidisBridge::GetForBrowserContext(profile);
   ArcNearbyShareBridge::GetForBrowserContext(profile);
-  ArcNetHostImpl::GetForBrowserContext(profile)->SetPrefService(
-      profile->GetPrefs());
+  {
+    auto* arc_net_host_impl = ArcNetHostImpl::GetForBrowserContext(profile);
+    arc_net_host_impl->SetPrefService(profile->GetPrefs());
+    arc_net_host_impl->SetCertManager(
+        std::make_unique<CertManagerImpl>(profile));
+  }
   ArcOemCryptoBridge::GetForBrowserContext(profile);
   ArcPaymentAppBridge::GetForBrowserContext(profile);
   ArcPipBridge::GetForBrowserContext(profile);
diff --git a/chrome/browser/ash/borealis/borealis_installer_impl.cc b/chrome/browser/ash/borealis/borealis_installer_impl.cc
index 40b515f..f2659157 100644
--- a/chrome/browser/ash/borealis/borealis_installer_impl.cc
+++ b/chrome/browser/ash/borealis/borealis_installer_impl.cc
@@ -22,7 +22,6 @@
 #include "chrome/browser/profiles/profile.h"
 #include "chromeos/dbus/concierge/concierge_client.h"
 #include "chromeos/dbus/concierge/concierge_service.pb.h"
-#include "chromeos/dbus/dlcservice/dlcservice.pb.h"
 #include "chromeos/dbus/vm_applications/apps.pb.h"
 #include "components/prefs/pref_service.h"
 #include "content/public/browser/browser_thread.h"
@@ -85,10 +84,8 @@
       return;
     }
     SetState(InstallingState::kInstallingDlc);
-    dlcservice::InstallRequest install_request;
-    install_request.set_id(kBorealisDlcName);
     chromeos::DlcserviceClient::Get()->Install(
-        install_request,
+        kBorealisDlcName,
         base::BindOnce(&Installation::OnDlcInstallationCompleted,
                        weak_factory_.GetWeakPtr()),
         base::BindRepeating(&Installation::OnDlcInstallationProgressUpdated,
diff --git a/chrome/browser/ash/borealis/borealis_task.cc b/chrome/browser/ash/borealis/borealis_task.cc
index c801412..d6a308d 100644
--- a/chrome/browser/ash/borealis/borealis_task.cc
+++ b/chrome/browser/ash/borealis/borealis_task.cc
@@ -30,7 +30,6 @@
 #include "chrome/browser/ash/profiles/profile_helper.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chromeos/dbus/concierge/concierge_service.pb.h"
-#include "chromeos/dbus/dlcservice/dlcservice.pb.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace borealis {
@@ -85,10 +84,8 @@
 void MountDlc::RunInternal(BorealisContext* context) {
   // TODO(b/172279567): Ensure the DLC is present before trying to install,
   // otherwise we will silently download borealis here.
-  dlcservice::InstallRequest install_request;
-  install_request.set_id(kBorealisDlcName);
   chromeos::DlcserviceClient::Get()->Install(
-      install_request,
+      kBorealisDlcName,
       base::BindOnce(&MountDlc::OnMountDlc, weak_factory_.GetWeakPtr(),
                      context),
       base::DoNothing());
diff --git a/chrome/browser/ash/chrome_browser_main_parts_ash.cc b/chrome/browser/ash/chrome_browser_main_parts_ash.cc
index 938a387..a58aaab 100644
--- a/chrome/browser/ash/chrome_browser_main_parts_ash.cc
+++ b/chrome/browser/ash/chrome_browser_main_parts_ash.cc
@@ -953,6 +953,12 @@
     std::string user_id_hash =
         parsed_command_line().GetSwitchValueASCII(switches::kLoginProfile);
 
+    if (BrowserDataMigratorImpl::MaybeForceResumeMoveMigration(
+            g_browser_process->local_state(), account_id, user_id_hash)) {
+      LOG(WARNING) << "Restarting chrome to resume move migration.";
+      return;
+    }
+
     if (BrowserDataMigratorImpl::MaybeRestartToMigrate(
             account_id, user_id_hash,
             crosapi::browser_util::PolicyInitState::kBeforeInit)) {
diff --git a/chrome/browser/ash/crosapi/browser_data_migrator.cc b/chrome/browser/ash/crosapi/browser_data_migrator.cc
index 575dced..1a6ef0f 100644
--- a/chrome/browser/ash/crosapi/browser_data_migrator.cc
+++ b/chrome/browser/ash/crosapi/browser_data_migrator.cc
@@ -32,8 +32,46 @@
 // Flag values for `switches::kForceBrowserDataMigrationForTesting`.
 const char kBrowserDataMigrationForceSkip[] = "force-skip";
 const char kBrowserDataMigrationForceMigration[] = "force-migration";
+
+base::RepeatingClosure* g_attempt_restart = nullptr;
 }  // namespace
 
+ScopedRestartAttemptForTesting::ScopedRestartAttemptForTesting(
+    base::RepeatingClosure callback) {
+  DCHECK(!g_attempt_restart);
+  g_attempt_restart = new base::RepeatingClosure(callback);
+}
+
+ScopedRestartAttemptForTesting::~ScopedRestartAttemptForTesting() {
+  DCHECK(g_attempt_restart);
+  delete g_attempt_restart;
+  g_attempt_restart = nullptr;
+}
+
+bool BrowserDataMigratorImpl::MaybeForceResumeMoveMigration(
+    PrefService* local_state,
+    const AccountId& account_id,
+    const std::string& user_id_hash) {
+  LOG(WARNING) << "MaybeForceResumeMoveMigration() is called.";
+  // TODO(crbug.com/1261730): Set a max number of force resume and if that
+  // number is reached, do not attempt a resume and simply mark move migration
+  // && migration as completed.
+  if (!MoveMigrator::ResumeRequired(local_state, user_id_hash))
+    return false;
+
+  return RestartToMigrate(account_id, user_id_hash);
+}
+
+// static
+void BrowserDataMigratorImpl::AttemptRestart() {
+  if (g_attempt_restart) {
+    g_attempt_restart->Run();
+    return;
+  }
+
+  chrome::AttemptRestart();
+}
+
 // static
 bool BrowserDataMigratorImpl::MaybeRestartToMigrate(
     const AccountId& account_id,
@@ -193,10 +231,11 @@
   bool success = SessionManagerClient::Get()->RequestBrowserDataMigration(
       cryptohome::CreateAccountIdentifierFromAccountId(account_id));
 
+  // TODO(crbug.com/1261730): Add an UMA.
   if (!success)
     return false;
 
-  chrome::AttemptRestart();
+  AttemptRestart();
   return true;
 }
 
@@ -231,7 +270,8 @@
   DCHECK(GetMigrationStep(local_state_) == MigrationStep::kRestartCalled);
   SetMigrationStep(local_state_, MigrationStep::kStarted);
 
-  if (base::FeatureList::IsEnabled(kLacrosMoveProfileMigration)) {
+  if (base::FeatureList::IsEnabled(kLacrosMoveProfileMigration) ||
+      MoveMigrator::ResumeRequired(local_state_, user_id_hash_)) {
     LOG(WARNING) << "Initializing MoveMigrator.";
     migrator_delegate_ = std::make_unique<MoveMigrator>(
         original_profile_dir_, user_id_hash_, std::move(progress_tracker_),
diff --git a/chrome/browser/ash/crosapi/browser_data_migrator.h b/chrome/browser/ash/crosapi/browser_data_migrator.h
index fdca409d..68f682e 100644
--- a/chrome/browser/ash/crosapi/browser_data_migrator.h
+++ b/chrome/browser/ash/crosapi/browser_data_migrator.h
@@ -47,6 +47,14 @@
 const base::Feature kLacrosMoveProfileMigration{
     "LacrosMoveProfileMigration", base::FEATURE_DISABLED_BY_DEFAULT};
 
+// Injects the restart function called from
+// `BrowserDataMigratorImpl::AttemptRestart()` in RAII manner.
+class ScopedRestartAttemptForTesting {
+ public:
+  explicit ScopedRestartAttemptForTesting(base::RepeatingClosure callback);
+  ~ScopedRestartAttemptForTesting();
+};
+
 // The interface is exposed to be inherited by fakes in tests.
 class BrowserDataMigrator {
  public:
@@ -147,6 +155,19 @@
   BrowserDataMigratorImpl& operator=(const BrowserDataMigratorImpl&) = delete;
   ~BrowserDataMigratorImpl() override;
 
+  // Calls `chrome::AttemptRestart()` unless `ScopedRestartAttemptForTesting` is
+  // in scope.
+  static void AttemptRestart();
+
+  // Check if move migration has to be resumed. This has to be checked before a
+  // Profile object is created using the user's profile data directory. Like
+  // `MaybeRestartToMigrate()` it returns true if the D-Bus call to the
+  // session_manager is made and successful. The return value of true means that
+  // `chrome::AttemptRestart()` has been called.
+  static bool MaybeForceResumeMoveMigration(PrefService* local_state,
+                                            const AccountId& account_id,
+                                            const std::string& user_id_hash);
+
   // Checks if migration is required for the user identified by `user_id_hash`
   // and if it is required, calls a D-Bus method to session_manager and
   // terminates ash-chrome. It returns true if the D-Bus call to the
diff --git a/chrome/browser/ash/crosapi/browser_data_migrator_browsertest.cc b/chrome/browser/ash/crosapi/browser_data_migrator_browsertest.cc
index 8a7ef58d..182d6cb 100644
--- a/chrome/browser/ash/crosapi/browser_data_migrator_browsertest.cc
+++ b/chrome/browser/ash/crosapi/browser_data_migrator_browsertest.cc
@@ -4,85 +4,157 @@
 
 #include "chrome/browser/ash/crosapi/browser_data_migrator.h"
 
-#include "ash/constants/ash_features.h"
+#include <string>
+
+#include "ash/components/login/auth/user_context.h"
 #include "ash/constants/ash_switches.h"
-#include "base/files/file_util.h"
+#include "base/run_loop.h"
+#include "base/test/bind.h"
 #include "base/test/scoped_feature_list.h"
-#include "base/threading/thread_restrictions.h"
+#include "chrome/browser/ash/crosapi/move_migrator.h"
+#include "chrome/browser/ash/login/existing_user_controller.h"
 #include "chrome/browser/ash/login/login_manager_test.h"
+#include "chrome/browser/ash/login/test/local_state_mixin.h"
 #include "chrome/browser/ash/login/test/login_manager_mixin.h"
 #include "chrome/browser/browser_process.h"
-#include "chrome/browser/profiles/profile.h"
-#include "chrome/browser/profiles/profile_manager.h"
-#include "chrome/common/chrome_constants.h"
-#include "components/account_id/account_id.h"
+#include "chromeos/dbus/session_manager/fake_session_manager_client.h"
+#include "chromeos/dbus/session_manager/session_manager_client.h"
 #include "content/public/test/browser_test.h"
 #include "content/public/test/test_launcher.h"
 
 namespace ash {
-class BrowserDataMigratorRestartTest : public ash::LoginManagerTest {
+
+namespace {
+
+constexpr char kUserIdHash[] = "abcdefg";
+
+// As defined in /ash/components/login/auth/stub_authenticator.cc
+static const char kUserIdHashSuffix[] = "-hash";
+
+}  // namespace
+
+class BrowserDataMigratorResumeOnSignInTest : public ash::LoginManagerTest,
+                                              public LocalStateMixin::Delegate {
  public:
-  BrowserDataMigratorRestartTest() = default;
-  BrowserDataMigratorRestartTest(BrowserDataMigratorRestartTest&) = delete;
-  BrowserDataMigratorRestartTest& operator=(BrowserDataMigratorRestartTest&) =
-      delete;
-  ~BrowserDataMigratorRestartTest() override = default;
+  BrowserDataMigratorResumeOnSignInTest() = default;
+  BrowserDataMigratorResumeOnSignInTest(
+      BrowserDataMigratorResumeOnSignInTest&) = delete;
+  BrowserDataMigratorResumeOnSignInTest& operator=(
+      BrowserDataMigratorResumeOnSignInTest&) = delete;
+  ~BrowserDataMigratorResumeOnSignInTest() override = default;
 
   // ash::LoginManagerTest:
   void SetUp() override {
-    if (content::IsPreTest()) {
-      feature_list_.InitAndDisableFeature(chromeos::features::kLacrosSupport);
-    } else {
-      feature_list_.InitAndEnableFeature(chromeos::features::kLacrosSupport);
-    }
-
-    login_manager_.AppendRegularUsers(1);
-    // This allows chrome to startup with the session info from
-    // `PRE_MigrateOnRestart` without actually needing to go through the login
-    // screen on `MigrateOnRestart`.
-    login_manager_.set_session_restore_enabled();
+    login_manager_mixin_.AppendRegularUsers(1);
 
     ash::LoginManagerTest::SetUp();
   }
 
-  void LoginAsRegularUser() {
-    const auto& users = login_manager_.users();
+  // LocalStateMixin::Delegate:
+  void SetUpLocalState() override {
+    const auto& user = login_manager_mixin_.users()[0];
 
-    LoginUser(users[0].account_id);
+    const std::string user_id_hash =
+        user.account_id.GetUserEmail() + kUserIdHashSuffix;
+
+    // Setting this pref triggers a restart to resume move migration. Check
+    // `BrowserDataMigratorImpl::MaybeForceResumeMoveMigration()`.
+    MoveMigrator::SetResumeStep(g_browser_process->local_state(), user_id_hash,
+                                MoveMigrator::ResumeStep::kRemoveHardLinks);
+  }
+
+  bool LoginAsRegularUser() {
+    ExistingUserController* controller =
+        ExistingUserController::current_controller();
+    if (!controller) {
+      return false;
+    }
+
+    const auto& test_user_info = login_manager_mixin_.users()[0];
+
+    const UserContext user_context =
+        CreateUserContext(test_user_info.account_id, kPassword);
+    SetExpectedCredentials(user_context);
+    controller->Login(user_context, SigninSpecifics());
+    return true;
+  }
+
+  void SetUpInProcessBrowserTestFixture() override {
+    chromeos::SessionManagerClient::InitializeFakeInMemory();
   }
 
  private:
-  LoginManagerMixin login_manager_{&mixin_host_};
+  LoginManagerMixin login_manager_mixin_{&mixin_host_};
+  LocalStateMixin local_state_mixin_{&mixin_host_, this};
   base::test::ScopedFeatureList feature_list_;
 };
 
-IN_PROC_BROWSER_TEST_F(BrowserDataMigratorRestartTest, PRE_MigrateOnRestart) {
+IN_PROC_BROWSER_TEST_F(BrowserDataMigratorResumeOnSignInTest,
+                       ForceResumeOnLogin) {
+  // Test `MaybeForceResumeMoveMigration()` in
+  // `ExistingUserController::ContinueAuthSuccessAfterResumeAttempt()`.
+  base::RunLoop run_loop;
+  ScopedRestartAttemptForTesting scoped_restart_attempt(
+      base::BindLambdaForTesting([&]() { run_loop.Quit(); }));
   LoginAsRegularUser();
-  Profile* profile =
-      g_browser_process->profile_manager()->GetPrimaryUserProfile();
-  {
-    base::ScopedAllowBlockingForTesting allow_blocking;
-    const base::FilePath new_user_data_directory =
-        profile->GetPath().Append(kLacrosDir);
-    // Make sure that lacros directory does not exist before migration.
-    ASSERT_FALSE(base::DirectoryExists(new_user_data_directory));
-    ASSERT_FALSE(base::PathExists(
-        new_user_data_directory.Append(chrome::kPreferencesFilename)));
-  }
+  run_loop.Run();
+  EXPECT_TRUE(chromeos::FakeSessionManagerClient::Get()
+                  ->request_browser_data_migration_called());
 }
 
-IN_PROC_BROWSER_TEST_F(BrowserDataMigratorRestartTest, MigrateOnRestart) {
-  Profile* profile =
-      g_browser_process->profile_manager()->GetPrimaryUserProfile();
-  {
-    base::ScopedAllowBlockingForTesting allow_blocking;
-    const base::FilePath new_user_data_directory =
-        profile->GetPath().Append(kLacrosDir);
-    // Check that the new profile data directory is created.
-    ASSERT_TRUE(base::DirectoryExists(new_user_data_directory));
-    ASSERT_TRUE(
-        base::PathExists(new_user_data_directory.Append(kLacrosProfilePath)
-                             .Append(chrome::kPreferencesFilename)));
+class BrowserDataMigratorResumeRestartInSession
+    : public MixinBasedInProcessBrowserTest,
+      public LocalStateMixin::Delegate {
+ public:
+  BrowserDataMigratorResumeRestartInSession()
+      : scoped_attempt_restart_(
+            std::make_unique<ScopedRestartAttemptForTesting>(
+                base::DoNothing())) {}
+
+  BrowserDataMigratorResumeRestartInSession(
+      BrowserDataMigratorResumeRestartInSession&) = delete;
+  BrowserDataMigratorResumeRestartInSession& operator=(
+      BrowserDataMigratorResumeRestartInSession&) = delete;
+  ~BrowserDataMigratorResumeRestartInSession() override = default;
+
+  void SetUp() override { MixinBasedInProcessBrowserTest::SetUp(); }
+
+  // LocalStateMixin::Delegate
+  void SetUpLocalState() override {
+    // Setting this pref triggers a restart to resume move migration. Check
+    // `BrowserDataMigratorImpl::MaybeForceResumeMoveMigration()`.
+    MoveMigrator::SetResumeStep(g_browser_process->local_state(), kUserIdHash,
+                                MoveMigrator::ResumeStep::kRemoveHardLinks);
   }
+
+  void SetUpCommandLine(base::CommandLine* command_line) override {
+    // By setting these flags, Ash is launched as if it's restarted inside a
+    // user session.
+    command_line->AppendSwitchASCII(switches::kLoginUser, kUserIdHash);
+    command_line->AppendSwitchASCII(switches::kLoginProfile, kUserIdHash);
+
+    MixinBasedInProcessBrowserTest::SetUpCommandLine(command_line);
+  }
+
+  void SetUpInProcessBrowserTestFixture() override {
+    chromeos::SessionManagerClient::InitializeFakeInMemory();
+  }
+
+ private:
+  std::unique_ptr<ScopedRestartAttemptForTesting> scoped_attempt_restart_;
+  LocalStateMixin local_state_mixin_{&mixin_host_, this};
+};
+
+IN_PROC_BROWSER_TEST_F(BrowserDataMigratorResumeRestartInSession,
+                       ResumeMigration) {
+  // Test `MaybeForceResumeMoveMigration()` in
+  // `ChromeBrowserMainPartsAsh::PreProfileInit()`.
+
+  // Note that by the time the body of the test is called,
+  // `ChromeBrowserMainPartsAsh::PreProfileInit()` would have been called. Thus
+  // there is no need for a waiter.
+  EXPECT_TRUE(chromeos::FakeSessionManagerClient::Get()
+                  ->request_browser_data_migration_called());
 }
+
 }  // namespace ash
diff --git a/chrome/browser/ash/crosapi/move_migrator.cc b/chrome/browser/ash/crosapi/move_migrator.cc
index 8b541e3..99fb396 100644
--- a/chrome/browser/ash/crosapi/move_migrator.cc
+++ b/chrome/browser/ash/crosapi/move_migrator.cc
@@ -97,6 +97,23 @@
 }
 
 // static
+bool MoveMigrator::ResumeRequired(PrefService* local_state,
+                                  const std::string& user_id_hash) {
+  ResumeStep resume_step = GetResumeStep(local_state, user_id_hash);
+
+  switch (resume_step) {
+    case ResumeStep::kStart:
+      return false;
+    case ResumeStep::kRemoveHardLinks:
+      return true;
+    case ResumeStep::kMoveTmpDir:
+      return true;
+    case ResumeStep::kCompleted:
+      return false;
+  }
+}
+
+// static
 void MoveMigrator::RegisterLocalStatePrefs(PrefRegistrySimple* registry) {
   registry->RegisterDictionaryPref(kMoveMigrationResumeStepPref,
                                    base::DictionaryValue());
@@ -108,7 +125,7 @@
     const std::string& user_id_hash) {
   return static_cast<ResumeStep>(
       local_state->GetDictionary(kMoveMigrationResumeStepPref)
-          ->FindIntPath(user_id_hash)
+          ->FindIntKey(user_id_hash)
           .value_or(0));
 }
 
diff --git a/chrome/browser/ash/crosapi/move_migrator.h b/chrome/browser/ash/crosapi/move_migrator.h
index 210b78a..234d35dc 100644
--- a/chrome/browser/ash/crosapi/move_migrator.h
+++ b/chrome/browser/ash/crosapi/move_migrator.h
@@ -81,9 +81,16 @@
   // BrowserDataMigratorImpl::MigratorDelegate override.
   void Migrate() override;
 
+  // Returns true if the `ResumeStep` stored in `local_state` for the user
+  // indicates that the migration had been left unfinished in the previous
+  // attempt and that it must be resumed before user profile is created.
+  static bool ResumeRequired(PrefService* local_state,
+                             const std::string& user_id_hash);
+
   static void RegisterLocalStatePrefs(PrefRegistrySimple* registry);
 
  private:
+  FRIEND_TEST_ALL_PREFIXES(MoveMigratorTest, ResumeRequired);
   FRIEND_TEST_ALL_PREFIXES(MoveMigratorTest, PreMigrationCleanUp);
   FRIEND_TEST_ALL_PREFIXES(MoveMigratorTest, SetupLacrosDir);
   FRIEND_TEST_ALL_PREFIXES(MoveMigratorTest,
@@ -92,8 +99,10 @@
   FRIEND_TEST_ALL_PREFIXES(MoveMigratorMigrateTest,
                            MigrateResumeFromRemoveHardLinks);
   FRIEND_TEST_ALL_PREFIXES(MoveMigratorMigrateTest, MigrateResumeFromMove);
+  friend class BrowserDataMigratorResumeOnSignInTest;
+  friend class BrowserDataMigratorResumeRestartInSession;
 
-  // Called in `Migrate()` to determine where to start the migration. Returns
+  // Called to determine where to start the migration. Returns
   // `ResumeStep::kStart` unless there is a step recorded in `Local State` from
   // the previous migration i.e. the previous migration did not complete and
   // crashed halfway.
diff --git a/chrome/browser/ash/crosapi/move_migrator_unittest.cc b/chrome/browser/ash/crosapi/move_migrator_unittest.cc
index 137b259..31a4c811 100644
--- a/chrome/browser/ash/crosapi/move_migrator_unittest.cc
+++ b/chrome/browser/ash/crosapi/move_migrator_unittest.cc
@@ -197,6 +197,26 @@
       base::PathExists(original_profile_dir.Append(kBookmarksFilePath)));
 }
 
+TEST(MoveMigratorTest, ResumeRequired) {
+  const std::string user_id_hash = "abcd";
+  TestingPrefServiceSimple pref_service;
+  MoveMigrator::RegisterLocalStatePrefs(pref_service.registry());
+
+  EXPECT_FALSE(MoveMigrator::ResumeRequired(&pref_service, user_id_hash));
+
+  MoveMigrator::SetResumeStep(&pref_service, user_id_hash,
+                              MoveMigrator::ResumeStep::kRemoveHardLinks);
+  EXPECT_TRUE(MoveMigrator::ResumeRequired(&pref_service, user_id_hash));
+
+  MoveMigrator::SetResumeStep(&pref_service, user_id_hash,
+                              MoveMigrator::ResumeStep::kMoveTmpDir);
+  EXPECT_TRUE(MoveMigrator::ResumeRequired(&pref_service, user_id_hash));
+
+  MoveMigrator::SetResumeStep(&pref_service, user_id_hash,
+                              MoveMigrator::ResumeStep::kCompleted);
+  EXPECT_FALSE(MoveMigrator::ResumeRequired(&pref_service, user_id_hash));
+}
+
 class MoveMigratorMigrateTest : public ::testing::Test {
  public:
   MoveMigratorMigrateTest()
diff --git a/chrome/browser/ash/crosapi/prefs_ash.cc b/chrome/browser/ash/crosapi/prefs_ash.cc
index 3ebcda8..f9889ac6 100644
--- a/chrome/browser/ash/crosapi/prefs_ash.cc
+++ b/chrome/browser/ash/crosapi/prefs_ash.cc
@@ -49,7 +49,7 @@
 PrefsAsh::~PrefsAsh() {
   // Remove this observer, if the Primary logged in profile is not yet created.
   // On actual shutdown, the ProfileManager will destruct before CrosapiManager.
-  if (ProfileManagerObserver::IsInObserverList() && profile_manager_) {
+  if (IsInObserverList() && profile_manager_) {
     profile_manager_->RemoveObserver(this);
   }
 }
@@ -89,7 +89,6 @@
   mojo::Remote<mojom::PrefObserver> remote(std::move(observer));
   remote->OnPrefChanged(value->Clone());
 
-  DCHECK(state->registrar);
   if (!state->registrar->IsObserved(state->path)) {
     // Unretained() is safe since PrefChangeRegistrar and RemoteSet within
     // observers_ are owned by this and wont invoke if PrefsAsh is destroyed.
@@ -119,12 +118,11 @@
       return State{local_state_, &local_state_registrar_,
                    metrics::prefs::kMetricsReportingEnabled};
     case mojom::PrefPath::kAccessibilitySpokenFeedbackEnabled:
-      if (!profile_prefs_registrar_) {
+      if (!profile_prefs_) {
         LOG(WARNING) << "Primary profile is not yet initialized";
         return absl::nullopt;
       }
-      return State{profile_prefs_registrar_->prefs(),
-                   profile_prefs_registrar_.get(),
+      return State{profile_prefs_, &profile_prefs_registrar_,
                    ash::prefs::kAccessibilitySpokenFeedbackEnabled};
     case mojom::PrefPath::kDeviceSystemWideTracingEnabled:
       return State{local_state_, &local_state_registrar_,
@@ -145,11 +143,6 @@
   profile_manager_ = nullptr;
 }
 
-void PrefsAsh::OnProfileWillBeDestroyed(Profile* profile) {
-  profile_observation_.Reset();
-  profile_prefs_registrar_.reset();
-}
-
 void PrefsAsh::OnPrefChanged(mojom::PrefPath path) {
   auto state = GetState(path);
   const base::Value* value =
@@ -173,9 +166,8 @@
 
 void PrefsAsh::OnPrimaryProfileReady(Profile* profile) {
   profile_manager_->RemoveObserver(this);
-
-  profile_prefs_registrar_ = std::make_unique<PrefChangeRegistrar>();
-  profile_prefs_registrar_->Init(profile->GetPrefs());
+  profile_prefs_ = profile->GetPrefs();
+  profile_prefs_registrar_.Init(profile_prefs_);
 }
 
 }  // namespace crosapi
diff --git a/chrome/browser/ash/crosapi/prefs_ash.h b/chrome/browser/ash/crosapi/prefs_ash.h
index 918a797b..2305256 100644
--- a/chrome/browser/ash/crosapi/prefs_ash.h
+++ b/chrome/browser/ash/crosapi/prefs_ash.h
@@ -10,10 +10,7 @@
 #include <utility>
 
 #include "base/gtest_prod_util.h"
-#include "base/scoped_observation.h"
-#include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/profiles/profile_manager_observer.h"
-#include "chrome/browser/profiles/profile_observer.h"
 #include "chromeos/crosapi/mojom/prefs.mojom.h"
 #include "components/prefs/pref_change_registrar.h"
 #include "mojo/public/cpp/bindings/pending_receiver.h"
@@ -29,9 +26,7 @@
 
 // The ash-chrome implementation of the Prefs crosapi interface.
 // This class must only be used from the main thread.
-class PrefsAsh : public mojom::Prefs,
-                 public ProfileManagerObserver,
-                 public ProfileObserver {
+class PrefsAsh : public mojom::Prefs, public ProfileManagerObserver {
  public:
   PrefsAsh(ProfileManager* profile_manager, PrefService* local_state);
   PrefsAsh(const PrefsAsh&) = delete;
@@ -52,9 +47,6 @@
   void OnProfileAdded(Profile* profile) override;
   void OnProfileManagerDestroying() override;
 
-  // ProfileObserver:
-  void OnProfileWillBeDestroyed(Profile* profile) override;
-
   // Used to inject |profile| as a primary profile for testing.
   void OnPrimaryProfileReadyForTesting(Profile* profile) {
     OnPrimaryProfileReady(profile);
@@ -81,18 +73,18 @@
   ProfileManager* profile_manager_;
   // In production, owned by g_browser_process, which outlives this object.
   PrefService* const local_state_;
+  // Owned by the primary profile. This will be set after the profile is
+  // initialized.
+  PrefService* profile_prefs_ = nullptr;
 
   PrefChangeRegistrar local_state_registrar_;
-  std::unique_ptr<PrefChangeRegistrar> profile_prefs_registrar_;
+  PrefChangeRegistrar profile_prefs_registrar_;
 
   // This class supports any number of connections.
   mojo::ReceiverSet<mojom::Prefs> receivers_;
 
   // This class supports any number of observers.
   std::map<mojom::PrefPath, mojo::RemoteSet<mojom::PrefObserver>> observers_;
-
-  // Observe profile destruction to reset prefs observation.
-  base::ScopedObservation<Profile, ProfileObserver> profile_observation_{this};
 };
 
 }  // namespace crosapi
diff --git a/chrome/browser/ash/crostini/termina_installer.cc b/chrome/browser/ash/crostini/termina_installer.cc
index 7a815a4..065c455 100644
--- a/chrome/browser/ash/crostini/termina_installer.cc
+++ b/chrome/browser/ash/crostini/termina_installer.cc
@@ -20,7 +20,6 @@
 #include "chrome/browser/ash/crostini/crostini_util.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/browser_process_platform_part_chromeos.h"
-#include "chromeos/dbus/dlcservice/dlcservice.pb.h"
 #include "content/public/browser/network_service_instance.h"
 #include "services/network/public/cpp/network_connection_tracker.h"
 #include "third_party/cros_system_api/dbus/service_constants.h"
@@ -102,10 +101,8 @@
 void TerminaInstaller::InstallDlc(
     base::OnceCallback<void(InstallResult)> callback,
     bool is_initial_install) {
-  dlcservice::InstallRequest install_request;
-  install_request.set_id(kCrostiniDlcName);
   chromeos::DlcserviceClient::Get()->Install(
-      install_request,
+      kCrostiniDlcName,
       base::BindOnce(&TerminaInstaller::OnInstallDlc,
                      weak_ptr_factory_.GetWeakPtr(), std::move(callback),
                      is_initial_install),
diff --git a/chrome/browser/ash/file_manager/file_manager_browsertest.cc b/chrome/browser/ash/file_manager/file_manager_browsertest.cc
index 9b28c4f..019b3b3 100644
--- a/chrome/browser/ash/file_manager/file_manager_browsertest.cc
+++ b/chrome/browser/ash/file_manager/file_manager_browsertest.cc
@@ -1596,7 +1596,12 @@
         TestCase("openOfficeFromMyFiles").EnableWebDriveOffice(),
         TestCase("openOfficeFromMyFiles").EnableWebDriveOffice().FilesSwa(),
         TestCase("openOfficeFromDrive").EnableWebDriveOffice(),
-        TestCase("openOfficeFromDrive").EnableWebDriveOffice().FilesSwa()));
+        TestCase("openOfficeFromDrive").EnableWebDriveOffice().FilesSwa(),
+        TestCase("openOfficeFromDriveOffline").EnableWebDriveOffice().Offline(),
+        TestCase("openOfficeFromDriveOffline")
+            .EnableWebDriveOffice()
+            .Offline()
+            .FilesSwa()));
 
 WRAPPED_INSTANTIATE_TEST_SUITE_P(
     GuestOs, /* guest_os.js */
diff --git a/chrome/browser/ash/file_manager/file_manager_string_util.cc b/chrome/browser/ash/file_manager/file_manager_string_util.cc
index 973f7b5..7a020f1 100644
--- a/chrome/browser/ash/file_manager/file_manager_string_util.cc
+++ b/chrome/browser/ash/file_manager/file_manager_string_util.cc
@@ -830,6 +830,8 @@
   SET_STRING("COLUMN_SORTED_ASC", IDS_FILE_BROWSER_COLUMN_SORTED_ASC_MESSAGE);
   SET_STRING("COLUMN_SORTED_DESC", IDS_FILE_BROWSER_COLUMN_SORTED_DESC_MESSAGE);
   SET_STRING("EXTERNAL_LINK_MESSAGE", IDS_FILE_BROWSER_EXTERNAL_LINK_MESSAGE);
+  SET_STRING("EXTRACT_ALL_BUTTON_LABEL",
+             IDS_FILE_BROWSER_EXTRACT_ALL_BUTTON_LABEL);
   SET_STRING("SELECTION_ADD_SINGLE_ENTRY",
              IDS_FILE_BROWSER_SELECTION_ADD_SINGLE_ENTRY);
   SET_STRING("SELECTION_REMOVE_SINGLE_ENTRY",
diff --git a/chrome/browser/ash/file_manager/file_tasks.cc b/chrome/browser/ash/file_manager/file_tasks.cc
index a0a6ebfb..0cb2709 100644
--- a/chrome/browser/ash/file_manager/file_tasks.cc
+++ b/chrome/browser/ash/file_manager/file_tasks.cc
@@ -338,7 +338,9 @@
     }
   }
 
-  if (!base::FeatureList::IsEnabled(ash::features::kFilesWebDriveOffice)) {
+  if (!base::FeatureList::IsEnabled(ash::features::kFilesWebDriveOffice) ||
+      drive::util::GetDriveConnectionStatus(profile) !=
+          drive::util::DRIVE_CONNECTED) {
     disabled_actions.emplace("open-web-drive-office");
   } else {
     for (const auto& entry : entries) {
diff --git a/chrome/browser/ash/login/existing_user_controller.cc b/chrome/browser/ash/login/existing_user_controller.cc
index efd01e4..2968759 100644
--- a/chrome/browser/ash/login/existing_user_controller.cc
+++ b/chrome/browser/ash/login/existing_user_controller.cc
@@ -991,6 +991,14 @@
                        user_context.GetAccountId(), true));
   }
 
+  if (BrowserDataMigratorImpl::MaybeForceResumeMoveMigration(
+          g_browser_process->local_state(), user_context.GetAccountId(),
+          user_context.GetUserIDHash())) {
+    // TODO(crbug.com/1261730): Add an UMA.
+    LOG(WARNING) << "Restarting Chrome to resume move migration.";
+    return;
+  }
+
   UserSessionManager::StartSessionType start_session_type =
       UserAddingScreen::Get()->IsRunning()
           ? UserSessionManager::StartSessionType::kSecondary
diff --git a/chrome/browser/ash/plugin_vm/plugin_vm_installer.cc b/chrome/browser/ash/plugin_vm/plugin_vm_installer.cc
index 19d0478b..840b6448 100644
--- a/chrome/browser/ash/plugin_vm/plugin_vm_installer.cc
+++ b/chrome/browser/ash/plugin_vm/plugin_vm_installer.cc
@@ -29,7 +29,6 @@
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/profiles/profile_key.h"
 #include "chromeos/dbus/debug_daemon/debug_daemon_client.h"
-#include "chromeos/dbus/dlcservice/dlcservice.pb.h"
 #include "components/download/public/background_service/background_download_service.h"
 #include "components/download/public/background_service/download_metadata.h"
 #include "components/prefs/pref_service.h"
@@ -468,10 +467,8 @@
     return;
   }
 
-  dlcservice::InstallRequest install_request;
-  install_request.set_id(kPitaDlc);
   chromeos::DlcserviceClient::Get()->Install(
-      install_request,
+      "pita",
       base::BindOnce(&PluginVmInstaller::OnDlcDownloadCompleted,
                      weak_ptr_factory_.GetWeakPtr()),
       base::BindRepeating(&PluginVmInstaller::OnDlcDownloadProgressUpdated,
diff --git a/chrome/browser/ash/plugin_vm/plugin_vm_manager_impl.cc b/chrome/browser/ash/plugin_vm/plugin_vm_manager_impl.cc
index 768a97a..030a41fd 100644
--- a/chrome/browser/ash/plugin_vm/plugin_vm_manager_impl.cc
+++ b/chrome/browser/ash/plugin_vm/plugin_vm_manager_impl.cc
@@ -24,7 +24,6 @@
 #include "chrome/grit/generated_resources.h"
 #include "chromeos/dbus/dbus_thread_manager.h"
 #include "chromeos/dbus/debug_daemon/debug_daemon_client.h"
-#include "chromeos/dbus/dlcservice/dlcservice.pb.h"
 #include "components/keyed_service/content/browser_context_dependency_manager.h"
 #include "components/keyed_service/content/browser_context_keyed_service_factory.h"
 #include "components/prefs/pref_service.h"
@@ -417,10 +416,8 @@
     base::OnceCallback<void(bool default_vm_exists)> success_callback,
     base::OnceClosure error_callback) {
   LOG_FUNCTION_CALL();
-  dlcservice::InstallRequest install_request;
-  install_request.set_id(kPitaDlc);
   chromeos::DlcserviceClient::Get()->Install(
-      install_request,
+      "pita",
       base::BindOnce(&PluginVmManagerImpl::OnInstallPluginVmDlc,
                      weak_ptr_factory_.GetWeakPtr(),
                      std::move(success_callback), std::move(error_callback)),
diff --git a/chrome/browser/ash/plugin_vm/plugin_vm_util.cc b/chrome/browser/ash/plugin_vm/plugin_vm_util.cc
index 61a0736..052ccb80 100644
--- a/chrome/browser/ash/plugin_vm/plugin_vm_util.cc
+++ b/chrome/browser/ash/plugin_vm/plugin_vm_util.cc
@@ -33,7 +33,6 @@
 
 namespace plugin_vm {
 
-const char kPitaDlc[] = "pita";
 const char kPluginVmShelfAppId[] = "lgjpclljbbmphhnalkeplcmnjpfmmaek";
 const char kPluginVmName[] = "PvmDefault";
 const char kChromeOSBaseDirectoryDisplayText[] = "Network \u203a ChromeOS";
diff --git a/chrome/browser/ash/plugin_vm/plugin_vm_util.h b/chrome/browser/ash/plugin_vm/plugin_vm_util.h
index 1f865c2..d3489ee 100644
--- a/chrome/browser/ash/plugin_vm/plugin_vm_util.h
+++ b/chrome/browser/ash/plugin_vm/plugin_vm_util.h
@@ -26,9 +26,6 @@
 
 class PluginVmPolicySubscription;
 
-// Name of the pita DLC.
-extern const char kPitaDlc[];
-
 // This is used by both the Plugin VM app and its installer.
 // Generated as crx_file::id_util::GenerateId("org.chromium.plugin_vm");
 extern const char kPluginVmShelfAppId[];
diff --git a/chrome/browser/chrome_browser_interface_binders.cc b/chrome/browser/chrome_browser_interface_binders.cc
index 03509986..b455296fc 100644
--- a/chrome/browser/chrome_browser_interface_binders.cc
+++ b/chrome/browser/chrome_browser_interface_binders.cc
@@ -249,9 +249,9 @@
 #include "chrome/browser/ui/webui/nearby_share/nearby_share_dialog_ui.h"
 #include "chrome/browser/ui/webui/nearby_share/public/mojom/nearby_share_settings.mojom.h"  // nogncheck crbug.com/1125897
 #include "chrome/browser/ui/webui/settings/ash/os_apps_page/mojom/app_notification_handler.mojom.h"
-#include "chrome/browser/ui/webui/settings/ash/search/search.mojom.h"
-#include "chrome/browser/ui/webui/settings/ash/search/user_action_recorder.mojom.h"
 #include "chrome/browser/ui/webui/settings/chromeos/os_settings_ui.h"
+#include "chrome/browser/ui/webui/settings/chromeos/search/search.mojom.h"
+#include "chrome/browser/ui/webui/settings/chromeos/search/user_action_recorder.mojom.h"
 #include "chromeos/components/local_search_service/public/mojom/index.mojom.h"
 #include "chromeos/services/bluetooth_config/public/mojom/cros_bluetooth_config.mojom.h"
 #include "chromeos/services/cellular_setup/public/mojom/cellular_setup.mojom.h"
diff --git a/chrome/browser/chrome_content_browser_client.cc b/chrome/browser/chrome_content_browser_client.cc
index 17daa08..5009b62d 100644
--- a/chrome/browser/chrome_content_browser_client.cc
+++ b/chrome/browser/chrome_content_browser_client.cc
@@ -1570,9 +1570,7 @@
       std::make_unique<CrashMemoryMetricsCollector>(host));
 #endif
 
-  Profile* original_profile = profile->GetOriginalProfile();
-  RendererUpdaterFactory::GetForProfile(original_profile)
-      ->InitializeRenderer(host);
+  RendererUpdaterFactory::GetForProfile(profile)->InitializeRenderer(host);
 
   for (size_t i = 0; i < extra_parts_.size(); ++i)
     extra_parts_[i]->RenderProcessWillLaunch(host);
diff --git a/chrome/browser/chromeos/BUILD.gn b/chrome/browser/chromeos/BUILD.gn
index 792ef16..3743c2bb 100644
--- a/chrome/browser/chromeos/BUILD.gn
+++ b/chrome/browser/chromeos/BUILD.gn
@@ -962,6 +962,8 @@
     "../ash/arc/nearby_share/ui/nearby_share_overlay_view.h",
     "../ash/arc/nearby_share/ui/progress_bar_dialog_view.cc",
     "../ash/arc/nearby_share/ui/progress_bar_dialog_view.h",
+    "../ash/arc/net/cert_manager_impl.cc",
+    "../ash/arc/net/cert_manager_impl.h",
     "../ash/arc/notification/arc_boot_error_notification.cc",
     "../ash/arc/notification/arc_boot_error_notification.h",
     "../ash/arc/notification/arc_management_transition_notification.cc",
@@ -4077,6 +4079,7 @@
     "../ash/arc/nearby_share/ui/low_disk_space_dialog_view_unittest.cc",
     "../ash/arc/nearby_share/ui/nearby_share_overlay_view_unittest.cc",
     "../ash/arc/nearby_share/ui/progress_bar_dialog_view_unittest.cc",
+    "../ash/arc/net/cert_manager_impl_unittest.cc",
     "../ash/arc/notification/arc_management_transition_notification_unittest.cc",
     "../ash/arc/notification/arc_provision_notification_service_unittest.cc",
     "../ash/arc/optin/arc_terms_of_service_default_negotiator_unittest.cc",
@@ -4795,9 +4798,6 @@
     "../ui/webui/settings/ash/calculator/size_calculator_test_api.h",
     "../ui/webui/settings/ash/os_apps_page/app_notification_handler_unittest.cc",
     "../ui/webui/settings/ash/os_apps_page/mojom/app_type_mojom_traits_unittest.cc",
-    "../ui/webui/settings/ash/search/per_session_settings_user_action_tracker_unittest.cc",
-    "../ui/webui/settings/ash/search/search_handler_unittest.cc",
-    "../ui/webui/settings/ash/search/search_tag_registry_unittest.cc",
     "../ui/webui/settings/chromeos/ambient_mode_handler_unittest.cc",
     "../ui/webui/settings/chromeos/bluetooth_handler_unittest.cc",
     "../ui/webui/settings/chromeos/change_picture_handler_unittest.cc",
@@ -4809,6 +4809,9 @@
     "../ui/webui/settings/chromeos/multidevice_handler_unittest.cc",
     "../ui/webui/settings/chromeos/os_settings_manager_unittest.cc",
     "../ui/webui/settings/chromeos/os_settings_section_unittest.cc",
+    "../ui/webui/settings/chromeos/search/per_session_settings_user_action_tracker_unittest.cc",
+    "../ui/webui/settings/chromeos/search/search_handler_unittest.cc",
+    "../ui/webui/settings/chromeos/search/search_tag_registry_unittest.cc",
     "../ui/webui/settings/chromeos/settings_user_action_tracker_unittest.cc",
   ]
   if (use_cups) {
diff --git a/chrome/browser/content_settings/content_settings_browsertest.cc b/chrome/browser/content_settings/content_settings_browsertest.cc
index 6b96cbbd..70609c9c 100644
--- a/chrome/browser/content_settings/content_settings_browsertest.cc
+++ b/chrome/browser/content_settings/content_settings_browsertest.cc
@@ -9,6 +9,7 @@
 #include "base/strings/utf_string_conversions.h"
 #include "base/task/post_task.h"
 #include "base/test/bind.h"
+#include "base/test/mock_callback.h"
 #include "base/test/scoped_feature_list.h"
 #include "build/build_config.h"
 #include "build/chromeos_buildflags.h"
@@ -48,6 +49,7 @@
 #include "content/public/test/browser_test_utils.h"
 #include "content/public/test/ppapi_test_utils.h"
 #include "content/public/test/prerender_test_util.h"
+#include "content/public/test/test_frame_navigation_observer.h"
 #include "content/public/test/test_utils.h"
 #include "net/cookies/canonical_cookie_test_helpers.h"
 #include "net/dns/mock_host_resolver.h"
@@ -104,6 +106,13 @@
   return result;
 }
 
+size_t GetRenderFrameHostCount(content::RenderFrameHost* starting_frame) {
+  size_t count = 0;
+  starting_frame->ForEachRenderFrameHost(
+      base::BindLambdaForTesting([&](content::RenderFrameHost*) { ++count; }));
+  return count;
+}
+
 class CookieChangeObserver : public content::WebContentsObserver {
  public:
   explicit CookieChangeObserver(content::WebContents* web_contents)
@@ -1099,6 +1108,77 @@
           ->IsContentBlocked(ContentSettingsType::JAVASCRIPT));
 }
 
+IN_PROC_BROWSER_TEST_F(ContentSettingsTest,
+                       SpareRenderProcessHostRulesAreUpdated) {
+  // Make sure a spare RenderProcessHost exists during the test.
+  content::RenderProcessHost::WarmupSpareRenderProcessHost(
+      browser()->profile());
+
+  ASSERT_TRUE(embedded_test_server()->Start());
+  // URL to a page that loads a cross-site iframe which creates another iframe
+  // via JavaScript. We will count iframes to test if JavaScript is blocked or
+  // not.
+  const GURL url = embedded_test_server()->GetURL(
+      "a.test", "/iframe_cross_site_with_script.html");
+
+  // Disable JavaScript. A warmed-up spare renderer should get ContentSettings
+  // updates and disable JavaScript.
+  HostContentSettingsMapFactory::GetForProfile(browser()->profile())
+      ->SetDefaultContentSetting(ContentSettingsType::JAVASCRIPT,
+                                 CONTENT_SETTING_BLOCK);
+  // Navigate to the page.
+  content::RenderFrameHost* main_frame =
+      ui_test_utils::NavigateToURL(browser(), url);
+  ASSERT_TRUE(main_frame);
+  // Ensure 2 frames exist after the load (main frame and 'b.test' frame).
+  EXPECT_EQ(2u, GetRenderFrameHostCount(main_frame));
+}
+
+IN_PROC_BROWSER_TEST_F(ContentSettingsTest, NonMainFrameRulesAreUpdated) {
+  ASSERT_TRUE(embedded_test_server()->Start());
+  // URL to a page that loads a cross-site iframe which creates another iframe
+  // via JavaScript. We will count iframes to test if JavaScript is blocked or
+  // not.
+  const GURL url = embedded_test_server()->GetURL(
+      "a.test", "/iframe_cross_site_with_script.html");
+
+  // Disable JavaScript.
+  HostContentSettingsMapFactory::GetForProfile(browser()->profile())
+      ->SetDefaultContentSetting(ContentSettingsType::JAVASCRIPT,
+                                 CONTENT_SETTING_BLOCK);
+  // Navigate to the page.
+  content::RenderFrameHost* main_frame =
+      ui_test_utils::NavigateToURL(browser(), url);
+  ASSERT_TRUE(main_frame);
+  // Ensure 2 frames exist after the load (main frame and 'b.test' frame).
+  EXPECT_EQ(2u, GetRenderFrameHostCount(main_frame));
+
+  // Enable JavaScript and load the same page.
+  HostContentSettingsMapFactory::GetForProfile(browser()->profile())
+      ->SetDefaultContentSetting(ContentSettingsType::JAVASCRIPT,
+                                 CONTENT_SETTING_DEFAULT);
+  main_frame = ui_test_utils::NavigateToURLWithDisposition(
+      browser(), url, WindowOpenDisposition::NEW_FOREGROUND_TAB,
+      ui_test_utils::BROWSER_TEST_WAIT_FOR_LOAD_STOP);
+  ASSERT_TRUE(main_frame);
+  // Ensure 3 frames exist after the load (main frame, 'b.test' frame and
+  // JavaScript-created 'b.test' nested frame).
+  EXPECT_EQ(3u, GetRenderFrameHostCount(main_frame));
+
+  // Disable JavaScript and reload the iframe which contains JavaScript.
+  HostContentSettingsMapFactory::GetForProfile(browser()->profile())
+      ->SetDefaultContentSetting(ContentSettingsType::JAVASCRIPT,
+                                 CONTENT_SETTING_BLOCK);
+  content::RenderFrameHost* iframe_to_reload =
+      content::ChildFrameAt(main_frame, 0);
+  content::TestFrameNavigationObserver iframe_nav_observer(iframe_to_reload);
+  iframe_to_reload->Reload();
+  iframe_nav_observer.Wait();
+
+  // Ensure 2 frames exist after iframe reload (main frame and 'b.test' frame).
+  EXPECT_EQ(2u, GetRenderFrameHostCount(main_frame));
+}
+
 class ContentSettingsWorkerModulesBrowserTest : public ContentSettingsTest {
  public:
   ContentSettingsWorkerModulesBrowserTest() = default;
diff --git a/chrome/browser/flag-metadata.json b/chrome/browser/flag-metadata.json
index 66cf237..e8c5ac5 100644
--- a/chrome/browser/flag-metadata.json
+++ b/chrome/browser/flag-metadata.json
@@ -4216,12 +4216,12 @@
   {
     "name": "notification-scheduler",
     "owners": [ "//chrome/browser/notifications/scheduler/OWNERS" ],
-    "expiry_milestone": 100
+    "expiry_milestone": 102
   },
   {
     "name": "notification-scheduler-debug-options",
     "owners": [ "//chrome/browser/notifications/scheduler/OWNERS" ],
-    "expiry_milestone": 100
+    "expiry_milestone": 106
   },
   {
     "name": "ntp-cache-one-google-bar",
diff --git a/chromeos/lacros/crosapi_pref_observer.cc b/chrome/browser/lacros/crosapi_pref_observer.cc
similarity index 93%
rename from chromeos/lacros/crosapi_pref_observer.cc
rename to chrome/browser/lacros/crosapi_pref_observer.cc
index 5d871195..b4c1e51 100644
--- a/chromeos/lacros/crosapi_pref_observer.cc
+++ b/chrome/browser/lacros/crosapi_pref_observer.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 "chromeos/lacros/crosapi_pref_observer.h"
+#include "chrome/browser/lacros/crosapi_pref_observer.h"
 
 #include "base/callback.h"
 #include "chromeos/lacros/lacros_service.h"
diff --git a/chromeos/lacros/crosapi_pref_observer.h b/chrome/browser/lacros/crosapi_pref_observer.h
similarity index 79%
rename from chromeos/lacros/crosapi_pref_observer.h
rename to chrome/browser/lacros/crosapi_pref_observer.h
index 8ede195..79a8a41 100644
--- a/chromeos/lacros/crosapi_pref_observer.h
+++ b/chrome/browser/lacros/crosapi_pref_observer.h
@@ -2,11 +2,10 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CHROMEOS_LACROS_CROSAPI_PREF_OBSERVER_H_
-#define CHROMEOS_LACROS_CROSAPI_PREF_OBSERVER_H_
+#ifndef CHROME_BROWSER_LACROS_CROSAPI_PREF_OBSERVER_H_
+#define CHROME_BROWSER_LACROS_CROSAPI_PREF_OBSERVER_H_
 
 #include "base/callback_forward.h"
-#include "base/component_export.h"
 #include "base/gtest_prod_util.h"
 #include "base/values.h"
 #include "chromeos/crosapi/mojom/prefs.mojom.h"
@@ -14,8 +13,7 @@
 
 // Helper to simplify the crosapi::mojom::PrefObserver API.
 // Observes ash-chrome for changes in specified pref.
-class COMPONENT_EXPORT(CHROMEOS_LACROS) CrosapiPrefObserver
-    : public crosapi::mojom::PrefObserver {
+class CrosapiPrefObserver : public crosapi::mojom::PrefObserver {
  public:
   using PrefChangedCallback = base::RepeatingCallback<void(base::Value value)>;
 
@@ -37,4 +35,4 @@
   mojo::Receiver<crosapi::mojom::PrefObserver> receiver_{this};
 };
 
-#endif  // CHROMEOS_LACROS_CROSAPI_PREF_OBSERVER_H_
+#endif  // CHROME_BROWSER_LACROS_CROSAPI_PREF_OBSERVER_H_
diff --git a/chrome/browser/lacros/crosapi_pref_observer_lacros_browsertest.cc b/chrome/browser/lacros/crosapi_pref_observer_lacros_browsertest.cc
index 9fae07c3..801294a 100644
--- a/chrome/browser/lacros/crosapi_pref_observer_lacros_browsertest.cc
+++ b/chrome/browser/lacros/crosapi_pref_observer_lacros_browsertest.cc
@@ -5,8 +5,8 @@
 #include "base/run_loop.h"
 #include "base/test/bind.h"
 #include "base/values.h"
+#include "chrome/browser/lacros/crosapi_pref_observer.h"
 #include "chrome/test/base/in_process_browser_test.h"
-#include "chromeos/lacros/crosapi_pref_observer.h"
 #include "content/public/test/browser_test.h"
 
 using CrosapiPrefObserverLacrosBrowserTest = InProcessBrowserTest;
diff --git a/chrome/browser/lacros/prefs_ash_observer.h b/chrome/browser/lacros/prefs_ash_observer.h
index 2b8b838e..87f00274 100644
--- a/chrome/browser/lacros/prefs_ash_observer.h
+++ b/chrome/browser/lacros/prefs_ash_observer.h
@@ -8,7 +8,7 @@
 #include <memory>
 
 #include "base/gtest_prod_util.h"
-#include "chromeos/lacros/crosapi_pref_observer.h"
+#include "chrome/browser/lacros/crosapi_pref_observer.h"
 #include "components/prefs/pref_service.h"
 
 // Observes ash-chrome for changes in the secure DNS preferences.
diff --git a/chrome/browser/optimization_guide/optimization_guide_keyed_service.cc b/chrome/browser/optimization_guide/optimization_guide_keyed_service.cc
index b588b450..79e3d249 100644
--- a/chrome/browser/optimization_guide/optimization_guide_keyed_service.cc
+++ b/chrome/browser/optimization_guide/optimization_guide_keyed_service.cc
@@ -9,18 +9,21 @@
 #include "base/files/file_util.h"
 #include "base/metrics/histogram_functions.h"
 #include "base/metrics/histogram_macros.h"
+#include "base/path_service.h"
 #include "base/task/sequenced_task_runner.h"
 #include "base/task/task_traits.h"
 #include "base/task/thread_pool.h"
 #include "build/build_config.h"
 #include "build/chromeos_buildflags.h"
 #include "chrome/browser/browser_process.h"
+#include "chrome/browser/download/background_download_service_factory.h"
 #include "chrome/browser/metrics/chrome_metrics_service_accessor.h"
 #include "chrome/browser/optimization_guide/chrome_hints_manager.h"
 #include "chrome/browser/optimization_guide/optimization_guide_keyed_service_factory.h"
-#include "chrome/browser/optimization_guide/prediction/prediction_manager.h"
 #include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/profiles/profile_key.h"
 #include "chrome/browser/profiles/profile_manager.h"
+#include "chrome/common/chrome_paths.h"
 #include "components/leveldb_proto/public/proto_database_provider.h"
 #include "components/optimization_guide/core/command_line_top_host_provider.h"
 #include "components/optimization_guide/core/hints_processing_util.h"
@@ -33,6 +36,7 @@
 #include "components/optimization_guide/core/optimization_guide_store.h"
 #include "components/optimization_guide/core/optimization_guide_switches.h"
 #include "components/optimization_guide/core/optimization_guide_util.h"
+#include "components/optimization_guide/core/prediction_manager.h"
 #include "components/optimization_guide/core/tab_url_provider.h"
 #include "components/optimization_guide/core/top_host_provider.h"
 #include "components/optimization_guide/proto/models.pb.h"
@@ -155,6 +159,12 @@
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
 }
 
+download::BackgroundDownloadService*
+OptimizationGuideKeyedService::BackgroundDownloadServiceProvider() {
+  Profile* profile = Profile::FromBrowserContext(browser_context_);
+  return BackgroundDownloadServiceFactory::GetForKey(profile->GetProfileKey());
+}
+
 void OptimizationGuideKeyedService::Initialize() {
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
 
@@ -238,9 +248,20 @@
       tab_url_provider_.get(), url_loader_factory,
       MaybeCreatePushNotificationManager(profile),
       optimization_guide_logger_.get());
+  base::FilePath models_dir;
+  base::PathService::Get(chrome::DIR_OPTIMIZATION_GUIDE_PREDICTION_MODELS,
+                         &models_dir);
+
   prediction_manager_ = std::make_unique<optimization_guide::PredictionManager>(
       prediction_model_and_features_store, url_loader_factory,
-      profile->GetPrefs(), profile, optimization_guide_logger_.get());
+      profile->GetPrefs(), profile->IsOffTheRecord(),
+      g_browser_process->GetApplicationLocale(), models_dir,
+      optimization_guide_logger_.get(),
+      base::BindOnce(
+          &OptimizationGuideKeyedService::BackgroundDownloadServiceProvider,
+          // It's safe to use |base::Unretained(this)| here because
+          // |this| owns |prediction_manager_|.
+          base::Unretained(this)));
 
   // The previous store paths were written in incorrect locations. Delete the
   // old paths. Remove this code in 04/2022 since it should be assumed that all
diff --git a/chrome/browser/optimization_guide/optimization_guide_keyed_service.h b/chrome/browser/optimization_guide/optimization_guide_keyed_service.h
index b6f0a13d..ff80109 100644
--- a/chrome/browser/optimization_guide/optimization_guide_keyed_service.h
+++ b/chrome/browser/optimization_guide/optimization_guide_keyed_service.h
@@ -25,6 +25,10 @@
 class NavigationHandle;
 }  // namespace content
 
+namespace download {
+class BackgroundDownloadService;
+}  // namespace download
+
 namespace optimization_guide {
 namespace android {
 class AndroidPushNotificationManagerJavaTest;
@@ -170,6 +174,8 @@
       optimization_guide::OnDemandOptimizationGuideDecisionRepeatingCallback
           callback) override;
 
+  download::BackgroundDownloadService* BackgroundDownloadServiceProvider();
+
   raw_ptr<content::BrowserContext> browser_context_;
 
   // The store of hints.
diff --git a/chrome/browser/optimization_guide/prediction/prediction_manager_browsertest.cc b/chrome/browser/optimization_guide/prediction/prediction_manager_browsertest.cc
index 2e23ce1..3310b86 100644
--- a/chrome/browser/optimization_guide/prediction/prediction_manager_browsertest.cc
+++ b/chrome/browser/optimization_guide/prediction/prediction_manager_browsertest.cc
@@ -18,7 +18,6 @@
 #include "chrome/browser/optimization_guide/browser_test_util.h"
 #include "chrome/browser/optimization_guide/optimization_guide_keyed_service.h"
 #include "chrome/browser/optimization_guide/optimization_guide_keyed_service_factory.h"
-#include "chrome/browser/optimization_guide/prediction/prediction_manager.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/profiles/profile_key.h"
 #include "chrome/browser/profiles/profile_manager.h"
@@ -32,6 +31,7 @@
 #include "components/optimization_guide/core/optimization_guide_store.h"
 #include "components/optimization_guide/core/optimization_guide_switches.h"
 #include "components/optimization_guide/core/optimization_guide_test_util.h"
+#include "components/optimization_guide/core/prediction_manager.h"
 #include "components/optimization_guide/core/store_update_data.h"
 #include "components/optimization_guide/proto/models.pb.h"
 #include "components/prefs/pref_service.h"
diff --git a/chrome/browser/optimization_guide/prediction/prediction_model_download_client.cc b/chrome/browser/optimization_guide/prediction/prediction_model_download_client.cc
index 231de3c2..2bec965 100644
--- a/chrome/browser/optimization_guide/prediction/prediction_model_download_client.cc
+++ b/chrome/browser/optimization_guide/prediction/prediction_model_download_client.cc
@@ -9,9 +9,9 @@
 #include "base/threading/sequenced_task_runner_handle.h"
 #include "chrome/browser/optimization_guide/optimization_guide_keyed_service.h"
 #include "chrome/browser/optimization_guide/optimization_guide_keyed_service_factory.h"
-#include "chrome/browser/optimization_guide/prediction/prediction_manager.h"
-#include "chrome/browser/optimization_guide/prediction/prediction_model_download_manager.h"
 #include "components/download/public/background_service/download_metadata.h"
+#include "components/optimization_guide/core/prediction_manager.h"
+#include "components/optimization_guide/core/prediction_model_download_manager.h"
 #include "services/network/public/cpp/resource_request_body.h"
 
 namespace optimization_guide {
diff --git a/chrome/browser/policy/test/policy_test_google_browsertest.cc b/chrome/browser/policy/test/policy_test_google_browsertest.cc
index 5d0b49e..39f37d9 100644
--- a/chrome/browser/policy/test/policy_test_google_browsertest.cc
+++ b/chrome/browser/policy/test/policy_test_google_browsertest.cc
@@ -57,7 +57,8 @@
   EXPECT_EQ(header, allowed_domain);
 }
 
-class PolicyTestGoogle : public SafeSearchPolicyTest {
+class PolicyTestGoogle : public SafeSearchPolicyTest,
+                         public testing::WithParamInterface<bool> {
  public:
   PolicyTestGoogle() : https_server_(net::EmbeddedTestServer::TYPE_HTTPS) {}
 
@@ -68,6 +69,14 @@
     return urls_requested_;
   }
 
+  Browser* GetBrowser() {
+    if (!is_incognito())
+      return browser();
+    if (!incognito_browser_)
+      incognito_browser_ = CreateIncognitoBrowser(browser()->profile());
+    return incognito_browser_;
+  }
+
  private:
   void SetUpOnMainThread() override {
     SafeSearchPolicyTest::SetUpOnMainThread();
@@ -103,34 +112,40 @@
     command_line->AppendSwitch(switches::kIgnoreGooglePortNumbers);
   }
 
+  bool is_incognito() const { return GetParam(); }
+
   net::EmbeddedTestServer https_server_;
   base::Lock lock_;
   std::map<std::string, net::HttpRequestHeaders> urls_requested_;
+  raw_ptr<Browser> incognito_browser_ = nullptr;
 };
 
-IN_PROC_BROWSER_TEST_F(PolicyTestGoogle, ForceGoogleSafeSearch) {
-  ApplySafeSearchPolicy(absl::nullopt,  // ForceSafeSearch
+INSTANTIATE_TEST_SUITE_P(, PolicyTestGoogle, ::testing::Bool());
+
+IN_PROC_BROWSER_TEST_P(PolicyTestGoogle, ForceGoogleSafeSearch) {
+  ApplySafeSearchPolicy(absl::nullopt,  // ForceSafeSearch (legacy)
                         base::Value(true),
-                        absl::nullopt,   // ForceYouTubeSafetyMode
+                        absl::nullopt,   // ForceYouTubeSafetyMode (legacy)
                         absl::nullopt);  // ForceYouTubeRestrict
 
   GURL url = https_server()->GetURL("www.google.com",
                                     "/server-redirect?http://google.com/");
-  CheckSafeSearch(browser(), true, url.spec());
+  CheckSafeSearch(GetBrowser(), true, url.spec());
 }
 
-IN_PROC_BROWSER_TEST_F(PolicyTestGoogle, ForceYouTubeRestrict) {
+IN_PROC_BROWSER_TEST_P(PolicyTestGoogle, ForceYouTubeRestrict) {
+  GURL youtube_url(https_server()->GetURL("youtube.com", "/empty.html"));
+  GURL youtube_script(https_server()->GetURL("youtube.com", "/json2.js"));
   for (int youtube_restrict_mode = safe_search_util::YOUTUBE_RESTRICT_OFF;
        youtube_restrict_mode < safe_search_util::YOUTUBE_RESTRICT_COUNT;
        ++youtube_restrict_mode) {
-    ApplySafeSearchPolicy(absl::nullopt,  // ForceSafeSearch
+    ApplySafeSearchPolicy(absl::nullopt,  // ForceSafeSearch (legacy)
                           absl::nullopt,  // ForceGoogleSafeSearch
-                          absl::nullopt,  // ForceYouTubeSafetyMode
+                          absl::nullopt,  // ForceYouTubeSafetyMode (legacy)
                           base::Value(youtube_restrict_mode));
     {
       // First check frame requests.
-      GURL youtube_url(https_server()->GetURL("youtube.com", "/empty.html"));
-      ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), youtube_url));
+      ASSERT_TRUE(ui_test_utils::NavigateToURL(GetBrowser(), youtube_url));
 
       CheckYouTubeRestricted(youtube_restrict_mode,
                              urls_requested()[youtube_url.path()]);
@@ -138,17 +153,31 @@
 
     {
       // Now check subresource loads.
-      GURL youtube_script(https_server()->GetURL("youtube.com", "/json2.js"));
-      FetchSubresource(browser()->tab_strip_model()->GetActiveWebContents(),
+      FetchSubresource(GetBrowser()->tab_strip_model()->GetActiveWebContents(),
                        youtube_script);
 
       CheckYouTubeRestricted(youtube_restrict_mode,
                              urls_requested()[youtube_script.path()]);
     }
+
+    if (youtube_restrict_mode != safe_search_util::YOUTUBE_RESTRICT_OFF) {
+      // If a restriction is active, disable it while the page is open to check
+      // that renderer rules are properly updated when a renderer is running.
+      ApplySafeSearchPolicy(
+          absl::nullopt,  // ForceSafeSearch (legacy)
+          absl::nullopt,  // ForceGoogleSafeSearch
+          absl::nullopt,  // ForceYouTubeSafetyMode (legacy)
+          base::Value(safe_search_util::YOUTUBE_RESTRICT_OFF));
+      FetchSubresource(GetBrowser()->tab_strip_model()->GetActiveWebContents(),
+                       youtube_script);
+
+      CheckYouTubeRestricted(safe_search_util::YOUTUBE_RESTRICT_OFF,
+                             urls_requested()[youtube_script.path()]);
+    }
   }
 }
 
-IN_PROC_BROWSER_TEST_F(PolicyTestGoogle, AllowedDomainsForApps) {
+IN_PROC_BROWSER_TEST_P(PolicyTestGoogle, AllowedDomainsForApps) {
   for (int allowed_domains = 0; allowed_domains < 2; ++allowed_domains) {
     std::string allowed_domain;
     if (allowed_domains) {
@@ -162,7 +191,7 @@
     {
       // First check frame requests.
       GURL google_url = https_server()->GetURL("google.com", "/empty.html");
-      ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), google_url));
+      ASSERT_TRUE(ui_test_utils::NavigateToURL(GetBrowser(), google_url));
 
       CheckAllowedDomainsHeader(allowed_domain,
                                 urls_requested()[google_url.path()]);
@@ -173,7 +202,7 @@
       GURL google_script =
           https_server()->GetURL("google.com", "/result_queue.js");
 
-      FetchSubresource(browser()->tab_strip_model()->GetActiveWebContents(),
+      FetchSubresource(GetBrowser()->tab_strip_model()->GetActiveWebContents(),
                        google_script);
 
       CheckAllowedDomainsHeader(allowed_domain,
@@ -183,7 +212,7 @@
     {
       // Double check that a frame to a non-Google url doesn't have the header.
       GURL non_google_url = https_server()->GetURL("/empty.html");
-      ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), non_google_url));
+      ASSERT_TRUE(ui_test_utils::NavigateToURL(GetBrowser(), non_google_url));
 
       CheckAllowedDomainsHeader(std::string(),
                                 urls_requested()[non_google_url.path()]);
diff --git a/chrome/browser/profiles/renderer_updater.cc b/chrome/browser/profiles/renderer_updater.cc
index a2c7b9c4..82bbd5d 100644
--- a/chrome/browser/profiles/renderer_updater.cc
+++ b/chrome/browser/profiles/renderer_updater.cc
@@ -16,6 +16,7 @@
 #include "chrome/common/renderer_configuration.mojom.h"
 #include "components/content_settings/common/content_settings_manager.mojom.h"
 #include "components/content_settings/core/browser/content_settings_utils.h"
+#include "components/content_settings/core/common/content_settings.h"
 #include "components/content_settings/core/common/content_settings_utils.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/render_process_host.h"
@@ -57,21 +58,50 @@
 }
 
 #endif  // BUILDFLAG(ENABLE_EXTENSIONS)
+
+bool IsRendererSupportedContentSettingsTypeSet(
+    ContentSettingsTypeSet content_type_set) {
+  // ContainsAllTypes() signals that multiple content settings may have been
+  // updated, e.g. by the PolicyProvider. This should always be sent to the
+  // renderer in case a relevant setting is updated.
+  if (content_type_set.ContainsAllTypes())
+    return true;
+
+  return RendererContentSettingRules::IsRendererContentSetting(
+      content_type_set.GetType());
+}
+
+bool ShouldSendUpdatedContentSettingsRulesToRenderer(
+    content::RenderProcessHost* render_process_host) {
+  // Some renderers use manually-crafted rules that aren't meant to be updated.
+  return !render_process_host->IsForGuestsOnly() &&
+         !render_process_host->IsPdf();
+}
+
 }  // namespace
 
-RendererUpdater::RendererUpdater(Profile* profile) : profile_(profile) {
-  identity_manager_ = IdentityManagerFactory::GetForProfile(profile);
-  identity_manager_observation_.Observe(identity_manager_.get());
+RendererUpdater::RendererUpdater(Profile* profile)
+    : profile_(profile),
+      is_off_the_record_(profile_->IsOffTheRecord()),
+      original_profile_(profile->GetOriginalProfile()) {
+  identity_manager_observation_.Observe(
+      IdentityManagerFactory::GetForProfile(original_profile_));
+
 #if BUILDFLAG(IS_CHROMEOS_ASH)
   oauth2_login_manager_ =
-      ash::OAuth2LoginManagerFactory::GetForProfile(profile_);
+      ash::OAuth2LoginManagerFactory::GetForProfile(original_profile_);
   oauth2_login_manager_->AddObserver(this);
   merge_session_running_ =
       ash::merge_session_throttling_utils::ShouldDelayRequestForProfile(
-          profile_);
+          original_profile_);
 #endif
 
-  PrefService* pref_service = profile->GetPrefs();
+  host_content_settings_map_ =
+      HostContentSettingsMapFactory::GetForProfile(profile_);
+  host_content_settings_map_observation_.Observe(
+      host_content_settings_map_.get());
+
+  PrefService* pref_service = profile_->GetPrefs();
   force_google_safesearch_.Init(prefs::kForceGoogleSafeSearch, pref_service);
   force_youtube_restrict_.Init(prefs::kForceYouTubeRestrict, pref_service);
   allowed_domains_for_apps_.Init(prefs::kAllowedDomainsForApps, pref_service);
@@ -80,41 +110,41 @@
   pref_change_registrar_.Add(
       prefs::kForceGoogleSafeSearch,
       base::BindRepeating(&RendererUpdater::UpdateAllRenderers,
-                          base::Unretained(this)));
+                          base::Unretained(this), kUpdateDynamicParams));
   pref_change_registrar_.Add(
       prefs::kForceYouTubeRestrict,
       base::BindRepeating(&RendererUpdater::UpdateAllRenderers,
-                          base::Unretained(this)));
+                          base::Unretained(this), kUpdateDynamicParams));
   pref_change_registrar_.Add(
       prefs::kAllowedDomainsForApps,
       base::BindRepeating(&RendererUpdater::UpdateAllRenderers,
-                          base::Unretained(this)));
+                          base::Unretained(this), kUpdateDynamicParams));
 }
 
 RendererUpdater::~RendererUpdater() {
-  DCHECK(!identity_manager_);
 #if BUILDFLAG(IS_CHROMEOS_ASH)
   DCHECK(!oauth2_login_manager_);
 #endif
+  DCHECK(!host_content_settings_map_);
 }
 
 void RendererUpdater::Shutdown() {
+  pref_change_registrar_.RemoveAll();
+  host_content_settings_map_observation_.Reset();
+  host_content_settings_map_ = nullptr;
 #if BUILDFLAG(IS_CHROMEOS_ASH)
   oauth2_login_manager_->RemoveObserver(this);
   oauth2_login_manager_ = nullptr;
 #endif
   identity_manager_observation_.Reset();
-  identity_manager_ = nullptr;
 }
 
 void RendererUpdater::InitializeRenderer(
     content::RenderProcessHost* render_process_host) {
+  DCHECK_EQ(profile_, Profile::FromBrowserContext(
+                          render_process_host->GetBrowserContext()));
   auto renderer_configuration = GetRendererConfiguration(render_process_host);
 
-  Profile* profile =
-      Profile::FromBrowserContext(render_process_host->GetBrowserContext());
-  bool is_incognito_process = profile->IsOffTheRecord();
-
   mojo::PendingReceiver<chrome::mojom::ChromeOSListener>
       chromeos_listener_receiver;
 #if BUILDFLAG(IS_CHROMEOS_ASH)
@@ -134,21 +164,21 @@
         std::make_unique<chrome::ContentSettingsManagerDelegate>());
   }
   renderer_configuration->SetInitialConfiguration(
-      is_incognito_process, std::move(chromeos_listener_receiver),
+      is_off_the_record_, std::move(chromeos_listener_receiver),
       std::move(content_settings_manager));
 
-  UpdateRenderer(&renderer_configuration);
+  renderer_configuration->SetConfiguration(CreateRendererDynamicParams());
 
   RendererContentSettingRules rules;
   if (render_process_host->IsForGuestsOnly()) {
 #if BUILDFLAG(ENABLE_EXTENSIONS)
-    GetGuestViewDefaultContentSettingRules(is_incognito_process, &rules);
+    GetGuestViewDefaultContentSettingRules(is_off_the_record_, &rules);
 #else
     NOTREACHED();
 #endif
   } else {
-    content_settings::GetRendererContentSettingRules(
-        HostContentSettingsMapFactory::GetForProfile(profile), &rules);
+    content_settings::GetRendererContentSettingRules(host_content_settings_map_,
+                                                     &rules);
 
     // Always allow scripting in PDF renderers to retain the functionality of
     // the scripted messaging proxy in between the plugins in the PDF renderers
@@ -160,29 +190,30 @@
           ContentSettingsPattern::Wildcard(),
           ContentSettingsPattern::Wildcard(),
           content_settings::ContentSettingToValue(CONTENT_SETTING_ALLOW),
-          std::string(), is_incognito_process);
+          std::string(), is_off_the_record_);
     }
   }
   renderer_configuration->SetContentSettingRules(rules);
 }
 
-std::vector<mojo::AssociatedRemote<chrome::mojom::RendererConfiguration>>
+RendererUpdater::RendererConfigurations
 RendererUpdater::GetRendererConfigurations() {
-  std::vector<mojo::AssociatedRemote<chrome::mojom::RendererConfiguration>> rv;
+  RendererConfigurations rc;
   for (content::RenderProcessHost::iterator it(
            content::RenderProcessHost::AllHostsIterator());
        !it.IsAtEnd(); it.Advance()) {
+    content::RenderProcessHost* render_process_host = it.GetCurrentValue();
     Profile* renderer_profile =
-        static_cast<Profile*>(it.GetCurrentValue()->GetBrowserContext());
-    if (renderer_profile == profile_ ||
-        renderer_profile->GetOriginalProfile() == profile_) {
+        Profile::FromBrowserContext(render_process_host->GetBrowserContext());
+    if (renderer_profile == profile_) {
       auto renderer_configuration =
-          GetRendererConfiguration(it.GetCurrentValue());
+          GetRendererConfiguration(render_process_host);
       if (renderer_configuration)
-        rv.push_back(std::move(renderer_configuration));
+        rc.push_back(std::make_pair(render_process_host,
+                                    std::move(renderer_configuration)));
     }
   }
-  return rv;
+  return rc;
 }
 
 mojo::AssociatedRemote<chrome::mojom::RendererConfiguration>
@@ -204,7 +235,7 @@
     ash::OAuth2LoginManager::SessionRestoreState state) {
   merge_session_running_ =
       ash::merge_session_throttling_utils::ShouldDelayRequestForProfile(
-          profile_);
+          original_profile_);
   if (merge_session_running_)
     return;
 
@@ -220,21 +251,54 @@
       signin::PrimaryAccountChangeEvent::Type::kNone) {
     return;
   }
-  UpdateAllRenderers();
+  UpdateAllRenderers(kUpdateDynamicParams);
 }
 
-void RendererUpdater::UpdateAllRenderers() {
+void RendererUpdater::OnContentSettingChanged(
+    const ContentSettingsPattern& primary_pattern,
+    const ContentSettingsPattern& secondary_pattern,
+    ContentSettingsTypeSet content_type_set) {
+  // Do not send updates for non-renderer supported types.
+  if (!IsRendererSupportedContentSettingsTypeSet(content_type_set)) {
+    return;
+  }
+
+  // Send updates to all RenderProcessHosts.
+  UpdateAllRenderers(kUpdateContentSettings);
+}
+
+void RendererUpdater::UpdateAllRenderers(UpdateTypes update_types) {
+  chrome::mojom::DynamicParamsPtr dynamic_params;
+  if (update_types & kUpdateDynamicParams) {
+    dynamic_params = CreateRendererDynamicParams();
+  }
+
+  RendererContentSettingRules rules;
+  if (update_types & kUpdateContentSettings) {
+    content_settings::GetRendererContentSettingRules(host_content_settings_map_,
+                                                     &rules);
+  }
+
   auto renderer_configurations = GetRendererConfigurations();
-  for (auto& renderer_configuration : renderer_configurations)
-    UpdateRenderer(&renderer_configuration);
+  for (auto& renderer_configuration : renderer_configurations) {
+    content::RenderProcessHost* render_process_host =
+        renderer_configuration.first;
+    if (!render_process_host->IsInitializedAndNotDead())
+      continue;
+
+    if (update_types & kUpdateDynamicParams) {
+      renderer_configuration.second->SetConfiguration(dynamic_params.Clone());
+    }
+    if (update_types & kUpdateContentSettings &&
+        ShouldSendUpdatedContentSettingsRulesToRenderer(render_process_host)) {
+      renderer_configuration.second->SetContentSettingRules(rules);
+    }
+  }
 }
 
-void RendererUpdater::UpdateRenderer(
-    mojo::AssociatedRemote<chrome::mojom::RendererConfiguration>*
-        renderer_configuration) {
-  (*renderer_configuration)
-      ->SetConfiguration(chrome::mojom::DynamicParams::New(
-          force_google_safesearch_.GetValue(),
-          force_youtube_restrict_.GetValue(),
-          allowed_domains_for_apps_.GetValue()));
+chrome::mojom::DynamicParamsPtr RendererUpdater::CreateRendererDynamicParams()
+    const {
+  return chrome::mojom::DynamicParams::New(
+      force_google_safesearch_.GetValue(), force_youtube_restrict_.GetValue(),
+      allowed_domains_for_apps_.GetValue());
 }
diff --git a/chrome/browser/profiles/renderer_updater.h b/chrome/browser/profiles/renderer_updater.h
index b4ebc62..89db17b 100644
--- a/chrome/browser/profiles/renderer_updater.h
+++ b/chrome/browser/profiles/renderer_updater.h
@@ -11,6 +11,7 @@
 #include "base/scoped_observation.h"
 #include "build/chromeos_buildflags.h"
 #include "chrome/common/renderer_configuration.mojom-forward.h"
+#include "components/content_settings/core/browser/host_content_settings_map.h"
 #include "components/keyed_service/core/keyed_service.h"
 #include "components/prefs/pref_change_registrar.h"
 #include "components/prefs/pref_member.h"
@@ -33,7 +34,8 @@
 #if BUILDFLAG(IS_CHROMEOS_ASH)
                         public ash::OAuth2LoginManager::Observer,
 #endif
-                        public signin::IdentityManager::Observer {
+                        public signin::IdentityManager::Observer,
+                        public content_settings::Observer {
  public:
   explicit RendererUpdater(Profile* profile);
   RendererUpdater(const RendererUpdater&) = delete;
@@ -47,8 +49,17 @@
   void InitializeRenderer(content::RenderProcessHost* render_process_host);
 
  private:
-  std::vector<mojo::AssociatedRemote<chrome::mojom::RendererConfiguration>>
-  GetRendererConfigurations();
+  enum UpdateTypes {
+    kUpdateDynamicParams = 1 << 0,
+    kUpdateContentSettings = 1 << 1,
+  };
+
+  using RendererConfigurations = std::vector<
+      std::pair<content::RenderProcessHost*,
+                mojo::AssociatedRemote<chrome::mojom::RendererConfiguration>>>;
+
+  // Returns active mojo interfaces to RendererConfiguration endpoints.
+  RendererConfigurations GetRendererConfigurations();
 
   mojo::AssociatedRemote<chrome::mojom::RendererConfiguration>
   GetRendererConfiguration(content::RenderProcessHost* render_process_host);
@@ -64,32 +75,41 @@
   void OnPrimaryAccountChanged(
       const signin::PrimaryAccountChangeEvent& event) override;
 
+  // content_settings::Observer:
+  void OnContentSettingChanged(
+      const ContentSettingsPattern& primary_pattern,
+      const ContentSettingsPattern& secondary_pattern,
+      ContentSettingsTypeSet content_type_set) override;
+
   // Update all renderers due to a configuration change.
-  void UpdateAllRenderers();
+  void UpdateAllRenderers(UpdateTypes update_types);
 
-  // Update the given renderer due to a configuration change.
-  void UpdateRenderer(
-      mojo::AssociatedRemote<chrome::mojom::RendererConfiguration>*
-          renderer_configuration);
+  // Create renderer configuration that changes at runtime.
+  chrome::mojom::DynamicParamsPtr CreateRendererDynamicParams() const;
 
-  raw_ptr<Profile> profile_;
-  PrefChangeRegistrar pref_change_registrar_;
+  const raw_ptr<Profile> profile_;
+  const bool is_off_the_record_;
+  const raw_ptr<Profile> original_profile_;
+
+  base::ScopedObservation<signin::IdentityManager,
+                          signin::IdentityManager::Observer>
+      identity_manager_observation_{this};
 #if BUILDFLAG(IS_CHROMEOS_ASH)
   ash::OAuth2LoginManager* oauth2_login_manager_;
   bool merge_session_running_;
   std::vector<mojo::Remote<chrome::mojom::ChromeOSListener>>
       chromeos_listeners_;
 #endif
+  raw_ptr<HostContentSettingsMap> host_content_settings_map_;
+  base::ScopedObservation<HostContentSettingsMap, content_settings::Observer>
+      host_content_settings_map_observation_{this};
+
+  PrefChangeRegistrar pref_change_registrar_;
 
   // Prefs that we sync to the renderers.
   BooleanPrefMember force_google_safesearch_;
   IntegerPrefMember force_youtube_restrict_;
   StringPrefMember allowed_domains_for_apps_;
-
-  base::ScopedObservation<signin::IdentityManager,
-                          signin::IdentityManager::Observer>
-      identity_manager_observation_{this};
-  raw_ptr<signin::IdentityManager> identity_manager_;
 };
 
 #endif  // CHROME_BROWSER_PROFILES_RENDERER_UPDATER_H_
diff --git a/chrome/browser/profiles/renderer_updater_factory.cc b/chrome/browser/profiles/renderer_updater_factory.cc
index d3b9ab5..1079d0d 100644
--- a/chrome/browser/profiles/renderer_updater_factory.cc
+++ b/chrome/browser/profiles/renderer_updater_factory.cc
@@ -4,6 +4,8 @@
 
 #include "chrome/browser/profiles/renderer_updater_factory.h"
 
+#include "chrome/browser/content_settings/host_content_settings_map_factory.h"
+#include "chrome/browser/profiles/incognito_helpers.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/profiles/renderer_updater.h"
 #include "chrome/browser/signin/identity_manager_factory.h"
@@ -14,6 +16,7 @@
           "RendererUpdater",
           BrowserContextDependencyManager::GetInstance()) {
   DependsOn(IdentityManagerFactory::GetInstance());
+  DependsOn(HostContentSettingsMapFactory::GetInstance());
 }
 
 RendererUpdaterFactory::~RendererUpdaterFactory() {}
@@ -37,3 +40,8 @@
 bool RendererUpdaterFactory::ServiceIsCreatedWithBrowserContext() const {
   return true;
 }
+
+content::BrowserContext* RendererUpdaterFactory::GetBrowserContextToUse(
+    content::BrowserContext* context) const {
+  return chrome::GetBrowserContextOwnInstanceInIncognito(context);
+}
diff --git a/chrome/browser/profiles/renderer_updater_factory.h b/chrome/browser/profiles/renderer_updater_factory.h
index 3d4267c8..89e441c 100644
--- a/chrome/browser/profiles/renderer_updater_factory.h
+++ b/chrome/browser/profiles/renderer_updater_factory.h
@@ -28,6 +28,8 @@
   // BrowserContextKeyedServiceFactory:
   KeyedService* BuildServiceInstanceFor(
       content::BrowserContext* profile) const override;
+  content::BrowserContext* GetBrowserContextToUse(
+      content::BrowserContext* context) const override;
   bool ServiceIsCreatedWithBrowserContext() const override;
 
  private:
diff --git a/chrome/browser/resources/settings/chromeos/BUILD.gn b/chrome/browser/resources/settings/chromeos/BUILD.gn
index 103a292d..1b5e09f 100644
--- a/chrome/browser/resources/settings/chromeos/BUILD.gn
+++ b/chrome/browser/resources/settings/chromeos/BUILD.gn
@@ -42,6 +42,7 @@
     deps = [
       ":copy_browser_settings_tsc",
       ":preprocess_gen_v3",
+      ":preprocess_mojo_v3",
       ":preprocess_v3",
       "../../../../../ui/webui/resources:preprocess",
       "../../nearby_share/shared:preprocess_v3",
@@ -93,18 +94,18 @@
 # OS Settings specific mojo files, bundled in optimized builds.
 preprocess_if_expr("preprocess_mojo_v3") {
   deps = [
-    "//chrome/browser/ui/webui/settings/ash/search:mojo_bindings_js",
     "//chrome/browser/ui/webui/settings/chromeos/constants:mojom_js",
+    "//chrome/browser/ui/webui/settings/chromeos/search:mojo_bindings_js",
   ]
-  in_folder = get_path_info("../../../ui/webui/settings/", "gen_dir")
+  in_folder = get_path_info("../../../ui/webui/settings/chromeos/", "gen_dir")
   out_folder = "$target_gen_dir/$preprocess_folder_v3"
   out_manifest = "$target_gen_dir/$preprocess_mojo_manifest"
   in_files = [
-    "ash/search/search.mojom-lite.js",
-    "ash/search/search_result_icon.mojom-lite.js",
-    "ash/search/user_action_recorder.mojom-lite.js",
-    "chromeos/constants/routes.mojom-lite.js",
-    "chromeos/constants/setting.mojom-lite.js",
+    "constants/routes.mojom-lite.js",
+    "constants/setting.mojom-lite.js",
+    "search/search.mojom-lite.js",
+    "search/search_result_icon.mojom-lite.js",
+    "search/user_action_recorder.mojom-lite.js",
   ]
 }
 
@@ -200,7 +201,6 @@
   input_files_base_dir = rebase_path(".", "//")
   deps = [
     ":preprocess_external_mojo",
-    ":preprocess_mojo_v3",
     "../../nearby_share:build_mojo_grdp",
   ]
   grdp_files = [ "$root_gen_dir/chrome/browser/resources/nearby_share/nearby_share_mojo_resources.grdp" ]
@@ -213,11 +213,6 @@
     "ui/webui/resources/cr_components/app_management/app_management.mojom-lite.js|app-management/app_management.mojom-lite.js",
     "chrome/browser/ui/webui/settings/ash/os_apps_page/mojom/app_notification_handler.mojom-lite.js|os_apps_page/app_notification_handler.mojom-lite.js",
     "components/services/app_service/public/mojom/types.mojom-lite.js|app-management/types.mojom-lite.js",
-    "ash/search/search.mojom-lite.js|search/search.mojom-lite.js",
-    "ash/search/search_result_icon.mojom-lite.js|search/search_result_icon.mojom-lite.js",
-    "ash/search/user_action_recorder.mojom-lite.js|search/user_action_recorder.mojom-lite.js",
-    "chromeos/constants/routes.mojom-lite.js|constants/routes.mojom-lite.js",
-    "chromeos/constants/setting.mojom-lite.js|constants/setting.mojom-lite.js",
     "../../nearby_share/shared/nearby_share_progress_bar_dark.json|nearby_share_progress_bar_dark.json",
     "../../nearby_share/shared/nearby_share_progress_bar_light.json|nearby_share_progress_bar_light.json",
     "../../nearby_share/shared/nearby_share_pulse_animation_dark.json|nearby_share_pulse_animation_dark.json",
@@ -239,6 +234,7 @@
     deps += [
       ":copy_browser_settings_tsc",
       ":preprocess_gen_v3",
+      ":preprocess_mojo_v3",
       ":preprocess_v3",
       "../../nearby_share/shared:build_v3_grdp",
       "//ui/webui/resources/cr_components/app_management:build_ts",
@@ -710,7 +706,7 @@
 
 js_library("metrics_recorder") {
   sources = [ "metrics_recorder.m.js" ]
-  deps = [ "//chrome/browser/ui/webui/settings/ash/search:mojo_bindings_js_library_for_compile" ]
+  deps = [ "//chrome/browser/ui/webui/settings/chromeos/search:mojo_bindings_js_library_for_compile" ]
 }
 
 js_library("os_icons.m") {
@@ -777,7 +773,7 @@
 
 js_library("search_handler") {
   deps = [
-    "//chrome/browser/ui/webui/settings/ash/search:mojo_bindings_js_library_for_compile",
+    "//chrome/browser/ui/webui/settings/chromeos/search:mojo_bindings_js_library_for_compile",
     "//ui/webui/resources/js:cr.m",
   ]
 }
diff --git a/chrome/browser/resources/settings/chromeos/internet_page/internet_subpage.js b/chrome/browser/resources/settings/chromeos/internet_page/internet_subpage.js
index 473cec53..adfda9b 100644
--- a/chrome/browser/resources/settings/chromeos/internet_page/internet_subpage.js
+++ b/chrome/browser/resources/settings/chromeos/internet_page/internet_subpage.js
@@ -1033,8 +1033,12 @@
     const alwaysOnVpnList = this.networkStateList_.slice();
     for (const vpnList of Object.values(this.thirdPartyVpns_)) {
       assert(vpnList.length > 0);
-      // ARC VPNs are excluded from always-on VPN for now.
-      if (vpnList[0].typeState.vpn.type === mojom.VpnType.kArc) {
+      // Exclude incompatible VPN technologies:
+      // - TODO(b/188864779): ARC VPNs are not supported yet,
+      // - Chrome VPN apps are deprecated and incompatible with lockdown mode
+      //   (see b/206910855).
+      if (vpnList[0].typeState.vpn.type === mojom.VpnType.kArc ||
+          vpnList[0].typeState.vpn.type === mojom.VpnType.kExtension) {
         continue;
       }
       alwaysOnVpnList.push(...vpnList);
diff --git a/chrome/browser/tracing/chrome_tracing_delegate.cc b/chrome/browser/tracing/chrome_tracing_delegate.cc
index 01b8d48a..673ba93 100644
--- a/chrome/browser/tracing/chrome_tracing_delegate.cc
+++ b/chrome/browser/tracing/chrome_tracing_delegate.cc
@@ -51,7 +51,7 @@
 #endif
 
 #if BUILDFLAG(IS_CHROMEOS_LACROS)
-#include "chromeos/lacros/crosapi_pref_observer.h"
+#include "chrome/browser/lacros/crosapi_pref_observer.h"
 #endif
 
 namespace {
diff --git a/chrome/browser/ui/BUILD.gn b/chrome/browser/ui/BUILD.gn
index 1bf5e2e..2267662 100644
--- a/chrome/browser/ui/BUILD.gn
+++ b/chrome/browser/ui/BUILD.gn
@@ -2777,13 +2777,6 @@
       "webui/settings/ash/calculator/size_calculator.h",
       "webui/settings/ash/os_apps_page/app_notification_handler.cc",
       "webui/settings/ash/os_apps_page/app_notification_handler.h",
-      "webui/settings/ash/search/per_session_settings_user_action_tracker.cc",
-      "webui/settings/ash/search/per_session_settings_user_action_tracker.h",
-      "webui/settings/ash/search/search_concept.h",
-      "webui/settings/ash/search/search_handler.cc",
-      "webui/settings/ash/search/search_handler.h",
-      "webui/settings/ash/search/search_tag_registry.cc",
-      "webui/settings/ash/search/search_tag_registry.h",
       "webui/settings/chromeos/about_section.cc",
       "webui/settings/chromeos/about_section.h",
       "webui/settings/chromeos/accessibility_handler.cc",
@@ -2897,6 +2890,13 @@
       "webui/settings/chromeos/quick_unlock_handler.h",
       "webui/settings/chromeos/reset_section.cc",
       "webui/settings/chromeos/reset_section.h",
+      "webui/settings/chromeos/search/per_session_settings_user_action_tracker.cc",
+      "webui/settings/chromeos/search/per_session_settings_user_action_tracker.h",
+      "webui/settings/chromeos/search/search_concept.h",
+      "webui/settings/chromeos/search/search_handler.cc",
+      "webui/settings/chromeos/search/search_handler.h",
+      "webui/settings/chromeos/search/search_tag_registry.cc",
+      "webui/settings/chromeos/search/search_tag_registry.h",
       "webui/settings/chromeos/search_section.cc",
       "webui/settings/chromeos/search_section.h",
       "webui/settings/chromeos/server_printer_url_util.cc",
@@ -3024,7 +3024,7 @@
       "//chrome/browser/ui/webui/nearby_share:mojom",
       "//chrome/browser/ui/webui/nearby_share/public/mojom",
       "//chrome/browser/ui/webui/settings/ash/os_apps_page/mojom",
-      "//chrome/browser/ui/webui/settings/ash/search:mojo_bindings",
+      "//chrome/browser/ui/webui/settings/chromeos/search:mojo_bindings",
       "//chrome/browser/web_applications",
       "//chrome/services/file_util/public/cpp",
       "//chromeos/assistant:buildflags",
diff --git a/chrome/browser/ui/android/management/BUILD.gn b/chrome/browser/ui/android/management/BUILD.gn
index 7550da3..6f270715 100644
--- a/chrome/browser/ui/android/management/BUILD.gn
+++ b/chrome/browser/ui/android/management/BUILD.gn
@@ -20,10 +20,10 @@
     "//chrome/browser/profiles/android:java",
     "//chrome/browser/tab:java",
     "//chrome/browser/ui/android/native_page:java",
+    "//components/browser_ui/widget/android:java",
     "//components/embedder_support/android:util_java",
     "//content/public/android:content_full_java",
     "//third_party/androidx:androidx_annotation_annotation_java",
-    "//third_party/androidx:androidx_constraintlayout_constraintlayout_java",
     "//ui/android:ui_no_recycler_view_java",
   ]
   resources_package = "org.chromium.chrome.browser.management"
@@ -35,6 +35,7 @@
     "java/res/drawable/enterprise_phone.xml",
     "java/res/drawable/enterprise_user_circle.xml",
     "java/res/layout/enterprise_management.xml",
+    "java/res/values/dimens.xml",
   ]
   deps = [
     "//chrome/browser/ui/android/strings:ui_strings_grd",
diff --git a/chrome/browser/ui/android/management/java/res/layout/enterprise_management.xml b/chrome/browser/ui/android/management/java/res/layout/enterprise_management.xml
index d3c78642..eee19105 100644
--- a/chrome/browser/ui/android/management/java/res/layout/enterprise_management.xml
+++ b/chrome/browser/ui/android/management/java/res/layout/enterprise_management.xml
@@ -10,85 +10,76 @@
     android:layout_height="match_parent"
     android:background="@macro/default_bg_color">
 
-    <androidx.constraintlayout.widget.ConstraintLayout
-        android:layout_width="match_parent"
-        android:layout_height="match_parent"
+    <LinearLayout
+        android:id="@+id/management_container"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:orientation="vertical"
         android:padding="@dimen/cm_padding">
 
-        <LinearLayout
+        <ImageView
+            android:layout_width="@dimen/cm_logo_width"
+            android:layout_height="@dimen/cm_logo_height"
+            android:src="@drawable/enterprise_icon"
+            android:contentDescription="@string/close" />
+
+        <TextView
+            android:id="@+id/title_text"
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
-            android:orientation="vertical"
-            app:layout_constraintWidth_max="@dimen/cm_max_width"
-            app:layout_constraintTop_toTopOf="parent"
-            app:layout_constraintStart_toStartOf="parent"
-            app:layout_constraintEnd_toEndOf="parent">
+            android:layout_marginTop="@dimen/cm_title_margin_top"
+            android:textAppearance="@style/TextAppearance.Headline.Primary" />
 
-            <ImageView
-                android:layout_width="@dimen/cm_logo_width"
-                android:layout_height="@dimen/cm_logo_height"
-                android:src="@drawable/enterprise_icon"
-                android:contentDescription="@string/close" />
+        <TextView
+            android:id="@+id/description_text"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_marginTop="@dimen/cm_description_margin_top"
+            android:text="@string/management_browser_notice"
+            android:textAppearance="@style/TextAppearance.TextMedium.Secondary" />
 
-            <TextView
-                android:id="@+id/title_text"
-                android:layout_width="wrap_content"
-                android:layout_height="wrap_content"
-                android:layout_marginTop="@dimen/cm_title_margin_top"
-                android:textAppearance="@style/TextAppearance.Headline.Primary" />
+        <TextView
+            android:id="@+id/learn_more"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_marginTop="@dimen/cm_learn_more_margin_top"
+            android:textAppearance="@style/TextAppearance.TextMedium.Link" />
 
-            <TextView
-                android:id="@+id/description_text"
-                android:layout_width="wrap_content"
-                android:layout_height="wrap_content"
-                android:layout_marginTop="@dimen/cm_description_margin_top"
-                android:text="@string/management_browser_notice"
-                android:textAppearance="@style/TextAppearance.TextMedium.Secondary" />
+        <TextView
+            android:id="@+id/browser_reporting"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_marginTop="@dimen/cm_browser_margin_top"
+            android:text="@string/management_browser_reporting"
+            android:textAppearance="@style/TextAppearance.TextLarge.Primary" />
 
-            <TextView
-                android:id="@+id/learn_more"
-                android:layout_width="wrap_content"
-                android:layout_height="wrap_content"
-                android:layout_marginTop="@dimen/cm_learn_more_margin_top"
-                android:textAppearance="@style/TextAppearance.TextMedium.Link" />
+        <TextView
+            android:id="@+id/browser_reporting_explanation"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:text="@string/management_browser_reporting_explanation"
+            android:textAppearance="@style/TextAppearance.TextMedium.Secondary" />
 
-            <TextView
-                android:id="@+id/browser_reporting"
-                android:layout_width="wrap_content"
-                android:layout_height="wrap_content"
-                android:layout_marginTop="@dimen/cm_browser_margin_top"
-                android:text="@string/management_browser_reporting"
-                android:textAppearance="@style/TextAppearance.TextLarge.Primary" />
+        <TextView
+            android:id="@+id/extension_report_username"
+            android:layout_width="wrap_content"
+            android:layout_height="fill_parent"
+            android:layout_marginTop="@dimen/cm_browser_item_margin_top"
+            android:drawablePadding="@dimen/cm_browser_item_drawable_padding"
+            android:text="@string/management_extension_report_username"
+            android:textAppearance="@style/TextAppearance.TextMedium.Secondary"
+            app:drawableLeftCompat="@drawable/enterprise_user_circle" />
 
-            <TextView
-                android:id="@+id/browser_reporting_explanation"
-                android:layout_width="wrap_content"
-                android:layout_height="wrap_content"
-                android:text="@string/management_browser_reporting_explanation"
-                android:textAppearance="@style/TextAppearance.TextMedium.Secondary" />
+        <TextView
+            android:id="@+id/extension_report_version"
+            android:layout_width="wrap_content"
+            android:layout_height="fill_parent"
+            android:layout_marginTop="@dimen/cm_browser_item_margin_top"
+            android:drawablePadding="@dimen/cm_browser_item_drawable_padding"
+            android:text="@string/management_extension_report_version"
+            android:textAppearance="@style/TextAppearance.TextMedium.Secondary"
+            app:drawableLeftCompat="@drawable/enterprise_phone" />
 
-            <TextView
-                android:id="@+id/extension_report_username"
-                android:layout_width="wrap_content"
-                android:layout_height="fill_parent"
-                android:layout_marginTop="@dimen/cm_browser_item_margin_top"
-                android:drawablePadding="@dimen/cm_browser_item_drawable_padding"
-                android:text="@string/management_extension_report_username"
-                android:textAppearance="@style/TextAppearance.TextMedium.Secondary"
-                app:drawableLeftCompat="@drawable/enterprise_user_circle" />
-
-            <TextView
-                android:id="@+id/extension_report_version"
-                android:layout_width="wrap_content"
-                android:layout_height="fill_parent"
-                android:layout_marginTop="@dimen/cm_browser_item_margin_top"
-                android:drawablePadding="@dimen/cm_browser_item_drawable_padding"
-                android:text="@string/management_extension_report_version"
-                android:textAppearance="@style/TextAppearance.TextMedium.Secondary"
-                app:drawableLeftCompat="@drawable/enterprise_phone" />
-
-        </LinearLayout>
-
-    </androidx.constraintlayout.widget.ConstraintLayout>
+    </LinearLayout>
 
 </org.chromium.chrome.browser.management.ManagementView>
\ No newline at end of file
diff --git a/chrome/browser/ui/android/management/java/res/values/dimens.xml b/chrome/browser/ui/android/management/java/res/values/dimens.xml
new file mode 100644
index 0000000..710d69cc
--- /dev/null
+++ b/chrome/browser/ui/android/management/java/res/values/dimens.xml
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright 2022 The Chromium Authors. All rights reserved.
+     Use of this source code is governed by a BSD-style license that can be
+     found in the LICENSE file. -->
+
+<resources xmlns:tools="http://schemas.android.com/tools">
+    <!-- Chrome Management dimensions -->
+    <dimen name="cm_padding">32dp</dimen>
+    <dimen name="cm_padding_wide">48dp</dimen>
+    <dimen name="cm_logo_width">40dp</dimen>
+    <dimen name="cm_logo_height">36dp</dimen>
+    <dimen name="cm_title_margin_top">30dp</dimen>
+    <dimen name="cm_description_margin_top">24dp</dimen>
+    <dimen name="cm_learn_more_margin_top">22dp</dimen>
+    <dimen name="cm_browser_margin_top">42dp</dimen>
+    <dimen name="cm_browser_item_margin_top">20dp</dimen>
+    <dimen name="cm_browser_item_drawable_padding">18dp</dimen>
+</resources>
\ No newline at end of file
diff --git a/chrome/browser/ui/android/management/java/src/org/chromium/chrome/browser/management/ManagementView.java b/chrome/browser/ui/android/management/java/src/org/chromium/chrome/browser/management/ManagementView.java
index cf58a76..63cd27e 100644
--- a/chrome/browser/ui/android/management/java/src/org/chromium/chrome/browser/management/ManagementView.java
+++ b/chrome/browser/ui/android/management/java/src/org/chromium/chrome/browser/management/ManagementView.java
@@ -5,15 +5,20 @@
 package org.chromium.chrome.browser.management;
 
 import android.content.Context;
+import android.content.res.Configuration;
 import android.text.SpannableString;
 import android.text.TextUtils;
 import android.text.method.LinkMovementMethod;
 import android.util.AttributeSet;
+import android.widget.LinearLayout;
 import android.widget.ScrollView;
 import android.widget.TextView;
 
 import androidx.annotation.Nullable;
 
+import org.chromium.components.browser_ui.widget.displaystyle.UiConfig;
+import org.chromium.components.browser_ui.widget.displaystyle.ViewResizer;
+
 /**
  * The View that renders the ManagementPage (chrome://management).
  * Consists of an medium size image icon over title and descriptive text.
@@ -22,6 +27,7 @@
     private boolean mIsManaged;
     private @Nullable String mManagerName;
 
+    private LinearLayout mManagementContainer;
     private TextView mTitle;
     private TextView mDescription;
     private TextView mLearnMore;
@@ -30,6 +36,9 @@
     private TextView mExtensionReportUsername;
     private TextView mExtensionReportVersion;
 
+    @Nullable
+    private UiConfig mUiConfig;
+
     /** Constructor for inflating from XML. */
     public ManagementView(Context context, AttributeSet attrs) {
         super(context, attrs);
@@ -39,6 +48,7 @@
     public void onFinishInflate() {
         super.onFinishInflate();
 
+        mManagementContainer = (LinearLayout) findViewById(R.id.management_container);
         mTitle = (TextView) findViewById(R.id.title_text);
         mDescription = (TextView) findViewById(R.id.description_text);
         mLearnMore = (TextView) findViewById(R.id.learn_more);
@@ -57,6 +67,16 @@
         // dismissed before the page is shown.
         setFocusable(true);
         setFocusableInTouchMode(true);
+
+        // Set width constraints.
+        configureWideDisplayStyle();
+    }
+
+    @Override
+    public void onConfigurationChanged(Configuration newConfig) {
+        super.onConfigurationChanged(newConfig);
+        // Set width constraints.
+        configureWideDisplayStyle();
     }
 
     /** Sets whether account is managed. Then updates view accordingly. */
@@ -112,4 +132,22 @@
         mExtensionReportUsername.setVisibility(mIsManaged ? VISIBLE : INVISIBLE);
         mExtensionReportVersion.setVisibility(mIsManaged ? VISIBLE : INVISIBLE);
     }
+
+    /**
+     * When this layout has a wide display style, it will be width constrained to
+     * {@link UiConfig#WIDE_DISPLAY_STYLE_MIN_WIDTH_DP}. If the current screen width is greater than
+     * UiConfig#WIDE_DISPLAY_STYLE_MIN_WIDTH_DP, the settings layout will be visually centered
+     * by adding padding to both sides.
+     */
+    private void configureWideDisplayStyle() {
+        if (mUiConfig == null) {
+            final int minPadding = getResources().getDimensionPixelSize(R.dimen.cm_padding);
+            final int minWidePadding = getResources().getDimensionPixelSize(R.dimen.cm_padding_wide);
+
+            mUiConfig = new UiConfig(mManagementContainer);
+            ViewResizer.createAndAttach(mManagementContainer, mUiConfig, minPadding, minWidePadding);
+        } else {
+            mUiConfig.updateDisplayStyle();
+        }
+    }
 }
diff --git a/chrome/browser/ui/app_list/search/os_settings_provider.cc b/chrome/browser/ui/app_list/search/os_settings_provider.cc
index 8e0d739d..5f346e1 100644
--- a/chrome/browser/ui/app_list/search/os_settings_provider.cc
+++ b/chrome/browser/ui/app_list/search/os_settings_provider.cc
@@ -21,10 +21,10 @@
 #include "chrome/browser/ui/app_list/search/common/icon_constants.h"
 #include "chrome/browser/ui/app_list/search/search_tags_util.h"
 #include "chrome/browser/ui/settings_window_manager_chromeos.h"
-#include "chrome/browser/ui/webui/settings/ash/search/search_handler.h"
 #include "chrome/browser/ui/webui/settings/chromeos/hierarchy.h"
 #include "chrome/browser/ui/webui/settings/chromeos/os_settings_manager.h"
 #include "chrome/browser/ui/webui/settings/chromeos/os_settings_manager_factory.h"
+#include "chrome/browser/ui/webui/settings/chromeos/search/search_handler.h"
 #include "chrome/browser/web_applications/web_app_id_constants.h"
 #include "chrome/common/chrome_features.h"
 #include "components/services/app_service/public/cpp/app_registry_cache.h"
diff --git a/chrome/browser/ui/app_list/search/os_settings_provider.h b/chrome/browser/ui/app_list/search/os_settings_provider.h
index bae525f..7449024a 100644
--- a/chrome/browser/ui/app_list/search/os_settings_provider.h
+++ b/chrome/browser/ui/app_list/search/os_settings_provider.h
@@ -13,7 +13,7 @@
 #include "chrome/browser/apps/app_service/app_service_proxy_forward.h"
 #include "chrome/browser/ui/app_list/search/chrome_search_result.h"
 #include "chrome/browser/ui/app_list/search/search_provider.h"
-#include "chrome/browser/ui/webui/settings/ash/search/search.mojom.h"
+#include "chrome/browser/ui/webui/settings/chromeos/search/search.mojom.h"
 #include "components/services/app_service/public/cpp/app_registry_cache.h"
 #include "components/services/app_service/public/cpp/icon_types.h"
 #include "components/services/app_service/public/mojom/types.mojom.h"
diff --git a/chrome/browser/ui/ash/sharesheet/sharesheet_bubble_view.cc b/chrome/browser/ui/ash/sharesheet/sharesheet_bubble_view.cc
index fadd993e..1596722 100644
--- a/chrome/browser/ui/ash/sharesheet/sharesheet_bubble_view.cc
+++ b/chrome/browser/ui/ash/sharesheet/sharesheet_bubble_view.cc
@@ -170,6 +170,7 @@
   // an alert dialog. This would make screen readers announce all of this dialog
   // which is undesirable.
   SetAccessibleRole(ax::mojom::Role::kDialog);
+  SetAccessibleTitle(l10n_util::GetStringUTF16(IDS_SHARESHEET_TITLE_LABEL));
   set_parent_window(native_window);
   views::Widget* const widget =
       views::Widget::GetWidgetForNativeWindow(native_window);
@@ -267,10 +268,7 @@
     }
   }
 
-  main_view_->SetFocusBehavior(View::FocusBehavior::ALWAYS);
-  main_view_->RequestFocus();
-  main_view_->GetViewAccessibility().OverrideName(
-      l10n_util::GetStringUTF16(IDS_SHARESHEET_TITLE_LABEL));
+  main_view_->SetFocusBehavior(View::FocusBehavior::NEVER);
   views::BubbleDialogDelegateView::CreateBubble(base::WrapUnique(this));
   GetWidget()->GetRootView()->Layout();
   RecordFormFactorMetric();
diff --git a/chrome/browser/ui/ash/sharesheet/sharesheet_header_view.cc b/chrome/browser/ui/ash/sharesheet/sharesheet_header_view.cc
index 60e97b0d..451afa1 100644
--- a/chrome/browser/ui/ash/sharesheet/sharesheet_header_view.cc
+++ b/chrome/browser/ui/ash/sharesheet/sharesheet_header_view.cc
@@ -271,7 +271,7 @@
   // Sets all views to be vertically centre-aligned.
   layout->set_cross_axis_alignment(
       views::BoxLayout::CrossAxisAlignment::kCenter);
-  SetFocusBehavior(View::FocusBehavior::ALWAYS);
+  SetFocusBehavior(View::FocusBehavior::ACCESSIBLE_ONLY);
 
   const bool has_files =
       (intent_->files.has_value() && !intent_->files.value().empty());
diff --git a/chrome/browser/ui/ash/sharesheet/sharesheet_target_button.cc b/chrome/browser/ui/ash/sharesheet/sharesheet_target_button.cc
index b12c4bc5..b80ed15 100644
--- a/chrome/browser/ui/ash/sharesheet/sharesheet_target_button.cc
+++ b/chrome/browser/ui/ash/sharesheet/sharesheet_target_button.cc
@@ -92,6 +92,7 @@
     const absl::optional<gfx::ImageSkia> icon,
     const gfx::VectorIcon* vector_icon)
     : Button(std::move(callback)) {
+  SetFocusBehavior(View::FocusBehavior::ALWAYS);
   // TODO(crbug.com/1097623) Margins shouldn't be within
   // SharesheetTargetButton as the margins are different in |expanded_view_|.
   auto* layout = SetLayoutManager(std::make_unique<views::BoxLayout>(
diff --git a/chrome/browser/ui/ash/shelf/chrome_shelf_controller_browsertest.cc b/chrome/browser/ui/ash/shelf/chrome_shelf_controller_browsertest.cc
index eb431083..61e2bb9f1 100644
--- a/chrome/browser/ui/ash/shelf/chrome_shelf_controller_browsertest.cc
+++ b/chrome/browser/ui/ash/shelf/chrome_shelf_controller_browsertest.cc
@@ -6,6 +6,9 @@
 
 #include <stddef.h>
 #include <memory>
+#include <string>
+#include <utility>
+#include <vector>
 
 #include "ash/constants/ash_features.h"
 #include "ash/public/cpp/app_list/app_list_features.h"
@@ -79,9 +82,10 @@
 #include "chrome/browser/ui/views/frame/browser_view.h"
 #include "chrome/browser/ui/web_applications/app_browser_controller.h"
 #include "chrome/browser/ui/web_applications/test/web_app_browsertest_util.h"
+#include "chrome/browser/web_applications/external_install_options.h"
 #include "chrome/browser/web_applications/externally_installed_web_app_prefs.h"
+#include "chrome/browser/web_applications/externally_managed_app_manager.h"
 #include "chrome/browser/web_applications/os_integration/os_integration_manager.h"
-#include "chrome/browser/web_applications/os_integration/web_app_shortcut_manager.h"
 #include "chrome/browser/web_applications/policy/web_app_policy_constants.h"
 #include "chrome/browser/web_applications/policy/web_app_policy_manager.h"
 #include "chrome/browser/web_applications/system_web_apps/system_web_app_manager.h"
@@ -93,8 +97,8 @@
 #include "chrome/browser/web_applications/web_app_id.h"
 #include "chrome/browser/web_applications/web_app_id_constants.h"
 #include "chrome/browser/web_applications/web_app_install_info.h"
+#include "chrome/browser/web_applications/web_app_install_manager.h"
 #include "chrome/browser/web_applications/web_app_provider.h"
-#include "chrome/browser/web_applications/web_app_registrar.h"
 #include "chrome/browser/web_applications/web_app_sync_bridge.h"
 #include "chrome/browser/web_applications/web_app_utils.h"
 #include "chrome/common/chrome_features.h"
@@ -104,6 +108,8 @@
 #include "chrome/test/base/ui_test_utils.h"
 #include "components/app_constants/constants.h"
 #include "components/crx_file/id_util.h"
+#include "components/prefs/pref_service.h"
+#include "components/services/app_service/public/mojom/types.mojom-shared.h"
 #include "content/public/browser/web_contents.h"
 #include "content/public/test/browser_test.h"
 #include "content/public/test/browser_test_utils.h"
@@ -135,6 +141,7 @@
 #include "ui/events/types/event_type.h"
 #include "ui/views/controls/menu/menu_item_view.h"
 #include "ui/views/test/widget_animation_waiter.h"
+#include "url/gurl.h"
 
 namespace {
 
@@ -2580,14 +2587,15 @@
 }
 
 IN_PROC_BROWSER_TEST_F(ShelfWebAppBrowserTest, WebAppPolicy) {
-  // Install web app.
+  // Install web app from policy.
   GURL app_url = https_server()->GetURL("/web_apps/basic.html");
-  web_app::AppId app_id = web_app::InstallWebAppFromPage(browser(), app_url);
+  web_app::ExternalInstallOptions options =
+      web_app::CreateInstallOptions(app_url);
+  options.install_source = web_app::ExternalInstallSource::kExternalPolicy;
+  web_app::ExternallyManagedAppManager::InstallResult result =
+      web_app::ExternallyManagedAppManagerInstall(browser()->profile(),
+                                                  options);
 
-  web_app::ExternallyInstalledWebAppPrefs web_app_prefs(
-      browser()->profile()->GetPrefs());
-  web_app_prefs.Insert(app_url, app_id,
-                       web_app::ExternalInstallSource::kExternalPolicy);
   apps::AppServiceProxyFactory::GetForProfile(profile())
       ->FlushMojoCallsForTesting();
 
@@ -2600,13 +2608,14 @@
   profile()->GetPrefs()->Set(prefs::kPolicyPinnedLauncherApps, policy_value);
 
   // Check web app is pinned and fixed.
-  EXPECT_EQ(shelf_model()->item_count(), 2);
+  ASSERT_EQ(shelf_model()->item_count(), 2);
   EXPECT_EQ(shelf_model()->items()[0].type, ash::TYPE_BROWSER_SHORTCUT);
   EXPECT_EQ(shelf_model()->items()[1].type, ash::TYPE_PINNED_APP);
-  EXPECT_EQ(shelf_model()->items()[1].id.app_id, app_id);
+  ASSERT_TRUE(result.app_id.has_value());
+  EXPECT_EQ(shelf_model()->items()[1].id.app_id, result.app_id.value());
   EXPECT_EQ(shelf_model()->items()[1].title, u"Basic web app");
   EXPECT_EQ(AppListControllerDelegate::PIN_FIXED,
-            GetPinnableForAppID(app_id, profile()));
+            GetPinnableForAppID(result.app_id.value(), profile()));
 }
 
 IN_PROC_BROWSER_TEST_F(ShelfWebAppBrowserTest, WebAppPolicyUpdate) {
@@ -2708,7 +2717,7 @@
       ->FlushMojoCallsForTesting();
 
   // Check web app is pinned and fixed.
-  EXPECT_EQ(shelf_model()->item_count(), 2);
+  ASSERT_EQ(shelf_model()->item_count(), 2);
   EXPECT_EQ(shelf_model()->items()[0].type, ash::TYPE_BROWSER_SHORTCUT);
   EXPECT_EQ(shelf_model()->items()[1].type, ash::TYPE_PINNED_APP);
   EXPECT_EQ(shelf_model()->items()[1].id.app_id, app_id);
diff --git a/chrome/browser/ui/ash/shelf/chrome_shelf_controller_unittest.cc b/chrome/browser/ui/ash/shelf/chrome_shelf_controller_unittest.cc
index a965c39..cc290b5 100644
--- a/chrome/browser/ui/ash/shelf/chrome_shelf_controller_unittest.cc
+++ b/chrome/browser/ui/ash/shelf/chrome_shelf_controller_unittest.cc
@@ -7,10 +7,12 @@
 #include <stddef.h>
 
 #include <algorithm>
+#include <initializer_list>
 #include <map>
 #include <memory>
 #include <set>
 #include <string>
+#include <type_traits>
 #include <utility>
 #include <vector>
 
@@ -33,6 +35,8 @@
 #include "ash/public/cpp/window_properties.h"
 #include "ash/shelf/shelf_application_menu_model.h"
 #include "base/callback_helpers.h"
+#include "base/check.h"
+#include "base/check_op.h"
 #include "base/command_line.h"
 #include "base/compiler_specific.h"
 #include "base/feature_list.h"
@@ -64,6 +68,7 @@
 #include "chrome/browser/extensions/extension_service.h"
 #include "chrome/browser/extensions/test_extension_system.h"
 #include "chrome/browser/prefs/browser_prefs.h"
+#include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/sync/sync_service_factory.h"
 #include "chrome/browser/ui/app_icon_loader.h"
 #include "chrome/browser/ui/app_list/app_list_syncable_service_factory.h"
@@ -99,12 +104,15 @@
 #include "chrome/browser/ui/tabs/tab_strip_model.h"
 #include "chrome/browser/ui/web_applications/test/web_app_browsertest_util.h"
 #include "chrome/browser/web_applications/externally_installed_web_app_prefs.h"
-#include "chrome/browser/web_applications/policy/web_app_policy_constants.h"
+#include "chrome/browser/web_applications/system_web_apps/system_web_app_manager.h"
 #include "chrome/browser/web_applications/system_web_apps/test/test_system_web_app_manager.h"
 #include "chrome/browser/web_applications/test/fake_web_app_provider.h"
 #include "chrome/browser/web_applications/test/web_app_install_test_utils.h"
 #include "chrome/browser/web_applications/web_app_constants.h"
+#include "chrome/browser/web_applications/web_app_helpers.h"
+#include "chrome/browser/web_applications/web_app_id.h"
 #include "chrome/browser/web_applications/web_app_id_constants.h"
+#include "chrome/browser/web_applications/web_app_install_info.h"
 #include "chrome/common/chrome_constants.h"
 #include "chrome/common/chrome_features.h"
 #include "chrome/common/chrome_switches.h"
@@ -120,7 +128,13 @@
 #include "components/app_constants/constants.h"
 #include "components/exo/shell_surface_util.h"
 #include "components/keep_alive_registry/scoped_keep_alive.h"
-#include "components/prefs/pref_notifier_impl.h"
+#include "components/prefs/pref_service.h"
+#include "components/services/app_service/public/cpp/app_registry_cache.h"
+#include "components/services/app_service/public/cpp/instance.h"
+#include "components/services/app_service/public/cpp/instance_registry.h"
+#include "components/services/app_service/public/mojom/types.mojom-forward.h"
+#include "components/services/app_service/public/mojom/types.mojom-shared.h"
+#include "components/services/app_service/public/mojom/types.mojom.h"
 #include "components/sync/base/model_type.h"
 #include "components/sync/driver/sync_service.h"
 #include "components/sync/driver/sync_user_settings.h"
@@ -161,6 +175,15 @@
 #include "ui/gfx/image/image_skia_operations.h"
 #include "ui/gfx/image/image_unittest_util.h"
 #include "ui/views/widget/widget.h"
+#include "url/gurl.h"
+
+namespace content {
+class BrowserContext;
+}  // namespace content
+
+namespace extensions {
+class Manifest;
+}  // namespace extensions
 
 using base::ASCIIToUTF16;
 using extensions::Extension;
@@ -4885,12 +4908,19 @@
   web_app::AppId InstallExternalWebApp(std::string start_url) {
     auto web_app_info = std::make_unique<WebAppInstallInfo>();
     web_app_info->start_url = GURL(start_url);
+    const web_app::AppId expected_web_app_id = web_app::GenerateAppId(
+        /*manifest_id=*/absl::nullopt, web_app_info->start_url);
+    PrefService* prefs = browser()->profile()->GetPrefs();
+    web_app::ExternallyInstalledWebAppPrefs web_app_prefs(prefs);
+    web_app_prefs.Insert(GURL(start_url), expected_web_app_id,
+                         web_app::ExternalInstallSource::kExternalPolicy);
+    // Ensure prefs are written before the web app install process reads them.
+    base::RunLoop run_loop;
+    prefs->CommitPendingWrite(run_loop.QuitClosure());
+    run_loop.Run();
     web_app::AppId web_app_id =
         web_app::test::InstallWebApp(profile(), std::move(web_app_info));
-    web_app::ExternallyInstalledWebAppPrefs web_app_prefs(
-        browser()->profile()->GetPrefs());
-    web_app_prefs.Insert(GURL(start_url), web_app_id,
-                         web_app::ExternalInstallSource::kExternalPolicy);
+    DCHECK_EQ(expected_web_app_id, web_app_id);
     return web_app_id;
   }
 
diff --git a/chrome/browser/ui/ash/shelf/chrome_shelf_prefs.cc b/chrome/browser/ui/ash/shelf/chrome_shelf_prefs.cc
index e60939f0..89eba5e 100644
--- a/chrome/browser/ui/ash/shelf/chrome_shelf_prefs.cc
+++ b/chrome/browser/ui/ash/shelf/chrome_shelf_prefs.cc
@@ -6,19 +6,29 @@
 
 #include <stddef.h>
 
+#include <algorithm>
+#include <map>
 #include <memory>
+#include <ostream>
 #include <utility>
 
 #include "ash/constants/ash_features.h"
 #include "ash/constants/ash_pref_names.h"
 #include "ash/constants/ash_switches.h"
-#include "ash/public/cpp/app_list/internal_app_id_constants.h"
+#include "ash/public/cpp/shelf_types.h"
+#include "base/check.h"
+#include "base/check_op.h"
+#include "base/containers/checked_iterators.h"
+#include "base/containers/checked_range.h"
+#include "base/containers/contains.h"
+#include "base/containers/span.h"
+#include "base/logging.h"
 #include "base/metrics/histogram_functions.h"
+#include "base/strings/string_piece_forward.h"
 #include "base/strings/string_util.h"
 #include "base/values.h"
 #include "chrome/browser/apps/app_service/app_service_proxy.h"
 #include "chrome/browser/apps/app_service/app_service_proxy_factory.h"
-#include "chrome/browser/apps/app_service/publishers/extension_apps_util.h"
 #include "chrome/browser/ash/crosapi/browser_util.h"
 #include "chrome/browser/ash/file_manager/app_id.h"
 #include "chrome/browser/ash/file_manager/prefs_migration_uma.h"
@@ -31,26 +41,24 @@
 #include "chrome/browser/ui/app_list/app_list_syncable_service.h"
 #include "chrome/browser/ui/app_list/app_list_syncable_service_factory.h"
 #include "chrome/browser/ui/app_list/arc/arc_app_list_prefs.h"
-#include "chrome/browser/ui/app_list/arc/arc_app_utils.h"
 #include "chrome/browser/ui/ash/default_pinned_apps.h"
-#include "chrome/browser/ui/ash/shelf/chrome_shelf_controller_util.h"
 #include "chrome/browser/ui/ash/shelf/shelf_controller_helper.h"
-#include "chrome/browser/web_applications/web_app_id.h"
-#include "chrome/browser/web_applications/web_app_provider.h"
-#include "chrome/browser/web_applications/web_app_registrar.h"
-#include "chrome/common/extensions/extension_constants.h"
 #include "chrome/common/pref_names.h"
 #include "components/app_constants/constants.h"
 #include "components/crx_file/id_util.h"
 #include "components/pref_registry/pref_registry_syncable.h"
+#include "components/prefs/pref_registry.h"
 #include "components/prefs/pref_service.h"
 #include "components/prefs/scoped_user_pref_update.h"
+#include "components/services/app_service/public/cpp/app_registry_cache.h"
+#include "components/services/app_service/public/cpp/app_update.h"
 #include "components/sync/base/user_selectable_type.h"
 #include "components/sync/driver/sync_service.h"
 #include "components/sync/driver/sync_user_settings.h"
 #include "components/sync/model/string_ordinal.h"
 #include "components/sync_preferences/pref_service_syncable.h"
 #include "extensions/common/constants.h"
+#include "url/gurl.h"
 
 using syncer::UserSelectableOsType;
 using syncer::UserSelectableType;
@@ -245,16 +253,27 @@
       continue;
     }
 
-    // Handle Web App ids
-    const GURL web_app_url(*policy_entry);
-    if (web_app_url.is_valid()) {
-      absl::optional<web_app::AppId> web_app_id =
-          web_app::WebAppProvider::GetDeprecated(helper->profile())
-              ->registrar()
-              .LookupExternalAppId(web_app_url);
-      if (web_app_id.has_value())
-        result.emplace_back(web_app_id.value());
-      continue;
+    // URLs provided through policy might not match exactly (eg. missing
+    // trailing slash), so check the normalized version of valid URLs too.
+    std::vector<std::string> policy_entries_to_check{*policy_entry};
+    const GURL normalized_policy_url(*policy_entry);
+    if (normalized_policy_url.is_valid() &&
+        normalized_policy_url.spec() != *policy_entry) {
+      policy_entries_to_check.push_back(normalized_policy_url.spec());
+    }
+
+    // Handle App Service policy IDs (currently Web Apps only)
+    if (apps::AppServiceProxyFactory::IsAppServiceAvailableForProfile(
+            helper->profile())) {
+      apps::AppServiceProxyFactory::GetForProfile(helper->profile())
+          ->AppRegistryCache()
+          .ForEachApp([&result, &policy_entries_to_check](
+                          const apps::AppUpdate& update) {
+            if (base::Contains(policy_entries_to_check, update.PolicyId())) {
+              DCHECK(!base::Contains(result, update.AppId()));
+              result.emplace_back(update.AppId());
+            }
+          });
     }
 
     // Handle Arc++ App ids
diff --git a/chrome/browser/ui/ash/shelf/chrome_shelf_prefs.h b/chrome/browser/ui/ash/shelf/chrome_shelf_prefs.h
index a42f4c7..cdcfa77 100644
--- a/chrome/browser/ui/ash/shelf/chrome_shelf_prefs.h
+++ b/chrome/browser/ui/ash/shelf/chrome_shelf_prefs.h
@@ -8,20 +8,16 @@
 #include <string>
 #include <vector>
 
-#include "ash/public/cpp/shelf_types.h"
-#include "base/feature_list.h"
-#include "base/metrics/field_trial_params.h"
 #include "chrome/browser/ui/app_list/app_list_syncable_service.h"
-#include "components/prefs/pref_change_registrar.h"
-#include "components/services/app_service/public/mojom/types.mojom.h"
+#include "components/services/app_service/public/mojom/types.mojom-shared.h"
 
 class ShelfControllerHelper;
 class PrefService;
 class Profile;
 
-namespace app_list {
-class AppListSyncableService;
-}  // namespace app_list
+namespace ash {
+struct ShelfID;
+}  // namespace ash
 
 namespace user_prefs {
 class PrefRegistrySyncable;
diff --git a/chrome/browser/ui/blocked_content/safe_browsing_triggered_popup_blocker_browsertest.cc b/chrome/browser/ui/blocked_content/safe_browsing_triggered_popup_blocker_browsertest.cc
index f15d7f29..b29c7d34c 100644
--- a/chrome/browser/ui/blocked_content/safe_browsing_triggered_popup_blocker_browsertest.cc
+++ b/chrome/browser/ui/blocked_content/safe_browsing_triggered_popup_blocker_browsertest.cc
@@ -13,6 +13,7 @@
 #include "base/containers/contains.h"
 #include "base/lazy_instance.h"
 #include "base/path_service.h"
+#include "base/test/bind.h"
 #include "base/test/metrics/histogram_tester.h"
 #include "base/test/scoped_feature_list.h"
 #include "build/build_config.h"
@@ -41,11 +42,13 @@
 #include "components/safe_browsing/core/browser/db/v4_embedded_test_server_util.h"
 #include "components/safe_browsing/core/browser/db/v4_test_util.h"
 #include "components/subresource_filter/core/browser/subresource_filter_features.h"
+#include "content/public/browser/render_frame_host.h"
 #include "content/public/browser/render_process_host.h"
 #include "content/public/browser/web_contents.h"
 #include "content/public/test/back_forward_cache_util.h"
 #include "content/public/test/browser_test.h"
 #include "content/public/test/browser_test_utils.h"
+#include "content/public/test/fenced_frame_test_util.h"
 #include "content/public/test/prerender_test_util.h"
 #include "content/public/test/test_navigation_observer.h"
 #include "net/dns/mock_host_resolver.h"
@@ -731,6 +734,31 @@
               kSafeBrowsingTriggeredPopupBlocker)));
 }
 
+// Tests that the popup blocker UI is shown when a sub frame tries to
+// open a new window if the main frame is marked as abusive since
+// SafeBrowsingTriggeredPopupBlocker works based on a main frame.
+IN_PROC_BROWSER_TEST_F(SafeBrowsingTriggeredPopupBlockerBrowserTest,
+                       OpenNewWindowInSubFrame) {
+  content::BackForwardCacheDisabledTester back_forward_cache_tester;
+  const GURL a_url(embedded_test_server()->GetURL("a.com", "/iframe.html"));
+  ConfigureAsAbusive(a_url);
+
+  // Navigate to an abusive page.
+  ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), a_url));
+
+  content::RenderFrameHost* main_frame =
+      browser()->tab_strip_model()->GetActiveWebContents()->GetMainFrame();
+
+  content::RenderFrameHost* sub_frame = content::ChildFrameAt(main_frame, 0);
+  EXPECT_NE(sub_frame, nullptr);
+  EXPECT_EQ(false, content::EvalJs(sub_frame, "!!window.open()"));
+
+  // Popup UI should be shown.
+  EXPECT_TRUE(
+      PageSpecificContentSettings::GetForFrame(web_contents()->GetMainFrame())
+          ->IsContentBlocked(ContentSettingsType::POPUPS));
+}
+
 class SafeBrowsingTriggeredPopupBlockerPrerenderingBrowserTest
     : public SafeBrowsingTriggeredPopupBlockerBrowserTest {
  public:
@@ -829,3 +857,70 @@
       PageSpecificContentSettings::GetForFrame(web_contents()->GetMainFrame())
           ->IsContentBlocked(ContentSettingsType::POPUPS));
 }
+
+class SafeBrowsingTriggeredPopupBlockerFencedFrameBrowserTest
+    : public SafeBrowsingTriggeredPopupBlockerBrowserTest {
+ public:
+  SafeBrowsingTriggeredPopupBlockerFencedFrameBrowserTest() = default;
+  ~SafeBrowsingTriggeredPopupBlockerFencedFrameBrowserTest() override = default;
+
+  content::test::FencedFrameTestHelper& fenced_frame_test_helper() {
+    return fenced_frame_test_helper_;
+  }
+
+ protected:
+  content::test::FencedFrameTestHelper fenced_frame_test_helper_;
+};
+
+// The following two tests ensure that SafeBrowsingTriggeredPopupBlocker
+// isn't triggered for a fenced frame since it's treated as a subframe for
+// SafeBrowsingTriggeredPopupBlocker even though it's a main frame in a frame
+// tree.
+// This test ensures that opening a new window in a fenced frame doesn't trigger
+// the popup blocker when the primary page is not marked as abusive, even if the
+// fenced frame's URL is.
+IN_PROC_BROWSER_TEST_F(SafeBrowsingTriggeredPopupBlockerFencedFrameBrowserTest,
+                       ShouldNotTriggerPopupBlocker) {
+  auto* first_web_contents = web_contents();
+  // Load an initial page.
+  GURL initial_url(embedded_test_server()->GetURL("/simple.html"));
+  ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), initial_url));
+
+  // Load a fenced frame and ensure that it doesn't trigger the popup blocker.
+  GURL fenced_url(embedded_test_server()->GetURL("/fenced_frames/title1.html"));
+  // Even though `fenced_url` is marked as abusive, it doesn't affect the popup
+  // blocker.
+  ConfigureAsAbusive(fenced_url);
+
+  // Loading a fenced frame should not trigger the popup blocker.
+  auto* fenced_frame_host = fenced_frame_test_helper().CreateFencedFrame(
+      first_web_contents->GetMainFrame(), fenced_url);
+  EXPECT_EQ(false, content::EvalJs(fenced_frame_host, "!!window.open()"));
+
+  // Check if the popup UI was shown from the previous web contents.
+  EXPECT_FALSE(PageSpecificContentSettings::GetForFrame(
+                   first_web_contents->GetMainFrame())
+                   ->IsContentBlocked(ContentSettingsType::POPUPS));
+}
+
+// This test ensures that the primary page has the popup blocker when
+// the primary page is marked as abusive and the fenced frame tries to open a
+// new window.
+IN_PROC_BROWSER_TEST_F(SafeBrowsingTriggeredPopupBlockerFencedFrameBrowserTest,
+                       ShouldTriggerPopupBlocker) {
+  // Load an initial page.
+  GURL initial_url(embedded_test_server()->GetURL("/simple.html"));
+  ConfigureAsAbusive(initial_url);
+  ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), initial_url));
+
+  // Load a fenced frame.
+  GURL fenced_url(embedded_test_server()->GetURL("/fenced_frames/title1.html"));
+  auto* fenced_frame_host = fenced_frame_test_helper().CreateFencedFrame(
+      web_contents()->GetMainFrame(), fenced_url);
+  EXPECT_EQ(false, content::EvalJs(fenced_frame_host, "!!window.open()"));
+
+  // Popup UI should be shown.
+  EXPECT_TRUE(
+      PageSpecificContentSettings::GetForFrame(web_contents()->GetMainFrame())
+          ->IsContentBlocked(ContentSettingsType::POPUPS));
+}
diff --git a/chrome/browser/ui/views/user_education/browser_user_education_service.cc b/chrome/browser/ui/views/user_education/browser_user_education_service.cc
index 6a149048..e6a7073 100644
--- a/chrome/browser/ui/views/user_education/browser_user_education_service.cc
+++ b/chrome/browser/ui/views/user_education/browser_user_education_service.cc
@@ -30,22 +30,8 @@
 #endif
 
 namespace {
-const char kTabGroupTutorialMetricPrefix[] = "TabGroup";
 
-// kIPHDesktopTabGroupsNewGroupFeature:
-ui::TrackedElement* GetTabGroupsAnchorView(
-    const ui::ElementTracker::ElementList& elements) {
-  if (elements.empty())
-    return nullptr;
-  TabStrip* const tab_strip = static_cast<TabStrip*>(
-      elements[0]->AsA<views::TrackedElementViews>()->view());
-  constexpr int kPreferredAnchorTab = 2;
-  views::View* const tab =
-      tab_strip->GetTabViewForPromoAnchor(kPreferredAnchorTab);
-  return tab ? views::ElementTrackerViews::GetInstance()->GetElementForView(
-                   tab, true)
-             : nullptr;
-}
+const char kTabGroupTutorialMetricPrefix[] = "TabGroup";
 
 }  // namespace
 
@@ -92,12 +78,10 @@
   registry.RegisterFeature(
       std::move(FeaturePromoSpecification::CreateForTutorialPromo(
                     feature_engagement::kIPHDesktopTabGroupsNewGroupFeature,
-                    kTabStripElementId, IDS_TAB_GROUPS_NEW_GROUP_PROMO,
+                    kTabStripRegionElementId, IDS_TAB_GROUPS_NEW_GROUP_PROMO,
                     kTabGroupTutorialId)
                     .SetBubbleArrow(HelpBubbleArrow::kTopCenter)
-                    .SetBubbleIcon(&vector_icons::kLightbulbOutlineIcon)
-                    .SetAnchorElementFilter(
-                        base::BindRepeating(&GetTabGroupsAnchorView))));
+                    .SetBubbleIcon(&vector_icons::kLightbulbOutlineIcon)));
 
   // kIPHLiveCaptionFeature:
   registry.RegisterFeature(FeaturePromoSpecification::CreateForToastPromo(
diff --git a/chrome/browser/ui/web_applications/test/web_app_browsertest_util.cc b/chrome/browser/ui/web_applications/test/web_app_browsertest_util.cc
index 281d7af..e18b631 100644
--- a/chrome/browser/ui/web_applications/test/web_app_browsertest_util.cc
+++ b/chrome/browser/ui/web_applications/test/web_app_browsertest_util.cc
@@ -5,15 +5,18 @@
 #include "chrome/browser/ui/web_applications/test/web_app_browsertest_util.h"
 
 #include <memory>
+#include <ostream>
 #include <string>
+#include <utility>
 
+#include "base/bind.h"
 #include "base/callback_helpers.h"
-#include "base/one_shot_event.h"
+#include "base/check.h"
+#include "base/check_op.h"
 #include "base/run_loop.h"
-#include "base/strings/utf_string_conversions.h"
 #include "base/test/bind.h"
-#include "base/threading/thread_task_runner_handle.h"
 #include "build/build_config.h"
+#include "build/buildflag.h"
 #include "chrome/browser/apps/app_service/app_launch_params.h"
 #include "chrome/browser/apps/app_service/app_service_proxy.h"
 #include "chrome/browser/apps/app_service/app_service_proxy_factory.h"
@@ -28,28 +31,31 @@
 #include "chrome/browser/ui/toolbar/app_menu_model.h"
 #include "chrome/browser/ui/web_applications/app_browser_controller.h"
 #include "chrome/browser/web_applications/external_install_options.h"
-#include "chrome/browser/web_applications/externally_managed_app_manager.h"
 #include "chrome/browser/web_applications/test/service_worker_registration_waiter.h"
 #include "chrome/browser/web_applications/test/web_app_install_test_utils.h"
-#include "chrome/browser/web_applications/web_app_helpers.h"
+#include "chrome/browser/web_applications/web_app_constants.h"
 #include "chrome/browser/web_applications/web_app_install_finalizer.h"
 #include "chrome/browser/web_applications/web_app_install_info.h"
 #include "chrome/browser/web_applications/web_app_install_manager.h"
 #include "chrome/browser/web_applications/web_app_install_params.h"
+#include "chrome/browser/web_applications/web_app_install_utils.h"
 #include "chrome/browser/web_applications/web_app_provider.h"
 #include "chrome/browser/web_applications/web_app_registrar.h"
 #include "chrome/browser/web_applications/web_app_tab_helper.h"
 #include "chrome/test/base/ui_test_utils.h"
 #include "components/security_interstitials/content/security_interstitial_tab_helper.h"
+#include "components/services/app_service/public/mojom/types.mojom-shared.h"
 #include "components/webapps/browser/install_result_code.h"
 #include "components/webapps/browser/installable/installable_metrics.h"
 #include "content/public/browser/notification_service.h"
+#include "content/public/browser/notification_source.h"
 #include "content/public/test/browser_test_utils.h"
 #include "content/public/test/test_navigation_observer.h"
 #include "content/public/test/test_utils.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "ui/base/models/menu_model.h"
 #include "ui/base/page_transition_types.h"
+#include "ui/base/window_open_disposition.h"
 
 #if BUILDFLAG(IS_MAC)
 #include <ImageIO/ImageIO.h>
@@ -63,6 +69,16 @@
 #include "ui/gfx/icon_util.h"
 #endif
 
+class GURL;
+
+namespace base {
+class FilePath;
+}  // namespace base
+
+namespace content {
+class WebContents;
+}  // namespace content
+
 using ui_test_utils::BrowserChangeObserver;
 
 namespace web_app {
@@ -221,7 +237,7 @@
   return install_options;
 }
 
-webapps::InstallResultCode ExternallyManagedAppManagerInstall(
+ExternallyManagedAppManager::InstallResult ExternallyManagedAppManagerInstall(
     Profile* profile,
     ExternalInstallOptions install_options) {
   DCHECK(profile);
@@ -229,19 +245,19 @@
   DCHECK(provider);
   test::WaitUntilReady(provider);
   base::RunLoop run_loop;
-  webapps::InstallResultCode result_code;
+  ExternallyManagedAppManager::InstallResult result;
 
   provider->externally_managed_app_manager().Install(
       std::move(install_options),
       base::BindLambdaForTesting(
-          [&result_code, &run_loop](
+          [&result, &run_loop](
               const GURL& provided_url,
-              ExternallyManagedAppManager::InstallResult result) {
-            result_code = result.code;
+              ExternallyManagedAppManager::InstallResult install_result) {
+            result = install_result;
             run_loop.Quit();
           }));
   run_loop.Run();
-  return result_code;
+  return result;
 }
 
 void NavigateToURLAndWait(Browser* browser,
diff --git a/chrome/browser/ui/web_applications/test/web_app_browsertest_util.h b/chrome/browser/ui/web_applications/test/web_app_browsertest_util.h
index 128b8a1c..8ba9172 100644
--- a/chrome/browser/ui/web_applications/test/web_app_browsertest_util.h
+++ b/chrome/browser/ui/web_applications/test/web_app_browsertest_util.h
@@ -5,21 +5,26 @@
 #ifndef CHROME_BROWSER_UI_WEB_APPLICATIONS_TEST_WEB_APP_BROWSERTEST_UTIL_H_
 #define CHROME_BROWSER_UI_WEB_APPLICATIONS_TEST_WEB_APP_BROWSERTEST_UTIL_H_
 
+#include "base/callback.h"
 #include "base/memory/raw_ptr.h"
 #include "base/run_loop.h"
 #include "base/scoped_observation.h"
+#include "base/strings/string_piece_forward.h"
 #include "chrome/browser/ui/browser_list_observer.h"
-#include "chrome/browser/web_applications/app_registrar_observer.h"
+#include "chrome/browser/web_applications/externally_managed_app_manager.h"
 #include "chrome/browser/web_applications/web_app_id.h"
-#include "chrome/browser/web_applications/web_app_install_info.h"
 #include "chrome/browser/web_applications/web_app_install_manager.h"
 #include "chrome/browser/web_applications/web_app_install_manager_observer.h"
-#include "chrome/browser/web_applications/web_app_registrar.h"
-#include "url/gurl.h"
+#include "third_party/skia/include/core/SkColor.h"
 
 class Browser;
+class GURL;
 class Profile;
 
+namespace base {
+class FilePath;
+}
+
 namespace webapps {
 enum class InstallResultCode;
 }
@@ -27,6 +32,7 @@
 namespace web_app {
 
 struct ExternalInstallOptions;
+class WebAppInstallManager;
 
 // For InstallWebAppFromInfo see web_app_install_test_utils.h
 
@@ -54,7 +60,7 @@
 ExternalInstallOptions CreateInstallOptions(const GURL& url);
 
 // Synchronous version of ExternallyManagedAppManager::Install.
-webapps::InstallResultCode ExternallyManagedAppManagerInstall(
+ExternallyManagedAppManager::InstallResult ExternallyManagedAppManagerInstall(
     Profile*,
     ExternalInstallOptions);
 
diff --git a/chrome/browser/ui/web_applications/web_app_engagement_browsertest.cc b/chrome/browser/ui/web_applications/web_app_engagement_browsertest.cc
index b32f0bc..d315b10 100644
--- a/chrome/browser/ui/web_applications/web_app_engagement_browsertest.cc
+++ b/chrome/browser/ui/web_applications/web_app_engagement_browsertest.cc
@@ -216,8 +216,10 @@
   }
 
   void InstallDefaultAppAndCountApps(ExternalInstallOptions install_options) {
-    result_code_ = ExternallyManagedAppManagerInstall(
-        browser()->profile(), std::move(install_options));
+    ExternallyManagedAppManager::InstallResult result =
+        ExternallyManagedAppManagerInstall(browser()->profile(),
+                                           std::move(install_options));
+    result_code_ = result.code;
     CountUserInstalledApps();
   }
 
@@ -486,9 +488,9 @@
   const GURL example_url(
       embedded_test_server()->GetURL("/banners/manifest_test_page.html"));
 
-  auto result_code = ExternallyManagedAppManagerInstall(
+  auto result = ExternallyManagedAppManagerInstall(
       browser()->profile(), CreateInstallOptions(example_url));
-  ASSERT_EQ(webapps::InstallResultCode::kSuccessNewInstall, result_code);
+  ASSERT_EQ(webapps::InstallResultCode::kSuccessNewInstall, result.code);
   absl::optional<AppId> app_id = FindAppWithUrlInScope(example_url);
   ASSERT_TRUE(app_id);
   content::WindowedNotificationObserver app_loaded_observer(
@@ -537,9 +539,9 @@
   const GURL example_url(
       embedded_test_server()->GetURL("/banners/manifest_test_page.html"));
 
-  auto result_code = ExternallyManagedAppManagerInstall(
+  auto result = ExternallyManagedAppManagerInstall(
       browser()->profile(), CreateInstallOptions(example_url));
-  ASSERT_EQ(webapps::InstallResultCode::kSuccessNewInstall, result_code);
+  ASSERT_EQ(webapps::InstallResultCode::kSuccessNewInstall, result.code);
   absl::optional<AppId> app_id = FindAppWithUrlInScope(example_url);
   ASSERT_TRUE(app_id);
   content::WindowedNotificationObserver app_loaded_observer(
@@ -592,9 +594,9 @@
 
   ExternalInstallOptions install_options = CreateInstallOptions(example_url);
   install_options.user_display_mode = DisplayMode::kBrowser;
-  auto result_code =
+  auto result =
       ExternallyManagedAppManagerInstall(browser()->profile(), install_options);
-  ASSERT_EQ(webapps::InstallResultCode::kSuccessNewInstall, result_code);
+  ASSERT_EQ(webapps::InstallResultCode::kSuccessNewInstall, result.code);
   absl::optional<AppId> app_id = FindAppWithUrlInScope(example_url);
   ASSERT_TRUE(app_id);
   content::WindowedNotificationObserver app_loaded_observer(
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 0c453003..4770c9f 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
@@ -46,6 +46,7 @@
 #include "chrome/grit/new_tab_page_resources.h"
 #include "chrome/grit/new_tab_page_resources_map.h"
 #include "chrome/grit/theme_resources.h"
+#include "components/commerce/core/commerce_feature_list.h"
 #include "components/favicon_base/favicon_url_parser.h"
 #include "components/google/core/common/google_util.h"
 #include "components/grit/components_scaled_resources.h"
@@ -83,6 +84,89 @@
 constexpr char kPrevNavigationTimePrefName[] = "NewTabPage.PrevNavigationTime";
 constexpr char kSignedOutNtpModulesSwitch[] = "signed-out-ntp-modules";
 
+void AddRawStringOrDefault(content::WebUIDataSource* source,
+                           const char key[],
+                           const std::string str,
+                           int default_string_id) {
+  if (str.empty()) {
+    source->AddLocalizedString(key, default_string_id);
+  } else {
+    source->AddString(key, str);
+  }
+}
+
+// The Discount Consent V2 is gated by Chrome Cart, and that is enabled for
+// en-us local only. So using plain en strings here are fine.
+void AddResourcesForCartDiscountConsentV2(content::WebUIDataSource* source) {
+  int discount_consent_variation =
+      ntp_features::kNtpChromeCartModuleDiscountConsentNtpVariation.Get();
+  source->AddInteger("modulesCartDiscountConsentVariation",
+                     discount_consent_variation);
+
+  if (discount_consent_variation ==
+      static_cast<int>(
+          ntp_features::DiscountConsentNtpVariation::kStringChange)) {
+    AddRawStringOrDefault(
+        source, "modulesCartDiscountConsentContent",
+        ntp_features::kNtpChromeCartModuleDiscountConsentStringChangeContent
+            .Get(),
+        IDS_NTP_MODULES_CART_DISCOUNT_CONSENT_CONTENT_V2);
+  } else {
+    if (discount_consent_variation ==
+        static_cast<int>(ntp_features::DiscountConsentNtpVariation::kInline)) {
+      source->AddBoolean(
+          "modulesCartConsentStepTwoDifferentColor",
+          ntp_features::
+              kNtpChromeCartModuleDiscountConsentInlineStepTwoDifferentColor
+                  .Get());
+    } else if (discount_consent_variation ==
+               static_cast<int>(
+                   ntp_features::DiscountConsentNtpVariation::kDialog)) {
+      AddRawStringOrDefault(
+          source, "modulesCartDiscountConentTitle",
+          ntp_features::kNtpChromeCartModuleDiscountConsentNtpDialogContentTitle
+              .Get(),
+          IDS_NTP_MODULES_CART_DISCOUNT_CONSENT_TITLE);
+    }
+    source->AddBoolean(
+        "modulesCartStepOneUseStaticContent",
+        ntp_features::
+            kNtpChromeCartModuleDiscountConsentNtpStepOneUseStaticContent
+                .Get());
+    // This does not have a raw string resource.
+    source->AddString(
+        "modulesCartStepOneStaticContent",
+        ntp_features::kNtpChromeCartModuleDiscountConsentNtpStepOneStaticContent
+            .Get());
+
+    AddRawStringOrDefault(
+        source, "modulesCartConsentStepOneOneMerchantContent",
+        ntp_features::
+            kNtpChromeCartModuleDiscountConsentNtpStepOneContentOneCart.Get(),
+        IDS_NTP_MODULES_CART_DISCOUNT_CONSENT_STEP_1_WITH_MERCHANT_NAME);
+    AddRawStringOrDefault(
+        source, "modulesCartConsentStepOneTwoMerchantsContent",
+        ntp_features::
+            kNtpChromeCartModuleDiscountConsentNtpStepOneContentTwoCarts.Get(),
+        IDS_NTP_MODULES_CART_DISCOUNT_CONSENT_STEP_1_WITH_TWO_MERCHANT_NAMES);
+    AddRawStringOrDefault(
+        source, "modulesCartConsentStepOneThreeMerchantsContent",
+        ntp_features::
+            kNtpChromeCartModuleDiscountConsentNtpStepOneContentThreeCarts
+                .Get(),
+        IDS_NTP_MODULES_CART_DISCOUNT_CONSENT_STEP_1_WITH_THREE_MERCHANT_NAMES);
+    AddRawStringOrDefault(
+        source, "modulesCartConsentStepTwoContent",
+        ntp_features::kNtpChromeCartModuleDiscountConsentNtpStepTwoContent
+            .Get(),
+        IDS_NTP_MODULES_CART_DISCOUNT_CONSENT_CONTENT_V3);
+
+    source->AddLocalizedString(
+        "modulesCartConsentStepOneButton",
+        IDS_NTP_MODULES_CART_DISCOUNT_CONSENT_STEP_1_CONTINUE);
+  }
+}
+
 content::WebUIDataSource* CreateNewTabPageUiHtmlSource(Profile* profile) {
   content::WebUIDataSource* source =
       content::WebUIDataSource::Create(chrome::kChromeUINewTabPageHost);
@@ -287,8 +371,6 @@
        IDS_NTP_MODULES_CART_DISCOUNT_CHIP_AMOUNT},
       {"modulesCartDiscountChipUpToAmount",
        IDS_NTP_MODULES_CART_DISCOUNT_CHIP_UP_TO_AMOUNT},
-      {"modulesCartDiscountConsentContent",
-       IDS_NTP_MODULES_CART_DISCOUNT_CONSENT_CONTENT},
       {"modulesCartDiscountConsentAccept",
        IDS_NTP_MODULES_CART_DISCOUNT_CONSENT_ACCEPT},
       {"modulesCartDiscountConsentAcceptConfirmation",
@@ -307,9 +389,16 @@
        IDS_NTP_MODULES_CART_DISCOUNT_CONSENT_CONTENT_V3},
       {"modulesCartDiscountConentTitle",
        IDS_NTP_MODULES_CART_DISCOUNT_CONSENT_TITLE},
-     };
+  };
   source->AddLocalizedStrings(kStrings);
 
+  if (base::FeatureList::IsEnabled(commerce::kDiscountConsentV2)) {
+    AddResourcesForCartDiscountConsentV2(source);
+  } else {
+    source->AddLocalizedString("modulesCartDiscountConsentContent",
+                               IDS_NTP_MODULES_CART_DISCOUNT_CONSENT_CONTENT);
+  }
+
 #if !defined(OFFICIAL_BUILD)
   source->AddBoolean(
       "dummyModulesEnabled",
diff --git a/chrome/browser/ui/webui/settings/chromeos/BUILD.gn b/chrome/browser/ui/webui/settings/chromeos/BUILD.gn
index 0fc7523..c86ea04 100644
--- a/chrome/browser/ui/webui/settings/chromeos/BUILD.gn
+++ b/chrome/browser/ui/webui/settings/chromeos/BUILD.gn
@@ -5,8 +5,8 @@
 group("mojom_js") {
   public_deps = [
     "constants:mojom_js",
+    "search:mojo_bindings_js",
     "//chrome/browser/ui/webui/nearby_share/public/mojom:mojom_js",
-    "//chrome/browser/ui/webui/settings/ash/search:mojo_bindings_js",
     "//ui/webui/resources/cr_components/app_management:mojo_bindings_js",
   ]
 }
diff --git a/chrome/browser/ui/webui/settings/chromeos/about_section.cc b/chrome/browser/ui/webui/settings/chromeos/about_section.cc
index 55e65de..f676a04f 100644
--- a/chrome/browser/ui/webui/settings/chromeos/about_section.cc
+++ b/chrome/browser/ui/webui/settings/chromeos/about_section.cc
@@ -19,8 +19,8 @@
 #include "chrome/browser/obsolete_system/obsolete_system.h"
 #include "chrome/browser/ui/webui/management/management_ui.h"
 #include "chrome/browser/ui/webui/settings/about_handler.h"
-#include "chrome/browser/ui/webui/settings/ash/search/search_tag_registry.h"
 #include "chrome/browser/ui/webui/settings/chromeos/device_name_handler.h"
+#include "chrome/browser/ui/webui/settings/chromeos/search/search_tag_registry.h"
 #include "chrome/browser/ui/webui/version/version_ui.h"
 #include "chrome/browser/ui/webui/webui_util.h"
 #include "chrome/common/channel_info.h"
diff --git a/chrome/browser/ui/webui/settings/chromeos/accessibility_section.cc b/chrome/browser/ui/webui/settings/chromeos/accessibility_section.cc
index 165f9e5..4933f39 100644
--- a/chrome/browser/ui/webui/settings/chromeos/accessibility_section.cc
+++ b/chrome/browser/ui/webui/settings/chromeos/accessibility_section.cc
@@ -21,9 +21,9 @@
 #include "chrome/browser/extensions/extension_service.h"
 #include "chrome/browser/speech/extension_api/tts_engine_extension_observer_chromeos.h"
 #include "chrome/browser/ui/webui/settings/accessibility_main_handler.h"
-#include "chrome/browser/ui/webui/settings/ash/search/search_tag_registry.h"
 #include "chrome/browser/ui/webui/settings/captions_handler.h"
 #include "chrome/browser/ui/webui/settings/chromeos/accessibility_handler.h"
+#include "chrome/browser/ui/webui/settings/chromeos/search/search_tag_registry.h"
 #include "chrome/browser/ui/webui/settings/chromeos/switch_access_handler.h"
 #include "chrome/browser/ui/webui/settings/chromeos/tts_handler.h"
 #include "chrome/browser/ui/webui/settings/font_handler.h"
diff --git a/chrome/browser/ui/webui/settings/chromeos/apps_section.cc b/chrome/browser/ui/webui/settings/chromeos/apps_section.cc
index 77ab8af..4774000 100644
--- a/chrome/browser/ui/webui/settings/chromeos/apps_section.cc
+++ b/chrome/browser/ui/webui/settings/chromeos/apps_section.cc
@@ -19,10 +19,10 @@
 #include "chrome/browser/ash/plugin_vm/plugin_vm_pref_names.h"
 #include "chrome/browser/ash/plugin_vm/plugin_vm_util.h"
 #include "chrome/browser/ui/app_list/arc/arc_app_utils.h"
-#include "chrome/browser/ui/webui/settings/ash/search/search_tag_registry.h"
 #include "chrome/browser/ui/webui/settings/chromeos/android_apps_handler.h"
 #include "chrome/browser/ui/webui/settings/chromeos/guest_os_handler.h"
 #include "chrome/browser/ui/webui/settings/chromeos/plugin_vm_handler.h"
+#include "chrome/browser/ui/webui/settings/chromeos/search/search_tag_registry.h"
 #include "chrome/browser/ui/webui/webui_util.h"
 #include "chrome/common/chrome_features.h"
 #include "chrome/common/url_constants.h"
diff --git a/chrome/browser/ui/webui/settings/chromeos/bluetooth_section.cc b/chrome/browser/ui/webui/settings/chromeos/bluetooth_section.cc
index cc4a119..c108923 100644
--- a/chrome/browser/ui/webui/settings/chromeos/bluetooth_section.cc
+++ b/chrome/browser/ui/webui/settings/chromeos/bluetooth_section.cc
@@ -10,12 +10,12 @@
 #include "base/metrics/histogram_functions.h"
 #include "base/no_destructor.h"
 #include "chrome/browser/ui/webui/chromeos/bluetooth_shared_load_time_data_provider.h"
-#include "chrome/browser/ui/webui/settings/ash/search/search.mojom.h"
-#include "chrome/browser/ui/webui/settings/ash/search/search_result_icon.mojom.h"
-#include "chrome/browser/ui/webui/settings/ash/search/search_tag_registry.h"
 #include "chrome/browser/ui/webui/settings/chromeos/bluetooth_handler.h"
 #include "chrome/browser/ui/webui/settings/chromeos/constants/routes.mojom.h"
 #include "chrome/browser/ui/webui/settings/chromeos/constants/setting.mojom.h"
+#include "chrome/browser/ui/webui/settings/chromeos/search/search.mojom.h"
+#include "chrome/browser/ui/webui/settings/chromeos/search/search_result_icon.mojom.h"
+#include "chrome/browser/ui/webui/settings/chromeos/search/search_tag_registry.h"
 #include "chrome/browser/ui/webui/webui_util.h"
 #include "chrome/common/webui_url_constants.h"
 #include "chrome/grit/generated_resources.h"
diff --git a/chrome/browser/ui/webui/settings/chromeos/crostini_section.cc b/chrome/browser/ui/webui/settings/chromeos/crostini_section.cc
index 75d0089..21fa9f10 100644
--- a/chrome/browser/ui/webui/settings/chromeos/crostini_section.cc
+++ b/chrome/browser/ui/webui/settings/chromeos/crostini_section.cc
@@ -16,9 +16,9 @@
 #include "chrome/browser/ash/profiles/profile_helper.h"
 #include "chrome/browser/policy/profile_policy_connector.h"
 #include "chrome/browser/profiles/profile.h"
-#include "chrome/browser/ui/webui/settings/ash/search/search_tag_registry.h"
 #include "chrome/browser/ui/webui/settings/chromeos/crostini_handler.h"
 #include "chrome/browser/ui/webui/settings/chromeos/guest_os_handler.h"
+#include "chrome/browser/ui/webui/settings/chromeos/search/search_tag_registry.h"
 #include "chrome/browser/ui/webui/webui_util.h"
 #include "chrome/common/url_constants.h"
 #include "chrome/grit/generated_resources.h"
diff --git a/chrome/browser/ui/webui/settings/chromeos/date_time_section.cc b/chrome/browser/ui/webui/settings/chromeos/date_time_section.cc
index 5280753..9c65bcd 100644
--- a/chrome/browser/ui/webui/settings/chromeos/date_time_section.cc
+++ b/chrome/browser/ui/webui/settings/chromeos/date_time_section.cc
@@ -12,8 +12,8 @@
 #include "base/strings/utf_string_conversions.h"
 #include "chrome/browser/ash/system/timezone_util.h"
 #include "chrome/browser/browser_process.h"
-#include "chrome/browser/ui/webui/settings/ash/search/search_tag_registry.h"
 #include "chrome/browser/ui/webui/settings/chromeos/date_time_handler.h"
+#include "chrome/browser/ui/webui/settings/chromeos/search/search_tag_registry.h"
 #include "chrome/browser/ui/webui/webui_util.h"
 #include "chrome/common/url_constants.h"
 #include "chrome/grit/generated_resources.h"
diff --git a/chrome/browser/ui/webui/settings/chromeos/device_section.cc b/chrome/browser/ui/webui/settings/chromeos/device_section.cc
index 7be9b02..486d5062 100644
--- a/chrome/browser/ui/webui/settings/chromeos/device_section.cc
+++ b/chrome/browser/ui/webui/settings/chromeos/device_section.cc
@@ -16,13 +16,13 @@
 #include "base/strings/string_number_conversions.h"
 #include "base/strings/utf_string_conversions.h"
 #include "chrome/browser/ash/login/demo_mode/demo_session.h"
-#include "chrome/browser/ui/webui/settings/ash/search/search_tag_registry.h"
 #include "chrome/browser/ui/webui/settings/chromeos/device_display_handler.h"
 #include "chrome/browser/ui/webui/settings/chromeos/device_keyboard_handler.h"
 #include "chrome/browser/ui/webui/settings/chromeos/device_pointer_handler.h"
 #include "chrome/browser/ui/webui/settings/chromeos/device_power_handler.h"
 #include "chrome/browser/ui/webui/settings/chromeos/device_stylus_handler.h"
 #include "chrome/browser/ui/webui/settings/chromeos/os_settings_features_util.h"
+#include "chrome/browser/ui/webui/settings/chromeos/search/search_tag_registry.h"
 #include "chrome/browser/ui/webui/webui_util.h"
 #include "chrome/common/chrome_features.h"
 #include "chrome/common/url_constants.h"
diff --git a/chrome/browser/ui/webui/settings/chromeos/files_section.cc b/chrome/browser/ui/webui/settings/chromeos/files_section.cc
index 7f1bd7b..ff4ff98 100644
--- a/chrome/browser/ui/webui/settings/chromeos/files_section.cc
+++ b/chrome/browser/ui/webui/settings/chromeos/files_section.cc
@@ -9,7 +9,7 @@
 #include "chrome/browser/ash/profiles/profile_helper.h"
 #include "chrome/browser/ui/webui/chromeos/smb_shares/smb_handler.h"
 #include "chrome/browser/ui/webui/chromeos/smb_shares/smb_shares_localized_strings_provider.h"
-#include "chrome/browser/ui/webui/settings/ash/search/search_tag_registry.h"
+#include "chrome/browser/ui/webui/settings/chromeos/search/search_tag_registry.h"
 #include "chrome/browser/ui/webui/webui_util.h"
 #include "chrome/common/url_constants.h"
 #include "chrome/grit/generated_resources.h"
diff --git a/chrome/browser/ui/webui/settings/chromeos/hierarchy.h b/chrome/browser/ui/webui/settings/chromeos/hierarchy.h
index 9051051..8ca5a79 100644
--- a/chrome/browser/ui/webui/settings/chromeos/hierarchy.h
+++ b/chrome/browser/ui/webui/settings/chromeos/hierarchy.h
@@ -10,10 +10,10 @@
 #include <utility>
 #include <vector>
 
-#include "chrome/browser/ui/webui/settings/ash/search/search.mojom.h"
 #include "chrome/browser/ui/webui/settings/chromeos/constants/routes.mojom.h"
 #include "chrome/browser/ui/webui/settings/chromeos/constants/setting.mojom.h"
 #include "chrome/browser/ui/webui/settings/chromeos/os_settings_identifier.h"
+#include "chrome/browser/ui/webui/settings/chromeos/search/search.mojom.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace chromeos {
diff --git a/chrome/browser/ui/webui/settings/chromeos/internet_section.cc b/chrome/browser/ui/webui/settings/chromeos/internet_section.cc
index 0982b45..caab949 100644
--- a/chrome/browser/ui/webui/settings/chromeos/internet_section.cc
+++ b/chrome/browser/ui/webui/settings/chromeos/internet_section.cc
@@ -14,9 +14,9 @@
 #include "base/no_destructor.h"
 #include "base/strings/stringprintf.h"
 #include "chrome/browser/ui/webui/chromeos/cellular_setup/cellular_setup_localized_strings_provider.h"
-#include "chrome/browser/ui/webui/settings/ash/search/search_tag_registry.h"
 #include "chrome/browser/ui/webui/settings/chromeos/constants/routes.mojom.h"
 #include "chrome/browser/ui/webui/settings/chromeos/internet_handler.h"
+#include "chrome/browser/ui/webui/settings/chromeos/search/search_tag_registry.h"
 #include "chrome/browser/ui/webui/webui_util.h"
 #include "chrome/common/chrome_features.h"
 #include "chrome/common/url_constants.h"
diff --git a/chrome/browser/ui/webui/settings/chromeos/kerberos_section.cc b/chrome/browser/ui/webui/settings/chromeos/kerberos_section.cc
index caa6029e..4bda75a 100644
--- a/chrome/browser/ui/webui/settings/chromeos/kerberos_section.cc
+++ b/chrome/browser/ui/webui/settings/chromeos/kerberos_section.cc
@@ -6,8 +6,8 @@
 
 #include "base/no_destructor.h"
 #include "chrome/browser/profiles/profile.h"
-#include "chrome/browser/ui/webui/settings/ash/search/search_tag_registry.h"
 #include "chrome/browser/ui/webui/settings/chromeos/kerberos_accounts_handler.h"
+#include "chrome/browser/ui/webui/settings/chromeos/search/search_tag_registry.h"
 #include "chrome/grit/generated_resources.h"
 #include "content/public/browser/web_ui.h"
 #include "content/public/browser/web_ui_data_source.h"
diff --git a/chrome/browser/ui/webui/settings/chromeos/languages_section.cc b/chrome/browser/ui/webui/settings/chromeos/languages_section.cc
index 6bc185f2..943f7d3 100644
--- a/chrome/browser/ui/webui/settings/chromeos/languages_section.cc
+++ b/chrome/browser/ui/webui/settings/chromeos/languages_section.cc
@@ -10,8 +10,8 @@
 #include "base/no_destructor.h"
 #include "base/strings/utf_string_conversions.h"
 #include "chrome/browser/profiles/profile.h"
-#include "chrome/browser/ui/webui/settings/ash/search/search_tag_registry.h"
 #include "chrome/browser/ui/webui/settings/chromeos/os_settings_features_util.h"
+#include "chrome/browser/ui/webui/settings/chromeos/search/search_tag_registry.h"
 #include "chrome/browser/ui/webui/settings/languages_handler.h"
 #include "chrome/browser/ui/webui/webui_util.h"
 #include "chrome/common/url_constants.h"
diff --git a/chrome/browser/ui/webui/settings/chromeos/multidevice_section.cc b/chrome/browser/ui/webui/settings/chromeos/multidevice_section.cc
index bfb0598d..767f6c34 100644
--- a/chrome/browser/ui/webui/settings/chromeos/multidevice_section.cc
+++ b/chrome/browser/ui/webui/settings/chromeos/multidevice_section.cc
@@ -23,8 +23,8 @@
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/ash/session_controller_client_impl.h"
 #include "chrome/browser/ui/webui/nearby_share/shared_resources.h"
-#include "chrome/browser/ui/webui/settings/ash/search/search_tag_registry.h"
 #include "chrome/browser/ui/webui/settings/chromeos/multidevice_handler.h"
+#include "chrome/browser/ui/webui/settings/chromeos/search/search_tag_registry.h"
 #include "chrome/browser/ui/webui/settings/shared_settings_localized_strings_provider.h"
 #include "chrome/browser/ui/webui/webui_util.h"
 #include "chrome/common/url_constants.h"
diff --git a/chrome/browser/ui/webui/settings/chromeos/os_settings_manager.cc b/chrome/browser/ui/webui/settings/chromeos/os_settings_manager.cc
index 7ee91f9..f7af139b 100644
--- a/chrome/browser/ui/webui/settings/chromeos/os_settings_manager.cc
+++ b/chrome/browser/ui/webui/settings/chromeos/os_settings_manager.cc
@@ -7,10 +7,10 @@
 #include "base/bind.h"
 #include "base/feature_list.h"
 #include "chrome/browser/ui/webui/settings/ash/os_apps_page/app_notification_handler.h"
-#include "chrome/browser/ui/webui/settings/ash/search/search_handler.h"
-#include "chrome/browser/ui/webui/settings/ash/search/search_tag_registry.h"
 #include "chrome/browser/ui/webui/settings/chromeos/hierarchy.h"
 #include "chrome/browser/ui/webui/settings/chromeos/os_settings_sections.h"
+#include "chrome/browser/ui/webui/settings/chromeos/search/search_handler.h"
+#include "chrome/browser/ui/webui/settings/chromeos/search/search_tag_registry.h"
 #include "chrome/browser/ui/webui/settings/chromeos/settings_user_action_tracker.h"
 #include "content/public/browser/web_ui_data_source.h"
 #include "ui/base/l10n/l10n_util.h"
diff --git a/chrome/browser/ui/webui/settings/chromeos/os_settings_section.h b/chrome/browser/ui/webui/settings/chromeos/os_settings_section.h
index 49119bbf..6ea23f8 100644
--- a/chrome/browser/ui/webui/settings/chromeos/os_settings_section.h
+++ b/chrome/browser/ui/webui/settings/chromeos/os_settings_section.h
@@ -11,10 +11,10 @@
 #include "base/containers/span.h"
 #include "base/gtest_prod_util.h"
 #include "base/values.h"
-#include "chrome/browser/ui/webui/settings/ash/search/search.mojom.h"
-#include "chrome/browser/ui/webui/settings/ash/search/search_concept.h"
 #include "chrome/browser/ui/webui/settings/chromeos/constants/routes.mojom.h"
 #include "chrome/browser/ui/webui/settings/chromeos/constants/setting.mojom.h"
+#include "chrome/browser/ui/webui/settings/chromeos/search/search.mojom.h"
+#include "chrome/browser/ui/webui/settings/chromeos/search/search_concept.h"
 
 class Profile;
 
diff --git a/chrome/browser/ui/webui/settings/chromeos/os_settings_ui.cc b/chrome/browser/ui/webui/settings/chromeos/os_settings_ui.cc
index e781f7bd..79eebde 100644
--- a/chrome/browser/ui/webui/settings/chromeos/os_settings_ui.cc
+++ b/chrome/browser/ui/webui/settings/chromeos/os_settings_ui.cc
@@ -18,11 +18,11 @@
 #include "chrome/browser/nearby_sharing/nearby_sharing_service_impl.h"
 #include "chrome/browser/ui/webui/managed_ui_handler.h"
 #include "chrome/browser/ui/webui/settings/ash/os_apps_page/app_notification_handler.h"
-#include "chrome/browser/ui/webui/settings/ash/search/search_handler.h"
 #include "chrome/browser/ui/webui/settings/chromeos/device_storage_handler.h"
 #include "chrome/browser/ui/webui/settings/chromeos/os_settings_manager.h"
 #include "chrome/browser/ui/webui/settings/chromeos/os_settings_manager_factory.h"
 #include "chrome/browser/ui/webui/settings/chromeos/pref_names.h"
+#include "chrome/browser/ui/webui/settings/chromeos/search/search_handler.h"
 #include "chrome/browser/ui/webui/settings/chromeos/settings_user_action_tracker.h"
 #include "chrome/browser/ui/webui/webui_util.h"
 #include "chrome/common/webui_url_constants.h"
diff --git a/chrome/browser/ui/webui/settings/chromeos/os_settings_ui.h b/chrome/browser/ui/webui/settings/chromeos/os_settings_ui.h
index b49fbf7..28600c2 100644
--- a/chrome/browser/ui/webui/settings/chromeos/os_settings_ui.h
+++ b/chrome/browser/ui/webui/settings/chromeos/os_settings_ui.h
@@ -13,7 +13,7 @@
 #include "chrome/browser/ui/webui/nearby_share/nearby_share.mojom.h"
 #include "chrome/browser/ui/webui/nearby_share/public/mojom/nearby_share_settings.mojom.h"
 #include "chrome/browser/ui/webui/settings/ash/os_apps_page/mojom/app_notification_handler.mojom-forward.h"
-#include "chrome/browser/ui/webui/settings/ash/search/user_action_recorder.mojom-forward.h"
+#include "chrome/browser/ui/webui/settings/chromeos/search/user_action_recorder.mojom-forward.h"
 #include "chrome/browser/ui/webui/webui_load_timer.h"
 #include "chromeos/services/bluetooth_config/public/mojom/cros_bluetooth_config.mojom-forward.h"
 #include "chromeos/services/cellular_setup/public/mojom/cellular_setup.mojom-forward.h"
diff --git a/chrome/browser/ui/webui/settings/chromeos/people_section.cc b/chrome/browser/ui/webui/settings/chromeos/people_section.cc
index 546d5217..11229c41a 100644
--- a/chrome/browser/ui/webui/settings/chromeos/people_section.cc
+++ b/chrome/browser/ui/webui/settings/chromeos/people_section.cc
@@ -27,12 +27,12 @@
 #include "chrome/browser/supervised_user/supervised_user_service.h"
 #include "chrome/browser/ui/ui_features.h"
 #include "chrome/browser/ui/webui/chromeos/sync/os_sync_handler.h"
-#include "chrome/browser/ui/webui/settings/ash/search/search_tag_registry.h"
 #include "chrome/browser/ui/webui/settings/chromeos/account_manager_handler.h"
 #include "chrome/browser/ui/webui/settings/chromeos/fingerprint_handler.h"
 #include "chrome/browser/ui/webui/settings/chromeos/os_settings_features_util.h"
 #include "chrome/browser/ui/webui/settings/chromeos/parental_controls_handler.h"
 #include "chrome/browser/ui/webui/settings/chromeos/quick_unlock_handler.h"
+#include "chrome/browser/ui/webui/settings/chromeos/search/search_tag_registry.h"
 #include "chrome/browser/ui/webui/settings/people_handler.h"
 #include "chrome/browser/ui/webui/settings/profile_info_handler.h"
 #include "chrome/browser/ui/webui/settings/shared_settings_localized_strings_provider.h"
diff --git a/chrome/browser/ui/webui/settings/chromeos/personalization_section.cc b/chrome/browser/ui/webui/settings/chromeos/personalization_section.cc
index 9d508f3..ed47d5f 100644
--- a/chrome/browser/ui/webui/settings/chromeos/personalization_section.cc
+++ b/chrome/browser/ui/webui/settings/chromeos/personalization_section.cc
@@ -12,11 +12,11 @@
 #include "base/no_destructor.h"
 #include "base/strings/utf_string_conversions.h"
 #include "chrome/browser/profiles/profile.h"
-#include "chrome/browser/ui/webui/settings/ash/search/search_tag_registry.h"
 #include "chrome/browser/ui/webui/settings/chromeos/ambient_mode_handler.h"
 #include "chrome/browser/ui/webui/settings/chromeos/change_picture_handler.h"
 #include "chrome/browser/ui/webui/settings/chromeos/os_settings_features_util.h"
 #include "chrome/browser/ui/webui/settings/chromeos/personalization_hub_handler.h"
+#include "chrome/browser/ui/webui/settings/chromeos/search/search_tag_registry.h"
 #include "chrome/browser/ui/webui/settings/chromeos/wallpaper_handler.h"
 #include "chrome/browser/ui/webui/webui_util.h"
 #include "chrome/common/chrome_features.h"
diff --git a/chrome/browser/ui/webui/settings/chromeos/printing_section.cc b/chrome/browser/ui/webui/settings/chromeos/printing_section.cc
index fd472f7..0ac876f 100644
--- a/chrome/browser/ui/webui/settings/chromeos/printing_section.cc
+++ b/chrome/browser/ui/webui/settings/chromeos/printing_section.cc
@@ -5,8 +5,8 @@
 #include "chrome/browser/ui/webui/settings/chromeos/printing_section.h"
 
 #include "base/no_destructor.h"
-#include "chrome/browser/ui/webui/settings/ash/search/search_tag_registry.h"
 #include "chrome/browser/ui/webui/settings/chromeos/cups_printers_handler.h"
+#include "chrome/browser/ui/webui/settings/chromeos/search/search_tag_registry.h"
 #include "chrome/browser/ui/webui/webui_util.h"
 #include "chrome/common/chrome_features.h"
 #include "chrome/common/url_constants.h"
diff --git a/chrome/browser/ui/webui/settings/chromeos/privacy_section.cc b/chrome/browser/ui/webui/settings/chromeos/privacy_section.cc
index 84b5ed8..e79dfa7 100644
--- a/chrome/browser/ui/webui/settings/chromeos/privacy_section.cc
+++ b/chrome/browser/ui/webui/settings/chromeos/privacy_section.cc
@@ -15,10 +15,10 @@
 #include "chrome/browser/ash/login/quick_unlock/quick_unlock_utils.h"
 #include "chrome/browser/ash/profiles/profile_helper.h"
 #include "chrome/browser/browser_process.h"
-#include "chrome/browser/ui/webui/settings/ash/search/search_tag_registry.h"
 #include "chrome/browser/ui/webui/settings/chromeos/metrics_consent_handler.h"
 #include "chrome/browser/ui/webui/settings/chromeos/os_settings_features_util.h"
 #include "chrome/browser/ui/webui/settings/chromeos/peripheral_data_access_handler.h"
+#include "chrome/browser/ui/webui/settings/chromeos/search/search_tag_registry.h"
 #include "chrome/browser/ui/webui/settings/settings_secure_dns_handler.h"
 #include "chrome/browser/ui/webui/settings/shared_settings_localized_strings_provider.h"
 #include "chrome/browser/ui/webui/webui_util.h"
diff --git a/chrome/browser/ui/webui/settings/chromeos/reset_section.cc b/chrome/browser/ui/webui/settings/chromeos/reset_section.cc
index ef7fcfff..3f14606 100644
--- a/chrome/browser/ui/webui/settings/chromeos/reset_section.cc
+++ b/chrome/browser/ui/webui/settings/chromeos/reset_section.cc
@@ -6,7 +6,7 @@
 
 #include "base/no_destructor.h"
 #include "chrome/browser/browser_process.h"
-#include "chrome/browser/ui/webui/settings/ash/search/search_tag_registry.h"
+#include "chrome/browser/ui/webui/settings/chromeos/search/search_tag_registry.h"
 #include "chrome/browser/ui/webui/settings/reset_settings_handler.h"
 #include "chrome/browser/ui/webui/webui_util.h"
 #include "chrome/grit/chromium_strings.h"
diff --git a/chrome/browser/ui/webui/settings/ash/search/BUILD.gn b/chrome/browser/ui/webui/settings/chromeos/search/BUILD.gn
similarity index 87%
rename from chrome/browser/ui/webui/settings/ash/search/BUILD.gn
rename to chrome/browser/ui/webui/settings/chromeos/search/BUILD.gn
index 58ce0fb..0065095 100644
--- a/chrome/browser/ui/webui/settings/ash/search/BUILD.gn
+++ b/chrome/browser/ui/webui/settings/chromeos/search/BUILD.gn
@@ -15,7 +15,7 @@
   ]
 
   public_deps = [
-    "//chrome/browser/ui/webui/settings/chromeos/constants:mojom",
+    "../constants:mojom",
     "//mojo/public/mojom/base",
   ]
 }
diff --git a/chrome/browser/ui/webui/settings/ash/search/OWNERS b/chrome/browser/ui/webui/settings/chromeos/search/OWNERS
similarity index 100%
rename from chrome/browser/ui/webui/settings/ash/search/OWNERS
rename to chrome/browser/ui/webui/settings/chromeos/search/OWNERS
diff --git a/chrome/browser/ui/webui/settings/ash/search/per_session_settings_user_action_tracker.cc b/chrome/browser/ui/webui/settings/chromeos/search/per_session_settings_user_action_tracker.cc
similarity index 97%
rename from chrome/browser/ui/webui/settings/ash/search/per_session_settings_user_action_tracker.cc
rename to chrome/browser/ui/webui/settings/chromeos/search/per_session_settings_user_action_tracker.cc
index 6d0272a..7f568b2 100644
--- a/chrome/browser/ui/webui/settings/ash/search/per_session_settings_user_action_tracker.cc
+++ b/chrome/browser/ui/webui/settings/chromeos/search/per_session_settings_user_action_tracker.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/webui/settings/ash/search/per_session_settings_user_action_tracker.h"
+#include "chrome/browser/ui/webui/settings/chromeos/search/per_session_settings_user_action_tracker.h"
 
 #include "base/metrics/histogram_functions.h"
 
diff --git a/chrome/browser/ui/webui/settings/ash/search/per_session_settings_user_action_tracker.h b/chrome/browser/ui/webui/settings/chromeos/search/per_session_settings_user_action_tracker.h
similarity index 88%
rename from chrome/browser/ui/webui/settings/ash/search/per_session_settings_user_action_tracker.h
rename to chrome/browser/ui/webui/settings/chromeos/search/per_session_settings_user_action_tracker.h
index 932a617..da5c3706 100644
--- a/chrome/browser/ui/webui/settings/ash/search/per_session_settings_user_action_tracker.h
+++ b/chrome/browser/ui/webui/settings/chromeos/search/per_session_settings_user_action_tracker.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_UI_WEBUI_SETTINGS_ASH_SEARCH_PER_SESSION_SETTINGS_USER_ACTION_TRACKER_H_
-#define CHROME_BROWSER_UI_WEBUI_SETTINGS_ASH_SEARCH_PER_SESSION_SETTINGS_USER_ACTION_TRACKER_H_
+#ifndef CHROME_BROWSER_UI_WEBUI_SETTINGS_CHROMEOS_SEARCH_PER_SESSION_SETTINGS_USER_ACTION_TRACKER_H_
+#define CHROME_BROWSER_UI_WEBUI_SETTINGS_CHROMEOS_SEARCH_PER_SESSION_SETTINGS_USER_ACTION_TRACKER_H_
 
 #include "base/time/time.h"
 #include "mojo/public/cpp/bindings/pending_receiver.h"
@@ -64,4 +64,4 @@
 }  // namespace settings
 }  // namespace chromeos
 
-#endif  // CHROME_BROWSER_UI_WEBUI_SETTINGS_ASH_SEARCH_PER_SESSION_SETTINGS_USER_ACTION_TRACKER_H_
+#endif  // CHROME_BROWSER_UI_WEBUI_SETTINGS_CHROMEOS_SEARCH_PER_SESSION_SETTINGS_USER_ACTION_TRACKER_H_
diff --git a/chrome/browser/ui/webui/settings/ash/search/per_session_settings_user_action_tracker_unittest.cc b/chrome/browser/ui/webui/settings/chromeos/search/per_session_settings_user_action_tracker_unittest.cc
similarity index 98%
rename from chrome/browser/ui/webui/settings/ash/search/per_session_settings_user_action_tracker_unittest.cc
rename to chrome/browser/ui/webui/settings/chromeos/search/per_session_settings_user_action_tracker_unittest.cc
index 96640f1..74f716e 100644
--- a/chrome/browser/ui/webui/settings/ash/search/per_session_settings_user_action_tracker_unittest.cc
+++ b/chrome/browser/ui/webui/settings/chromeos/search/per_session_settings_user_action_tracker_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/ui/webui/settings/ash/search/per_session_settings_user_action_tracker.h"
+#include "chrome/browser/ui/webui/settings/chromeos/search/per_session_settings_user_action_tracker.h"
 
 #include "base/test/metrics/histogram_tester.h"
 #include "base/test/task_environment.h"
diff --git a/chrome/browser/ui/webui/settings/ash/search/search.mojom b/chrome/browser/ui/webui/settings/chromeos/search/search.mojom
similarity index 98%
rename from chrome/browser/ui/webui/settings/ash/search/search.mojom
rename to chrome/browser/ui/webui/settings/chromeos/search/search.mojom
index a3c098d..d63ae03 100644
--- a/chrome/browser/ui/webui/settings/ash/search/search.mojom
+++ b/chrome/browser/ui/webui/settings/chromeos/search/search.mojom
@@ -6,7 +6,7 @@
 
 import "chrome/browser/ui/webui/settings/chromeos/constants/routes.mojom";
 import "chrome/browser/ui/webui/settings/chromeos/constants/setting.mojom";
-import "chrome/browser/ui/webui/settings/ash/search/search_result_icon.mojom";
+import "chrome/browser/ui/webui/settings/chromeos/search/search_result_icon.mojom";
 import "mojo/public/mojom/base/string16.mojom";
 
 // Describes the type of settings result.
diff --git a/chrome/browser/ui/webui/settings/ash/search/search_concept.h b/chrome/browser/ui/webui/settings/chromeos/search/search_concept.h
similarity index 88%
rename from chrome/browser/ui/webui/settings/ash/search/search_concept.h
rename to chrome/browser/ui/webui/settings/chromeos/search/search_concept.h
index 45a35d1..156e6c81 100644
--- a/chrome/browser/ui/webui/settings/ash/search/search_concept.h
+++ b/chrome/browser/ui/webui/settings/chromeos/search/search_concept.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_UI_WEBUI_SETTINGS_ASH_SEARCH_SEARCH_CONCEPT_H_
-#define CHROME_BROWSER_UI_WEBUI_SETTINGS_ASH_SEARCH_SEARCH_CONCEPT_H_
+#ifndef CHROME_BROWSER_UI_WEBUI_SETTINGS_CHROMEOS_SEARCH_SEARCH_CONCEPT_H_
+#define CHROME_BROWSER_UI_WEBUI_SETTINGS_CHROMEOS_SEARCH_SEARCH_CONCEPT_H_
 
-#include "chrome/browser/ui/webui/settings/ash/search/search.mojom.h"
-#include "chrome/browser/ui/webui/settings/ash/search/search_result_icon.mojom.h"
 #include "chrome/browser/ui/webui/settings/chromeos/constants/routes.mojom.h"
 #include "chrome/browser/ui/webui/settings/chromeos/constants/setting.mojom.h"
 #include "chrome/browser/ui/webui/settings/chromeos/os_settings_identifier.h"
+#include "chrome/browser/ui/webui/settings/chromeos/search/search.mojom.h"
+#include "chrome/browser/ui/webui/settings/chromeos/search/search_result_icon.mojom.h"
 
 namespace chromeos {
 namespace settings {
@@ -71,4 +71,4 @@
 }  // namespace settings
 }  // namespace chromeos
 
-#endif  // CHROME_BROWSER_UI_WEBUI_SETTINGS_ASH_SEARCH_SEARCH_CONCEPT_H_
+#endif  // CHROME_BROWSER_UI_WEBUI_SETTINGS_CHROMEOS_SEARCH_SEARCH_CONCEPT_H_
diff --git a/chrome/browser/ui/webui/settings/ash/search/search_handler.cc b/chrome/browser/ui/webui/settings/chromeos/search/search_handler.cc
similarity index 97%
rename from chrome/browser/ui/webui/settings/ash/search/search_handler.cc
rename to chrome/browser/ui/webui/settings/chromeos/search/search_handler.cc
index f8fb158..34fc3c59 100644
--- a/chrome/browser/ui/webui/settings/ash/search/search_handler.cc
+++ b/chrome/browser/ui/webui/settings/chromeos/search/search_handler.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/ui/webui/settings/ash/search/search_handler.h"
+#include "chrome/browser/ui/webui/settings/chromeos/search/search_handler.h"
 
 #include <algorithm>
 
 #include "base/strings/string_number_conversions.h"
 #include "chrome/browser/browser_process.h"
-#include "chrome/browser/ui/webui/settings/ash/search/search_concept.h"
-#include "chrome/browser/ui/webui/settings/ash/search/search_result_icon.mojom.h"
 #include "chrome/browser/ui/webui/settings/chromeos/hierarchy.h"
 #include "chrome/browser/ui/webui/settings/chromeos/os_settings_sections.h"
+#include "chrome/browser/ui/webui/settings/chromeos/search/search_concept.h"
+#include "chrome/browser/ui/webui/settings/chromeos/search/search_result_icon.mojom.h"
 #include "chrome/grit/generated_resources.h"
 #include "chromeos/components/local_search_service/public/cpp/local_search_service_proxy.h"
 #include "ui/base/l10n/l10n_util.h"
diff --git a/chrome/browser/ui/webui/settings/ash/search/search_handler.h b/chrome/browser/ui/webui/settings/chromeos/search/search_handler.h
similarity index 91%
rename from chrome/browser/ui/webui/settings/ash/search/search_handler.h
rename to chrome/browser/ui/webui/settings/chromeos/search/search_handler.h
index 28effd6..3dbffcf 100644
--- a/chrome/browser/ui/webui/settings/ash/search/search_handler.h
+++ b/chrome/browser/ui/webui/settings/chromeos/search/search_handler.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_UI_WEBUI_SETTINGS_ASH_SEARCH_SEARCH_HANDLER_H_
-#define CHROME_BROWSER_UI_WEBUI_SETTINGS_ASH_SEARCH_SEARCH_HANDLER_H_
+#ifndef CHROME_BROWSER_UI_WEBUI_SETTINGS_CHROMEOS_SEARCH_SEARCH_HANDLER_H_
+#define CHROME_BROWSER_UI_WEBUI_SETTINGS_CHROMEOS_SEARCH_SEARCH_HANDLER_H_
 
 #include <vector>
 
 #include "base/gtest_prod_util.h"
-#include "chrome/browser/ui/webui/settings/ash/search/search.mojom.h"
-#include "chrome/browser/ui/webui/settings/ash/search/search_tag_registry.h"
+#include "chrome/browser/ui/webui/settings/chromeos/search/search.mojom.h"
+#include "chrome/browser/ui/webui/settings/chromeos/search/search_tag_registry.h"
 #include "chromeos/components/local_search_service/public/cpp/local_search_service_proxy.h"
 #include "chromeos/components/local_search_service/public/mojom/index.mojom.h"
 #include "mojo/public/cpp/bindings/pending_receiver.h"
@@ -119,4 +119,4 @@
 }  // namespace settings
 }  // namespace chromeos
 
-#endif  // CHROME_BROWSER_UI_WEBUI_SETTINGS_ASH_SEARCH_SEARCH_HANDLER_H_
+#endif  // CHROME_BROWSER_UI_WEBUI_SETTINGS_CHROMEOS_SEARCH_SEARCH_HANDLER_H_
diff --git a/chrome/browser/ui/webui/settings/ash/search/search_handler_unittest.cc b/chrome/browser/ui/webui/settings/chromeos/search/search_handler_unittest.cc
similarity index 97%
rename from chrome/browser/ui/webui/settings/ash/search/search_handler_unittest.cc
rename to chrome/browser/ui/webui/settings/chromeos/search/search_handler_unittest.cc
index 757f81f12..c298276 100644
--- a/chrome/browser/ui/webui/settings/ash/search/search_handler_unittest.cc
+++ b/chrome/browser/ui/webui/settings/chromeos/search/search_handler_unittest.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/ui/webui/settings/ash/search/search_handler.h"
+#include "chrome/browser/ui/webui/settings/chromeos/search/search_handler.h"
 
 #include "base/no_destructor.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/test/task_environment.h"
-#include "chrome/browser/ui/webui/settings/ash/search/search.mojom-test-utils.h"
-#include "chrome/browser/ui/webui/settings/ash/search/search_tag_registry.h"
 #include "chrome/browser/ui/webui/settings/chromeos/constants/routes.mojom.h"
 #include "chrome/browser/ui/webui/settings/chromeos/fake_hierarchy.h"
 #include "chrome/browser/ui/webui/settings/chromeos/fake_os_settings_sections.h"
+#include "chrome/browser/ui/webui/settings/chromeos/search/search.mojom-test-utils.h"
+#include "chrome/browser/ui/webui/settings/chromeos/search/search_tag_registry.h"
 #include "chrome/grit/generated_resources.h"
 #include "chromeos/components/local_search_service/public/cpp/local_search_service_proxy.h"
 #include "mojo/public/cpp/bindings/remote.h"
diff --git a/chrome/browser/ui/webui/settings/ash/search/search_result_icon.mojom b/chrome/browser/ui/webui/settings/chromeos/search/search_result_icon.mojom
similarity index 100%
rename from chrome/browser/ui/webui/settings/ash/search/search_result_icon.mojom
rename to chrome/browser/ui/webui/settings/chromeos/search/search_result_icon.mojom
diff --git a/chrome/browser/ui/webui/settings/ash/search/search_tag_registry.cc b/chrome/browser/ui/webui/settings/chromeos/search/search_tag_registry.cc
similarity index 97%
rename from chrome/browser/ui/webui/settings/ash/search/search_tag_registry.cc
rename to chrome/browser/ui/webui/settings/chromeos/search/search_tag_registry.cc
index 17de7c6..1772dcb 100644
--- a/chrome/browser/ui/webui/settings/ash/search/search_tag_registry.cc
+++ b/chrome/browser/ui/webui/settings/chromeos/search/search_tag_registry.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/webui/settings/ash/search/search_tag_registry.h"
+#include "chrome/browser/ui/webui/settings/chromeos/search/search_tag_registry.h"
 
 #include <algorithm>
 #include <sstream>
@@ -10,7 +10,7 @@
 #include "base/feature_list.h"
 #include "base/strings/string_number_conversions.h"
 #include "chrome/browser/browser_process.h"
-#include "chrome/browser/ui/webui/settings/ash/search/search_concept.h"
+#include "chrome/browser/ui/webui/settings/chromeos/search/search_concept.h"
 #include "chromeos/components/local_search_service/public/cpp/local_search_service_proxy.h"
 #include "ui/base/l10n/l10n_util.h"
 
diff --git a/chrome/browser/ui/webui/settings/ash/search/search_tag_registry.h b/chrome/browser/ui/webui/settings/chromeos/search/search_tag_registry.h
similarity index 94%
rename from chrome/browser/ui/webui/settings/ash/search/search_tag_registry.h
rename to chrome/browser/ui/webui/settings/chromeos/search/search_tag_registry.h
index d9f00d5..46e9e2b7 100644
--- a/chrome/browser/ui/webui/settings/ash/search/search_tag_registry.h
+++ b/chrome/browser/ui/webui/settings/chromeos/search/search_tag_registry.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_UI_WEBUI_SETTINGS_ASH_SEARCH_SEARCH_TAG_REGISTRY_H_
-#define CHROME_BROWSER_UI_WEBUI_SETTINGS_ASH_SEARCH_SEARCH_TAG_REGISTRY_H_
+#ifndef CHROME_BROWSER_UI_WEBUI_SETTINGS_CHROMEOS_SEARCH_SEARCH_TAG_REGISTRY_H_
+#define CHROME_BROWSER_UI_WEBUI_SETTINGS_CHROMEOS_SEARCH_SEARCH_TAG_REGISTRY_H_
 
 #include <unordered_map>
 #include <utility>
@@ -79,7 +79,6 @@
   // returned by the LocalSearchService. If no metadata is available, null
   // is returned.
   const SearchConcept* GetTagMetadata(const std::string& result_id) const;
-
  private:
   FRIEND_TEST_ALL_PREFIXES(SearchTagRegistryTest, AddAndRemove);
 
@@ -109,4 +108,4 @@
 }  // namespace settings
 }  // namespace chromeos
 
-#endif  // CHROME_BROWSER_UI_WEBUI_SETTINGS_ASH_SEARCH_SEARCH_TAG_REGISTRY_H_
+#endif  // CHROME_BROWSER_UI_WEBUI_SETTINGS_CHROMEOS_SEARCH_SEARCH_TAG_REGISTRY_H_
diff --git a/chrome/browser/ui/webui/settings/ash/search/search_tag_registry_unittest.cc b/chrome/browser/ui/webui/settings/chromeos/search/search_tag_registry_unittest.cc
similarity index 96%
rename from chrome/browser/ui/webui/settings/ash/search/search_tag_registry_unittest.cc
rename to chrome/browser/ui/webui/settings/chromeos/search/search_tag_registry_unittest.cc
index bd4e862..5611dab7 100644
--- a/chrome/browser/ui/webui/settings/ash/search/search_tag_registry_unittest.cc
+++ b/chrome/browser/ui/webui/settings/chromeos/search/search_tag_registry_unittest.cc
@@ -2,13 +2,13 @@
 // 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/ash/search/search_tag_registry.h"
+#include "chrome/browser/ui/webui/settings/chromeos/search/search_tag_registry.h"
 
 #include "base/no_destructor.h"
 #include "base/test/task_environment.h"
 #include "chrome/browser/browser_process.h"
-#include "chrome/browser/ui/webui/settings/ash/search/search_concept.h"
 #include "chrome/browser/ui/webui/settings/chromeos/constants/routes.mojom.h"
+#include "chrome/browser/ui/webui/settings/chromeos/search/search_concept.h"
 #include "chrome/grit/generated_resources.h"
 #include "chromeos/components/local_search_service/public/mojom/index.mojom.h"
 #include "testing/gtest/include/gtest/gtest.h"
diff --git a/chrome/browser/ui/webui/settings/ash/search/user_action_recorder.mojom b/chrome/browser/ui/webui/settings/chromeos/search/user_action_recorder.mojom
similarity index 100%
rename from chrome/browser/ui/webui/settings/ash/search/user_action_recorder.mojom
rename to chrome/browser/ui/webui/settings/chromeos/search/user_action_recorder.mojom
diff --git a/chrome/browser/ui/webui/settings/chromeos/search_section.cc b/chrome/browser/ui/webui/settings/chromeos/search_section.cc
index d3c2fdaf..60150addd 100644
--- a/chrome/browser/ui/webui/settings/chromeos/search_section.cc
+++ b/chrome/browser/ui/webui/settings/chromeos/search_section.cc
@@ -15,8 +15,8 @@
 #include "chrome/browser/ash/assistant/assistant_util.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/webui/chromeos/assistant_optin/assistant_optin_utils.h"
-#include "chrome/browser/ui/webui/settings/ash/search/search_tag_registry.h"
 #include "chrome/browser/ui/webui/settings/chromeos/google_assistant_handler.h"
+#include "chrome/browser/ui/webui/settings/chromeos/search/search_tag_registry.h"
 #include "chrome/browser/ui/webui/settings/search_engines_handler.h"
 #include "chrome/browser/ui/webui/webui_util.h"
 #include "chrome/common/url_constants.h"
diff --git a/chrome/browser/ui/webui/settings/chromeos/settings_user_action_tracker.h b/chrome/browser/ui/webui/settings/chromeos/settings_user_action_tracker.h
index ff57567..f05770d 100644
--- a/chrome/browser/ui/webui/settings/chromeos/settings_user_action_tracker.h
+++ b/chrome/browser/ui/webui/settings/chromeos/settings_user_action_tracker.h
@@ -8,9 +8,9 @@
 #include <memory>
 
 #include "base/gtest_prod_util.h"
-#include "chrome/browser/ui/webui/settings/ash/search/per_session_settings_user_action_tracker.h"
-#include "chrome/browser/ui/webui/settings/ash/search/user_action_recorder.mojom.h"
 #include "chrome/browser/ui/webui/settings/chromeos/constants/setting.mojom.h"
+#include "chrome/browser/ui/webui/settings/chromeos/search/per_session_settings_user_action_tracker.h"
+#include "chrome/browser/ui/webui/settings/chromeos/search/user_action_recorder.mojom.h"
 #include "mojo/public/cpp/bindings/pending_receiver.h"
 #include "mojo/public/cpp/bindings/receiver.h"
 
diff --git a/chrome/browser/ui/webui/settings/chromeos/settings_user_action_tracker_unittest.cc b/chrome/browser/ui/webui/settings/chromeos/settings_user_action_tracker_unittest.cc
index 9641684..0adb5d6 100644
--- a/chrome/browser/ui/webui/settings/chromeos/settings_user_action_tracker_unittest.cc
+++ b/chrome/browser/ui/webui/settings/chromeos/settings_user_action_tracker_unittest.cc
@@ -6,12 +6,12 @@
 
 #include "base/test/metrics/histogram_tester.h"
 #include "base/values.h"
-#include "chrome/browser/ui/webui/settings/ash/search/per_session_settings_user_action_tracker.h"
-#include "chrome/browser/ui/webui/settings/ash/search/user_action_recorder.mojom.h"
 #include "chrome/browser/ui/webui/settings/chromeos/constants/setting.mojom.h"
 #include "chrome/browser/ui/webui/settings/chromeos/fake_hierarchy.h"
 #include "chrome/browser/ui/webui/settings/chromeos/fake_os_settings_section.h"
 #include "chrome/browser/ui/webui/settings/chromeos/fake_os_settings_sections.h"
+#include "chrome/browser/ui/webui/settings/chromeos/search/per_session_settings_user_action_tracker.h"
+#include "chrome/browser/ui/webui/settings/chromeos/search/user_action_recorder.mojom.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace chromeos {
diff --git a/chrome/browser/web_applications/externally_managed_app_manager_impl_browsertest.cc b/chrome/browser/web_applications/externally_managed_app_manager_impl_browsertest.cc
index e0761cc..e6f49b99 100644
--- a/chrome/browser/web_applications/externally_managed_app_manager_impl_browsertest.cc
+++ b/chrome/browser/web_applications/externally_managed_app_manager_impl_browsertest.cc
@@ -62,8 +62,9 @@
   }
 
   void InstallApp(ExternalInstallOptions install_options) {
-    result_code_ =
+    auto result =
         ExternallyManagedAppManagerInstall(profile(), install_options);
+    result_code_ = result.code;
   }
 
   void CheckServiceWorkerStatus(const GURL& url,
diff --git a/chrome/browser/web_applications/os_integration/web_app_file_handler_registration_linux_browsertest.cc b/chrome/browser/web_applications/os_integration/web_app_file_handler_registration_linux_browsertest.cc
index 20a751a..6ab8cd34 100644
--- a/chrome/browser/web_applications/os_integration/web_app_file_handler_registration_linux_browsertest.cc
+++ b/chrome/browser/web_applications/os_integration/web_app_file_handler_registration_linux_browsertest.cc
@@ -62,8 +62,9 @@
   }
 
   void InstallApp(ExternalInstallOptions install_options) {
-    result_code_ = web_app::ExternallyManagedAppManagerInstall(
+    auto result = web_app::ExternallyManagedAppManagerInstall(
         browser()->profile(), install_options);
+    result_code_ = result.code;
   }
 
   base::test::ScopedFeatureList scoped_feature_list_;
diff --git a/chrome/build/linux.pgo.txt b/chrome/build/linux.pgo.txt
index 2474cf2e..a114758 100644
--- a/chrome/build/linux.pgo.txt
+++ b/chrome/build/linux.pgo.txt
@@ -1 +1 @@
-chrome-linux-main-1645725546-b53c30b8c73e1f07b0010ded6d07c7d4d337efe3.profdata
+chrome-linux-main-1645768781-bb464cc52b278b12cc2abb848b19cebc75d215ed.profdata
diff --git a/chrome/build/mac-arm.pgo.txt b/chrome/build/mac-arm.pgo.txt
index 71ac0011..abaec4ee 100644
--- a/chrome/build/mac-arm.pgo.txt
+++ b/chrome/build/mac-arm.pgo.txt
@@ -1 +1 @@
-chrome-mac-arm-main-1645725546-ae525cbe5c7e91c6041580b18b86ec2b9bf73f54.profdata
+chrome-mac-arm-main-1645768781-49befff043bd9e3516398aa4f22b62a6ba4d412a.profdata
diff --git a/chrome/build/mac.pgo.txt b/chrome/build/mac.pgo.txt
index 5d945a3..365cbfe2 100644
--- a/chrome/build/mac.pgo.txt
+++ b/chrome/build/mac.pgo.txt
@@ -1 +1 @@
-chrome-mac-main-1645725546-ea12a24d5e25cecb1388e4c7f3e4d0b777134634.profdata
+chrome-mac-main-1645747137-9ec32e48b5cc7eb5891c059f03441638585af5b1.profdata
diff --git a/chrome/build/win32.pgo.txt b/chrome/build/win32.pgo.txt
index 96116ede..990fe8f 100644
--- a/chrome/build/win32.pgo.txt
+++ b/chrome/build/win32.pgo.txt
@@ -1 +1 @@
-chrome-win32-main-1645736298-0bbb2e587ea8d357893033f7560c7f635d8bdf91.profdata
+chrome-win32-main-1645757816-6e56a6556841fa3052b891b0f2c64671736c3537.profdata
diff --git a/chrome/build/win64.pgo.txt b/chrome/build/win64.pgo.txt
index e05b36e..eed3419 100644
--- a/chrome/build/win64.pgo.txt
+++ b/chrome/build/win64.pgo.txt
@@ -1 +1 @@
-chrome-win64-main-1645736298-1f88af4fba2e513be7f01a82d64e31bce7ea806d.profdata
+chrome-win64-main-1645747137-6f25eb0b2ebd939ae2f91205eecc6053bb478302.profdata
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn
index 58f23581..05774730 100644
--- a/chrome/test/BUILD.gn
+++ b/chrome/test/BUILD.gn
@@ -1254,6 +1254,7 @@
       "//components/optimization_guide/content/renderer",
       "//components/optimization_guide/core",
       "//components/optimization_guide/core:bloomfilter",
+      "//components/optimization_guide/core:prediction",
       "//components/optimization_guide/core:test_support",
       "//components/optimization_guide/proto:optimization_guide_proto",
       "//components/page_load_metrics/browser",
@@ -3172,8 +3173,9 @@
 
     # Browser tests for functionality that is only intended to be present in
     # ash-chrome, not lacros-chrome.
-    if (is_chromeos_ash && !is_chromeos_lacros) {
+    if (is_chromeos_ash) {
       sources += [
+        "../browser/ash/crosapi/browser_data_migrator_browsertest.cc",
         "../browser/ash/crosapi/network_settings_service_ash_browsertest.cc",
         "../browser/ash/crosapi/screen_manager_ash_browsertest.cc",
       ]
@@ -4815,8 +4817,6 @@
     "../browser/notifications/persistent_notification_handler_unittest.cc",
     "../browser/notifications/platform_notification_service_unittest.cc",
     "../browser/optimization_guide/chrome_hints_manager_unittest.cc",
-    "../browser/optimization_guide/prediction/prediction_manager_unittest.cc",
-    "../browser/optimization_guide/prediction/prediction_model_download_manager_unittest.cc",
     "../browser/page_load_metrics/metrics_web_contents_observer_unittest.cc",
     "../browser/page_load_metrics/observers/aborts_page_load_metrics_observer_unittest.cc",
     "../browser/page_load_metrics/observers/core/amp_page_load_metrics_observer_unittest.cc",
diff --git a/chrome/test/data/iframe_cross_site_with_script.html b/chrome/test/data/iframe_cross_site_with_script.html
new file mode 100644
index 0000000..6aaf2126
--- /dev/null
+++ b/chrome/test/data/iframe_cross_site_with_script.html
@@ -0,0 +1,6 @@
+<html><head><title>cross-site iframe with script test</title></head>
+<body>
+<!-- This only works if the CrossSiteRedirector is running on the embedded test
+     server, and the host_resolver is set up to handle b.test. -->
+<iframe src="/cross-site/b.test/iframe_blob.html" id="frame1"></iframe>
+</body></html>
diff --git a/chrome/test/data/webui/cr_components/chromeos/network/network_config_test.js b/chrome/test/data/webui/cr_components/chromeos/network/network_config_test.js
index 50eef78..41dc75f 100644
--- a/chrome/test/data/webui/cr_components/chromeos/network/network_config_test.js
+++ b/chrome/test/data/webui/cr_components/chromeos/network/network_config_test.js
@@ -162,6 +162,15 @@
       PolymerTest.clearBody();
     });
 
+    // Sets all mandatory fields for an L2TP/IPsec service except for server CA
+    // and user certificate.
+    function setMandatoryFields() {
+      const configProperties = networkConfig.get('configProperties_');
+      configProperties.name = 'test-vpn';
+      configProperties.typeConfig.vpn.host = 'test-vpn-host';
+      configProperties.typeConfig.vpn.l2tp.username = 'test-username';
+    }
+
     test('Switch VPN Type', function() {
       initNetworkConfig();
 
@@ -208,8 +217,61 @@
         return flushAsync().then(() => {
           // Check that with no certificates, 'do-not-check' and 'no-certs' are
           // selected.
-          assertEquals('do-not-check', networkConfig.selectedServerCaHash_);
+          assertEquals('no-certs', networkConfig.selectedServerCaHash_);
           assertEquals('no-certs', networkConfig.selectedUserCertHash_);
+
+          // Set all other mandatory fields. vpnIsConfigured_() should be false
+          // due to empty server CA and user cert.
+          setMandatoryFields();
+          assertFalse(networkConfig.vpnIsConfigured_());
+        });
+      });
+    });
+
+    test('No Server CA Certs', function() {
+      mojoApi_.setCertificatesForTest([], [{
+                                        hash: kUserHash1,
+                                        availableForNetworkAuth: true,
+                                        hardwareBacked: true,
+                                        deviceWide: false
+                                      }]);
+      initNetworkConfig();
+      networkConfig.set('vpnType_', 'L2TP_IPsec');
+      networkConfig.set('ipsecAuthType_', 'Cert');
+      return mojoApi_.whenCalled('getNetworkCertificates').then(() => {
+        return flushAsync().then(() => {
+          assertEquals('no-certs', networkConfig.selectedServerCaHash_);
+          assertEquals(kUserHash1, networkConfig.selectedUserCertHash_);
+
+          // Set all other mandatory fields. vpnIsConfigured_() should be false
+          // due to empty server CA.
+          setMandatoryFields();
+          assertFalse(networkConfig.vpnIsConfigured_());
+        });
+      });
+    });
+
+    test('No Client Certs', function() {
+      mojoApi_.setCertificatesForTest(
+          [{
+            hash: kCaHash,
+            availableForNetworkAuth: true,
+            hardwareBacked: true,
+            deviceWide: true
+          }],
+          []);
+      initNetworkConfig();
+      networkConfig.set('vpnType_', 'L2TP_IPsec');
+      networkConfig.set('ipsecAuthType_', 'Cert');
+      return mojoApi_.whenCalled('getNetworkCertificates').then(() => {
+        return flushAsync().then(() => {
+          assertEquals(kCaHash, networkConfig.selectedServerCaHash_);
+          assertEquals('no-certs', networkConfig.selectedUserCertHash_);
+
+          // Set all other mandatory fields. vpnIsConfigured_() should be false
+          // due to empty client cert.
+          setMandatoryFields();
+          assertFalse(networkConfig.vpnIsConfigured_());
         });
       });
     });
@@ -236,6 +298,10 @@
           // The first Server CA and User certificate should be selected.
           assertEquals(kCaHash, networkConfig.selectedServerCaHash_);
           assertEquals(kUserHash1, networkConfig.selectedUserCertHash_);
+
+          // Set all other mandatory fields. vpnIsConfigured_() should be true.
+          setMandatoryFields();
+          assertTrue(networkConfig.vpnIsConfigured_());
         });
       });
     });
diff --git a/chrome/test/data/webui/settings/chromeos/BUILD.gn b/chrome/test/data/webui/settings/chromeos/BUILD.gn
index 46317f5..d52f574 100644
--- a/chrome/test/data/webui/settings/chromeos/BUILD.gn
+++ b/chrome/test/data/webui/settings/chromeos/BUILD.gn
@@ -18,14 +18,14 @@
 
 js_library("fake_user_action_recorder") {
   deps = [
-    "//chrome/browser/ui/webui/settings/ash/search:mojo_bindings_js_library_for_compile",
+    "//chrome/browser/ui/webui/settings/chromeos/search:mojo_bindings_js_library_for_compile",
     "//ui/webui/resources/js:cr",
   ]
 }
 
 js_library("fake_settings_search_handler") {
   deps = [
-    "//chrome/browser/ui/webui/settings/ash/search:mojo_bindings_js_library_for_compile",
+    "//chrome/browser/ui/webui/settings/chromeos/search:mojo_bindings_js_library_for_compile",
     "//ui/webui/resources/js:cr",
   ]
 }
diff --git a/chrome/test/data/webui/settings/chromeos/internet_subpage_tests.js b/chrome/test/data/webui/settings/chromeos/internet_subpage_tests.js
index dbae0f0..455dc00 100644
--- a/chrome/test/data/webui/settings/chromeos/internet_subpage_tests.js
+++ b/chrome/test/data/webui/settings/chromeos/internet_subpage_tests.js
@@ -594,7 +594,7 @@
                   internetSubpage.$$('#alwaysOnVpnSelector');
               assert(networkAlwaysOnVpn);
               // The list should contain 2 compatible networks.
-              assertEquals(5, networkAlwaysOnVpn.networks.length);
+              assertEquals(2, networkAlwaysOnVpn.networks.length);
             });
       });
     });
diff --git a/chromeos/dbus/dlcservice/dlcservice_client.cc b/chromeos/dbus/dlcservice/dlcservice_client.cc
index 52699c9..d3756a5 100644
--- a/chromeos/dbus/dlcservice/dlcservice_client.cc
+++ b/chromeos/dbus/dlcservice/dlcservice_client.cc
@@ -110,31 +110,30 @@
 
   ~DlcserviceClientImpl() override = default;
 
-  void Install(const dlcservice::InstallRequest& install_request,
+  void Install(const std::string& dlc_id,
                InstallCallback install_callback,
                ProgressCallback progress_callback) override {
     CheckServiceAvailable("Install");
-    const std::string& id = install_request.id();
     // If another installation for the same DLC ID was already called, go ahead
     // and hold the installation fields.
-    if (installation_holder_.find(id) != installation_holder_.end()) {
-      HoldInstallation(install_request, std::move(install_callback),
+    if (installation_holder_.find(dlc_id) != installation_holder_.end()) {
+      HoldInstallation(dlc_id, std::move(install_callback),
                        std::move(progress_callback));
       return;
     }
     if (installing_) {
-      EnqueueTask(base::BindOnce(
-          &DlcserviceClientImpl::Install, weak_ptr_factory_.GetWeakPtr(),
-          std::move(install_request), std::move(install_callback),
-          std::move(progress_callback)));
+      EnqueueTask(base::BindOnce(&DlcserviceClientImpl::Install,
+                                 weak_ptr_factory_.GetWeakPtr(),
+                                 std::move(dlc_id), std::move(install_callback),
+                                 std::move(progress_callback)));
       return;
     }
 
     TaskStarted();
     dbus::MethodCall method_call(dlcservice::kDlcServiceInterface,
-                                 dlcservice::kInstallMethod);
+                                 dlcservice::kInstallDlcMethod);
     dbus::MessageWriter writer(&method_call);
-    writer.AppendProtoAsArrayOfBytes(install_request);
+    writer.AppendString(dlc_id);
 
     VLOG(1) << "Requesting to install DLC(s).";
     // TODO(b/166782419): dlcservice hashes preloadable DLC images which can
@@ -143,10 +142,9 @@
     constexpr int timeout_ms = 5 * 60 * 1000;
     dlcservice_proxy_->CallMethodWithErrorResponse(
         &method_call, timeout_ms,
-        base::BindOnce(&DlcserviceClientImpl::OnInstall,
-                       weak_ptr_factory_.GetWeakPtr(), install_request,
-                       std::move(install_callback),
-                       std::move(progress_callback)));
+        base::BindOnce(
+            &DlcserviceClientImpl::OnInstall, weak_ptr_factory_.GetWeakPtr(),
+            dlc_id, std::move(install_callback), std::move(progress_callback)));
   }
 
   void Uninstall(const std::string& dlc_id,
@@ -237,12 +235,12 @@
   // Fields related to an installation allowing for multiple installations to be
   // in flight concurrently and handled by this dlcservice client. The callbacks
   // are used to report progress and the final installation.
-  struct InstallationHolder {
+  struct InstallationCallbacks {
     InstallCallback install_callback;
     ProgressCallback progress_callback;
 
-    InstallationHolder(InstallCallback install_callback,
-                       ProgressCallback progress_callback)
+    InstallationCallbacks(InstallCallback install_callback,
+                          ProgressCallback progress_callback)
         : install_callback(std::move(install_callback)),
           progress_callback(std::move(progress_callback)) {}
   };
@@ -262,11 +260,11 @@
   // Clears any state an installation had setup while being performed.
   void TaskEnded() { installing_ = false; }
 
-  void HoldInstallation(const dlcservice::InstallRequest& install_request,
+  void HoldInstallation(const std::string& id,
                         InstallCallback install_callback,
                         ProgressCallback progress_callback) {
-    installation_holder_[install_request.id()].emplace_back(
-        std::move(install_callback), std::move(progress_callback));
+    installation_holder_[id].emplace_back(std::move(install_callback),
+                                          std::move(progress_callback));
   }
 
   void ReleaseInstallation(const std::string& id) {
@@ -359,29 +357,27 @@
     LOG_IF(ERROR, !success) << "Failed to connect to DlcStateChanged signal.";
   }
 
-  void OnInstall(const dlcservice::InstallRequest& install_request,
+  void OnInstall(const std::string& dlc_id,
                  InstallCallback install_callback,
                  ProgressCallback progress_callback,
                  dbus::Response* response,
                  dbus::ErrorResponse* err_response) {
-    const std::string& id = install_request.id();
     if (response) {
-      HoldInstallation(install_request, std::move(install_callback),
+      HoldInstallation(dlc_id, std::move(install_callback),
                        std::move(progress_callback));
       return;
     }
 
     const auto err = DlcserviceErrorResponseHandler(err_response).get_err();
     if (err == dlcservice::kErrorBusy) {
-      EnqueueTask(base::BindOnce(&DlcserviceClientImpl::Install,
-                                 weak_ptr_factory_.GetWeakPtr(),
-                                 install_request, std::move(install_callback),
-                                 std::move(progress_callback)));
+      EnqueueTask(base::BindOnce(
+          &DlcserviceClientImpl::Install, weak_ptr_factory_.GetWeakPtr(),
+          dlc_id, std::move(install_callback), std::move(progress_callback)));
     } else {
-      HoldInstallation(install_request, std::move(install_callback),
+      HoldInstallation(dlc_id, std::move(install_callback),
                        std::move(progress_callback));
       dlcservice::DlcState dlc_state;
-      dlc_state.set_id(id);
+      dlc_state.set_id(dlc_id);
       dlc_state.set_last_error_code(err);
       SendCompleted(dlc_state);
     }
@@ -440,8 +436,9 @@
                    << " called when dlcservice is not available.";
   }
 
-  // DLC ID to `InstallationHolder` mapping.
-  std::map<std::string, std::vector<InstallationHolder>> installation_holder_;
+  // DLC ID to |InstallationCallbacks| mapping.
+  std::map<std::string, std::vector<InstallationCallbacks>>
+      installation_holder_;
 
   dbus::ObjectProxy* dlcservice_proxy_;
 
diff --git a/chromeos/dbus/dlcservice/dlcservice_client.h b/chromeos/dbus/dlcservice/dlcservice_client.h
index 4be5671..ed1562a 100644
--- a/chromeos/dbus/dlcservice/dlcservice_client.h
+++ b/chromeos/dbus/dlcservice/dlcservice_client.h
@@ -88,7 +88,7 @@
 
   // Installs the DLC passed in while reporting progress through the progress
   // callback and only calls install callback on install success/failure.
-  virtual void Install(const dlcservice::InstallRequest& install_request,
+  virtual void Install(const std::string& dlc_id,
                        InstallCallback callback,
                        ProgressCallback progress_callback) = 0;
 
diff --git a/chromeos/dbus/dlcservice/dlcservice_client_unittest.cc b/chromeos/dbus/dlcservice/dlcservice_client_unittest.cc
index c934428..883bf83 100644
--- a/chromeos/dbus/dlcservice/dlcservice_client_unittest.cc
+++ b/chromeos/dbus/dlcservice/dlcservice_client_unittest.cc
@@ -95,17 +95,6 @@
   }
 
  protected:
-  dlcservice::InstallRequest CreateInstallRequest(
-      const std::string& id = {},
-      const std::string& omaha_url = {},
-      bool reserve = false) {
-    dlcservice::InstallRequest install_request;
-    install_request.set_id(id);
-    install_request.set_omaha_url(omaha_url);
-    install_request.set_reserve(reserve);
-    return install_request;
-  }
-
   base::test::SingleThreadTaskEnvironment task_environment_;
   DlcserviceClient* client_;
   scoped_refptr<dbus::MockBus> mock_bus_;
@@ -343,8 +332,7 @@
       base::BindOnce([](const DlcserviceClient::InstallResult& install_result) {
         EXPECT_EQ(dlcservice::kErrorNone, install_result.error);
       });
-  client_->Install(CreateInstallRequest("foo-dlc"), std::move(install_callback),
-                   base::DoNothing());
+  client_->Install("foo-dlc", std::move(install_callback), base::DoNothing());
   base::RunLoop().RunUntilIdle();
 }
 
@@ -363,8 +351,7 @@
       base::BindOnce([](const DlcserviceClient::InstallResult& install_result) {
         EXPECT_EQ(dlcservice::kErrorInternal, install_result.error);
       });
-  client_->Install(CreateInstallRequest("foo-dlc"), std::move(install_callback),
-                   base::DoNothing());
+  client_->Install("foo-dlc", std::move(install_callback), base::DoNothing());
   base::RunLoop().RunUntilIdle();
 }
 
@@ -379,7 +366,7 @@
       [](decltype(counter)* counter, double) { ++*counter; }, &counter);
 
   responses_.push_back(dbus::Response::CreateEmpty());
-  client_->Install(CreateInstallRequest(), std::move(install_callback),
+  client_->Install({}, std::move(install_callback),
                    std::move(progress_callback));
   base::RunLoop().RunUntilIdle();
   EXPECT_EQ(0u, counter.load());
@@ -402,7 +389,7 @@
   DlcserviceClient::ProgressCallback progress_callback = base::BindRepeating(
       [](decltype(counter)* counter, double) { ++*counter; }, &counter);
 
-  client_->Install(CreateInstallRequest("foo"), std::move(install_callback),
+  client_->Install({"foo"}, std::move(install_callback),
                    std::move(progress_callback));
   base::RunLoop().RunUntilIdle();
   EXPECT_EQ(0u, counter.load());
@@ -434,8 +421,7 @@
       base::BindOnce([](const DlcserviceClient::InstallResult& install_result) {
         EXPECT_EQ(dlcservice::kErrorNone, install_result.error);
       });
-  client_->Install(CreateInstallRequest("foo-dlc"), std::move(install_callback),
-                   base::DoNothing());
+  client_->Install("foo-dlc", std::move(install_callback), base::DoNothing());
   base::RunLoop().RunUntilIdle();
 }
 
@@ -456,8 +442,7 @@
         },
         &counter);
     responses_.push_back(dbus::Response::CreateEmpty());
-    client_->Install(CreateInstallRequest(), std::move(install_callback),
-                     base::DoNothing());
+    client_->Install({}, std::move(install_callback), base::DoNothing());
   }
   base::RunLoop().RunUntilIdle();
   EXPECT_EQ(0u, counter.load());
diff --git a/chromeos/dbus/dlcservice/fake_dlcservice_client.cc b/chromeos/dbus/dlcservice/fake_dlcservice_client.cc
index f372ddd..a7f888e 100644
--- a/chromeos/dbus/dlcservice/fake_dlcservice_client.cc
+++ b/chromeos/dbus/dlcservice/fake_dlcservice_client.cc
@@ -14,18 +14,16 @@
 
 FakeDlcserviceClient::~FakeDlcserviceClient() = default;
 
-void FakeDlcserviceClient::Install(
-    const dlcservice::InstallRequest& install_request,
-    InstallCallback callback,
-    ProgressCallback progress_callback) {
+void FakeDlcserviceClient::Install(const std::string& dlc_id,
+                                   InstallCallback callback,
+                                   ProgressCallback progress_callback) {
   VLOG(1) << "Requesting to install DLC(s).";
-  const std::string& id = install_request.id();
   InstallResult install_result{
       .error = install_err_,
-      .dlc_id = id,
+      .dlc_id = dlc_id,
       .root_path = install_root_path_,
   };
-  dlcs_with_content_.add_dlc_infos()->set_id(id);
+  dlcs_with_content_.add_dlc_infos()->set_id(dlc_id);
   base::ThreadTaskRunnerHandle::Get()->PostTask(
       FROM_HERE,
       base::BindOnce(std::move(callback), std::move(install_result)));
diff --git a/chromeos/dbus/dlcservice/fake_dlcservice_client.h b/chromeos/dbus/dlcservice/fake_dlcservice_client.h
index c8deab4..336418c 100644
--- a/chromeos/dbus/dlcservice/fake_dlcservice_client.h
+++ b/chromeos/dbus/dlcservice/fake_dlcservice_client.h
@@ -22,7 +22,7 @@
   ~FakeDlcserviceClient() override;
 
   // DlcserviceClient:
-  void Install(const dlcservice::InstallRequest& install_request,
+  void Install(const std::string& dlc_id,
                InstallCallback callback,
                ProgressCallback progress_callback) override;
   // Uninstalling disables the DLC.
diff --git a/chromeos/dbus/session_manager/fake_session_manager_client.cc b/chromeos/dbus/session_manager/fake_session_manager_client.cc
index 18e60bc..f65a6ab2 100644
--- a/chromeos/dbus/session_manager/fake_session_manager_client.cc
+++ b/chromeos/dbus/session_manager/fake_session_manager_client.cc
@@ -408,6 +408,8 @@
 
 bool FakeSessionManagerClient::RequestBrowserDataMigration(
     const cryptohome::AccountIdentifier& cryptohome_id) {
+  request_browser_data_migration_called_ = true;
+
   return true;
 }
 
diff --git a/chromeos/dbus/session_manager/fake_session_manager_client.h b/chromeos/dbus/session_manager/fake_session_manager_client.h
index 539e57a..6722ec0f 100644
--- a/chromeos/dbus/session_manager/fake_session_manager_client.h
+++ b/chromeos/dbus/session_manager/fake_session_manager_client.h
@@ -317,6 +317,10 @@
     return primary_user_id_;
   }
 
+  bool request_browser_data_migration_called() const {
+    return request_browser_data_migration_called_;
+  }
+
  private:
   // Called in response to writing owner key file specified in new device
   // policy - used for in-memory fake only.
@@ -385,6 +389,8 @@
 
   std::string login_password_;
 
+  bool request_browser_data_migration_called_ = false;
+
   // Contains last request passed to StartArcMiniContainer
   login_manager::StartArcMiniContainerRequest
       last_start_arc_mini_container_request_;
diff --git a/chromeos/lacros/BUILD.gn b/chromeos/lacros/BUILD.gn
index 9c44ae1..b6f847c 100644
--- a/chromeos/lacros/BUILD.gn
+++ b/chromeos/lacros/BUILD.gn
@@ -28,8 +28,6 @@
     "//ui/native_theme",
   ]
   sources = [
-    "crosapi_pref_observer.cc",
-    "crosapi_pref_observer.h",
     "lacros_service.cc",
     "lacros_service.h",
     "lacros_service_never_blocking_state.cc",
diff --git a/chromeos/language/language_packs/language_pack_manager.cc b/chromeos/language/language_packs/language_pack_manager.cc
index 8dbd320e..f8bbdb11 100644
--- a/chromeos/language/language_packs/language_pack_manager.cc
+++ b/chromeos/language/language_packs/language_pack_manager.cc
@@ -209,11 +209,8 @@
     return;
   }
 
-  dlcservice::InstallRequest install_request;
-  install_request.set_id(dlc_id);
   DlcserviceClient::Get()->Install(
-      install_request,
-      base::BindOnce(&OnInstallDlcComplete, std::move(callback)),
+      dlc_id, base::BindOnce(&OnInstallDlcComplete, std::move(callback)),
       base::DoNothing());
 }
 
diff --git a/chromeos/profiles/atom.afdo.newest.txt b/chromeos/profiles/atom.afdo.newest.txt
index fc33d0c..e37ba70 100644
--- a/chromeos/profiles/atom.afdo.newest.txt
+++ b/chromeos/profiles/atom.afdo.newest.txt
@@ -1 +1 @@
-chromeos-chrome-amd64-atom-100-4863.0-1644842555-benchmark-100.0.4892.0-r2-redacted.afdo.xz
+chromeos-chrome-amd64-atom-100-4880.0-1645442860-benchmark-100.0.4896.12-r1-redacted.afdo.xz
diff --git a/chromeos/profiles/bigcore.afdo.newest.txt b/chromeos/profiles/bigcore.afdo.newest.txt
index f9bd8fc..eae4db7 100644
--- a/chromeos/profiles/bigcore.afdo.newest.txt
+++ b/chromeos/profiles/bigcore.afdo.newest.txt
@@ -1 +1 @@
-chromeos-chrome-amd64-bigcore-100-4863.0-1644841718-benchmark-100.0.4892.0-r2-redacted.afdo.xz
+chromeos-chrome-amd64-bigcore-100-4880.0-1645439876-benchmark-100.0.4896.12-r1-redacted.afdo.xz
diff --git a/chromeos/services/machine_learning/cpp/ash/handwriting_model_loader.cc b/chromeos/services/machine_learning/cpp/ash/handwriting_model_loader.cc
index b59d2094..7d0e581 100644
--- a/chromeos/services/machine_learning/cpp/ash/handwriting_model_loader.cc
+++ b/chromeos/services/machine_learning/cpp/ash/handwriting_model_loader.cc
@@ -93,10 +93,8 @@
   // the handwriting dlc if it is already on device.
   for (const auto& dlc_info : dlcs_with_content.dlc_infos()) {
     if (dlc_info.id() == kLibHandwritingDlcId) {
-      dlcservice::InstallRequest install_request;
-      install_request.set_id(kLibHandwritingDlcId);
       dlc_client->Install(
-          install_request,
+          kLibHandwritingDlcId,
           base::BindOnce(&OnInstallDlcComplete, std::move(spec),
                          std::move(receiver), std::move(callback)),
           base::DoNothing());
diff --git a/components/BUILD.gn b/components/BUILD.gn
index 02080e8..9e745b1 100644
--- a/components/BUILD.gn
+++ b/components/BUILD.gn
@@ -162,6 +162,7 @@
     "//components/omnibox/browser:unit_tests",
     "//components/open_from_clipboard:unit_tests",
     "//components/openscreen_platform:unittests",
+    "//components/optimization_guide/core:unit_tests",
     "//components/os_crypt:unit_tests",
     "//components/password_manager/core/browser:unit_tests",
     "//components/payments/core:unit_tests",
@@ -327,7 +328,6 @@
       "//components/offline_pages:unit_tests",
       "//components/optimization_guide/content/browser:unit_tests",
       "//components/optimization_guide/content/renderer:unit_tests",
-      "//components/optimization_guide/core:unit_tests",
       "//components/page_image_annotation/content/renderer:unit_tests",
       "//components/page_image_annotation/core:unit_tests",
       "//components/page_info/core:unit_tests",
diff --git a/components/autofill_assistant/android/BUILD.gn b/components/autofill_assistant/android/BUILD.gn
index 2812f09..19394d5 100644
--- a/components/autofill_assistant/android/BUILD.gn
+++ b/components/autofill_assistant/android/BUILD.gn
@@ -10,9 +10,86 @@
 import("//chrome/common/features.gni")
 import("//tools/grit/grit_rule.gni")
 
+android_library("java") {
+  resources_package = "org.chromium.components.autofill_assistant"
+
+  deps = [
+    ":autofill_assistant_public_java",
+    ":public_java",
+    "//base:base_java",
+    "//components/autofill/android:autofill_java",
+    "//components/autofill_assistant/android:autofill_assistant_public_java",
+    "//components/autofill_assistant/android:java_resources",
+    "//components/browser_ui/bottomsheet/android:java",
+    "//components/browser_ui/modaldialog/android:java",
+    "//components/browser_ui/settings/android:java",
+    "//components/browser_ui/styles/android:java",
+    "//components/browser_ui/widget/android:java",
+    "//components/embedder_support/android:util_java",
+    "//components/favicon/android:java",
+    "//components/image_fetcher:java",
+    "//components/payments/content/android:java",
+    "//components/policy/android:policy_java",
+    "//components/signin/public/android:java",
+    "//components/url_formatter/android:url_formatter_java",
+    "//components/version_info/android:version_constants_java",
+    "//content/public/android:content_java",
+    "//mojo/public/java:bindings_java",
+    "//third_party/android_deps:android_support_v7_appcompat_java",
+    "//third_party/android_deps:com_android_support_support_annotations_java",
+    "//third_party/android_deps:material_design_java",
+    "//third_party/androidx:androidx_annotation_annotation_java",
+    "//third_party/androidx:androidx_appcompat_appcompat_resources_java",
+    "//third_party/androidx:androidx_collection_collection_java",
+    "//third_party/androidx:androidx_coordinatorlayout_coordinatorlayout_java",
+    "//third_party/androidx:androidx_core_core_java",
+    "//third_party/androidx:androidx_gridlayout_gridlayout_java",
+    "//third_party/androidx:androidx_lifecycle_lifecycle_common_java",
+    "//third_party/androidx:androidx_lifecycle_lifecycle_runtime_java",
+    "//third_party/androidx:androidx_recyclerview_recyclerview_java",
+    "//third_party/blink/public/mojom:android_mojo_bindings_java",
+    "//ui/android:ui_java",
+    "//url:gurl_java",
+  ]
+
+  sources = [
+    "java/src/org/chromium/components/autofill_assistant/AssistantAccessibilityUtils.java",
+    "java/src/org/chromium/components/autofill_assistant/AssistantBottomBarDelegate.java",
+    "java/src/org/chromium/components/autofill_assistant/AssistantBottomSheetContent.java",
+    "java/src/org/chromium/components/autofill_assistant/AssistantTagsForTesting.java",
+    "java/src/org/chromium/components/autofill_assistant/AssistantTextUtils.java",
+    "java/src/org/chromium/components/autofill_assistant/BottomSheetUtils.java",
+    "java/src/org/chromium/components/autofill_assistant/FeedbackContext.java",
+    "java/src/org/chromium/components/autofill_assistant/LayoutUtils.java",
+    "java/src/org/chromium/components/autofill_assistant/ScrollToHideGestureListener.java",
+    "java/src/org/chromium/components/autofill_assistant/SizeListenableLinearLayout.java",
+  ]
+
+  annotation_processor_deps = [ "//base/android/jni_generator:jni_processor" ]
+}
+
 android_library("public_java") {
   resources_package = "org.chromium.components.autofill_assistant"
 
+  deps = [
+    "//base:base_java",
+    "//base:jni_java",
+    "//components/autofill/android:autofill_payments_java_resources",
+    "//components/autofill/android:main_autofill_java",
+    "//components/autofill_assistant/android:autofill_assistant_public_java",
+    "//components/browser_ui/bottomsheet/android:java",
+    "//components/favicon/android:java",
+    "//components/image_fetcher:java",
+    "//components/module_installer/android:module_installer_java",
+    "//components/module_installer/android:module_interface_java",
+    "//components/payments/content/android:java_resources",
+    "//components/signin/public/android:java",
+    "//content/public/android:content_java",
+    "//third_party/androidx:androidx_annotation_annotation_java",
+    "//ui/android:ui_java",
+    "//url:gurl_java",
+  ]
+
   sources = [
     "public/java/src/org/chromium/components/autofill_assistant/AssistantAccessTokenUtil.java",
     "public/java/src/org/chromium/components/autofill_assistant/AssistantAutofillCreditCard.java",
@@ -32,6 +109,7 @@
     "public/java/src/org/chromium/components/autofill_assistant/AssistantOnboardingHelper.java",
     "public/java/src/org/chromium/components/autofill_assistant/AssistantOptionModel.java",
     "public/java/src/org/chromium/components/autofill_assistant/AssistantPaymentInstrument.java",
+    "public/java/src/org/chromium/components/autofill_assistant/AssistantPaymentInstrumentEditorGms.java",
     "public/java/src/org/chromium/components/autofill_assistant/AssistantProfileImageUtil.java",
     "public/java/src/org/chromium/components/autofill_assistant/AssistantSettingsUtil.java",
     "public/java/src/org/chromium/components/autofill_assistant/AssistantSnackbar.java",
@@ -51,25 +129,6 @@
     "public/java/src/org/chromium/components/autofill_assistant/TriggerContext.java",
   ]
 
-  deps = [
-    "//base:base_java",
-    "//base:jni_java",
-    "//components/autofill/android:autofill_payments_java_resources",
-    "//components/autofill/android:main_autofill_java",
-    "//components/autofill_assistant/android:autofill_assistant_public_java",
-    "//components/browser_ui/bottomsheet/android:java",
-    "//components/favicon/android:java",
-    "//components/image_fetcher:java",
-    "//components/module_installer/android:module_installer_java",
-    "//components/module_installer/android:module_interface_java",
-    "//components/payments/content/android:java_resources",
-    "//components/signin/public/android:java",
-    "//content/public/android:content_java",
-    "//third_party/androidx:androidx_annotation_annotation_java",
-    "//ui/android:ui_java",
-    "//url:gurl_java",
-  ]
-
   annotation_processor_deps = [
     "//base/android/jni_generator:jni_processor",
     "//components/module_installer/android:module_interface_processor",
diff --git a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/AssistantAccessibilityUtils.java b/components/autofill_assistant/android/java/src/org/chromium/components/autofill_assistant/AssistantAccessibilityUtils.java
similarity index 94%
rename from chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/AssistantAccessibilityUtils.java
rename to components/autofill_assistant/android/java/src/org/chromium/components/autofill_assistant/AssistantAccessibilityUtils.java
index 88836e1..53c1acc 100644
--- a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/AssistantAccessibilityUtils.java
+++ b/components/autofill_assistant/android/java/src/org/chromium/components/autofill_assistant/AssistantAccessibilityUtils.java
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-package org.chromium.chrome.browser.autofill_assistant;
+package org.chromium.components.autofill_assistant;
 
 import static android.view.View.IMPORTANT_FOR_ACCESSIBILITY_AUTO;
 import static android.view.View.IMPORTANT_FOR_ACCESSIBILITY_NO;
diff --git a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/AssistantBottomBarDelegate.java b/components/autofill_assistant/android/java/src/org/chromium/components/autofill_assistant/AssistantBottomBarDelegate.java
similarity index 93%
rename from chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/AssistantBottomBarDelegate.java
rename to components/autofill_assistant/android/java/src/org/chromium/components/autofill_assistant/AssistantBottomBarDelegate.java
index 1486979..c18d71e 100644
--- a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/AssistantBottomBarDelegate.java
+++ b/components/autofill_assistant/android/java/src/org/chromium/components/autofill_assistant/AssistantBottomBarDelegate.java
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-package org.chromium.chrome.browser.autofill_assistant;
+package org.chromium.components.autofill_assistant;
 
 /** Common interface for the bottom bar delegate. */
 public interface AssistantBottomBarDelegate {
diff --git a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/AssistantBottomSheetContent.java b/components/autofill_assistant/android/java/src/org/chromium/components/autofill_assistant/AssistantBottomSheetContent.java
similarity index 97%
rename from chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/AssistantBottomSheetContent.java
rename to components/autofill_assistant/android/java/src/org/chromium/components/autofill_assistant/AssistantBottomSheetContent.java
index c2c53edd..d7c4ee7 100644
--- a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/AssistantBottomSheetContent.java
+++ b/components/autofill_assistant/android/java/src/org/chromium/components/autofill_assistant/AssistantBottomSheetContent.java
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-package org.chromium.chrome.browser.autofill_assistant;
+package org.chromium.components.autofill_assistant;
 
 import android.content.Context;
 import android.view.View;
@@ -13,7 +13,6 @@
 
 import org.chromium.base.Callback;
 import org.chromium.base.supplier.Supplier;
-import org.chromium.chrome.autofill_assistant.R;
 import org.chromium.components.browser_ui.bottomsheet.BottomSheetContent;
 import org.chromium.components.browser_ui.bottomsheet.BottomSheetController;
 
diff --git a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/AssistantTagsForTesting.java b/components/autofill_assistant/android/java/src/org/chromium/components/autofill_assistant/AssistantTagsForTesting.java
similarity index 96%
rename from chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/AssistantTagsForTesting.java
rename to components/autofill_assistant/android/java/src/org/chromium/components/autofill_assistant/AssistantTagsForTesting.java
index 54b5ab34..9d5ad3c 100644
--- a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/AssistantTagsForTesting.java
+++ b/components/autofill_assistant/android/java/src/org/chromium/components/autofill_assistant/AssistantTagsForTesting.java
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-package org.chromium.chrome.browser.autofill_assistant;
+package org.chromium.components.autofill_assistant;
 
 /**
  * Contains tags used for java UI tests.
diff --git a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/AssistantTextUtils.java b/components/autofill_assistant/android/java/src/org/chromium/components/autofill_assistant/AssistantTextUtils.java
similarity index 97%
rename from chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/AssistantTextUtils.java
rename to components/autofill_assistant/android/java/src/org/chromium/components/autofill_assistant/AssistantTextUtils.java
index 4be24fe..92793a6 100644
--- a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/AssistantTextUtils.java
+++ b/components/autofill_assistant/android/java/src/org/chromium/components/autofill_assistant/AssistantTextUtils.java
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-package org.chromium.chrome.browser.autofill_assistant;
+package org.chromium.components.autofill_assistant;
 
 import android.graphics.Typeface;
 import android.text.method.LinkMovementMethod;
diff --git a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/BottomSheetUtils.java b/components/autofill_assistant/android/java/src/org/chromium/components/autofill_assistant/BottomSheetUtils.java
similarity index 96%
rename from chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/BottomSheetUtils.java
rename to components/autofill_assistant/android/java/src/org/chromium/components/autofill_assistant/BottomSheetUtils.java
index ea0e6eb..483160d 100644
--- a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/BottomSheetUtils.java
+++ b/components/autofill_assistant/android/java/src/org/chromium/components/autofill_assistant/BottomSheetUtils.java
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-package org.chromium.chrome.browser.autofill_assistant;
+package org.chromium.components.autofill_assistant;
 
 import org.chromium.base.task.PostTask;
 import org.chromium.components.browser_ui.bottomsheet.BottomSheetContent;
@@ -42,7 +42,7 @@
         }
     }
 
-    static void restoreState(BottomSheetController controller, BottomSheetContent content,
+    public static void restoreState(BottomSheetController controller, BottomSheetContent content,
             @SheetState int targetState) {
         if (controller.getSheetState() == targetState) {
             return;
diff --git a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/FeedbackContext.java b/components/autofill_assistant/android/java/src/org/chromium/components/autofill_assistant/FeedbackContext.java
similarity index 90%
rename from chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/FeedbackContext.java
rename to components/autofill_assistant/android/java/src/org/chromium/components/autofill_assistant/FeedbackContext.java
index f24661f..1fb15ae 100644
--- a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/FeedbackContext.java
+++ b/components/autofill_assistant/android/java/src/org/chromium/components/autofill_assistant/FeedbackContext.java
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-package org.chromium.chrome.browser.autofill_assistant;
+package org.chromium.components.autofill_assistant;
 
 import android.app.Activity;
 
@@ -13,7 +13,8 @@
  * Automatically extracts context information and serializes it in JSON form.
  */
 class FeedbackContext extends JSONObject {
-    static String buildContextString(Activity activity, String debugContext, int indentSpaces) {
+    public static String buildContextString(
+            Activity activity, String debugContext, int indentSpaces) {
         try {
             return new FeedbackContext(activity, debugContext).toString(indentSpaces);
         } catch (JSONException e) {
diff --git a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/LayoutUtils.java b/components/autofill_assistant/android/java/src/org/chromium/components/autofill_assistant/LayoutUtils.java
similarity index 92%
rename from chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/LayoutUtils.java
rename to components/autofill_assistant/android/java/src/org/chromium/components/autofill_assistant/LayoutUtils.java
index 1f62b97..6a08eb68 100644
--- a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/LayoutUtils.java
+++ b/components/autofill_assistant/android/java/src/org/chromium/components/autofill_assistant/LayoutUtils.java
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-package org.chromium.chrome.browser.autofill_assistant;
+package org.chromium.components.autofill_assistant;
 
 import android.content.Context;
 import android.view.LayoutInflater;
diff --git a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/ScrollToHideGestureListener.java b/components/autofill_assistant/android/java/src/org/chromium/components/autofill_assistant/ScrollToHideGestureListener.java
similarity index 98%
rename from chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/ScrollToHideGestureListener.java
rename to components/autofill_assistant/android/java/src/org/chromium/components/autofill_assistant/ScrollToHideGestureListener.java
index e288d9d..286f2e2 100644
--- a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/ScrollToHideGestureListener.java
+++ b/components/autofill_assistant/android/java/src/org/chromium/components/autofill_assistant/ScrollToHideGestureListener.java
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-package org.chromium.chrome.browser.autofill_assistant;
+package org.chromium.components.autofill_assistant;
 
 import android.animation.ValueAnimator;
 import android.view.animation.DecelerateInterpolator;
diff --git a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/SizeListenableLinearLayout.java b/components/autofill_assistant/android/java/src/org/chromium/components/autofill_assistant/SizeListenableLinearLayout.java
similarity index 92%
rename from chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/SizeListenableLinearLayout.java
rename to components/autofill_assistant/android/java/src/org/chromium/components/autofill_assistant/SizeListenableLinearLayout.java
index 00d541b..a86ed42 100644
--- a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/SizeListenableLinearLayout.java
+++ b/components/autofill_assistant/android/java/src/org/chromium/components/autofill_assistant/SizeListenableLinearLayout.java
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-package org.chromium.chrome.browser.autofill_assistant;
+package org.chromium.components.autofill_assistant;
 
 import android.content.Context;
 import android.util.AttributeSet;
@@ -50,7 +50,7 @@
         }
     }
 
-    void setContentSizeListener(@Nullable BottomSheetContent.ContentSizeListener listener) {
+    public void setContentSizeListener(@Nullable BottomSheetContent.ContentSizeListener listener) {
         mListener = listener;
     }
 }
diff --git a/components/autofill_assistant/android/public/java/src/org/chromium/components/autofill_assistant/AssistantContactEditorAccount.java b/components/autofill_assistant/android/public/java/src/org/chromium/components/autofill_assistant/AssistantContactEditorAccount.java
index 3e44661..2d9ee27 100644
--- a/components/autofill_assistant/android/public/java/src/org/chromium/components/autofill_assistant/AssistantContactEditorAccount.java
+++ b/components/autofill_assistant/android/public/java/src/org/chromium/components/autofill_assistant/AssistantContactEditorAccount.java
@@ -46,9 +46,9 @@
      * @param oldItem The item to be edited, can be null in which case a new item is created.
      * @param doneCallback Called after the editor is closed, assuming that the item has been
      *                     successfully edited. The callback will be called with the
-     *                     {@code oldItem} and can be null. The list of new items needs to be
+     *                     {@code oldItem} which can be null. The list of new items needs to be
      *                     requested.
-     * @param cancelCallback Called after the editor is closed, assuming that the edit has been
+     * @param cancelCallback Only called if the intent failed to be launched.
      */
     @Override
     public void createOrEditItem(@Nullable ContactModel oldItem,
diff --git a/components/autofill_assistant/android/public/java/src/org/chromium/components/autofill_assistant/AssistantEditorFactory.java b/components/autofill_assistant/android/public/java/src/org/chromium/components/autofill_assistant/AssistantEditorFactory.java
index f76b193..3ed62875 100644
--- a/components/autofill_assistant/android/public/java/src/org/chromium/components/autofill_assistant/AssistantEditorFactory.java
+++ b/components/autofill_assistant/android/public/java/src/org/chromium/components/autofill_assistant/AssistantEditorFactory.java
@@ -31,4 +31,7 @@
 
     AssistantPaymentInstrumentEditor createPaymentInstrumentEditor(WebContents webContents,
             Activity activity, List<String> supportedCardNetworks, boolean shouldStoreChanges);
+
+    AssistantPaymentInstrumentEditor createGmsPaymentInstrumentEditor(Activity activity,
+            WindowAndroid windowAndroid, String accountEmail, byte[] addInstrumentactionToken);
 }
diff --git a/components/autofill_assistant/android/public/java/src/org/chromium/components/autofill_assistant/AssistantPaymentInstrumentEditorGms.java b/components/autofill_assistant/android/public/java/src/org/chromium/components/autofill_assistant/AssistantPaymentInstrumentEditorGms.java
new file mode 100644
index 0000000..326f4f53
--- /dev/null
+++ b/components/autofill_assistant/android/public/java/src/org/chromium/components/autofill_assistant/AssistantPaymentInstrumentEditorGms.java
@@ -0,0 +1,55 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.components.autofill_assistant;
+
+import android.app.Activity;
+
+import org.chromium.base.Callback;
+import org.chromium.chrome.browser.autofill_assistant.user_data.GmsIntegrator;
+import org.chromium.components.autofill_assistant.AssistantEditor.AssistantPaymentInstrumentEditor;
+import org.chromium.components.autofill_assistant.AssistantOptionModel.PaymentInstrumentModel;
+import org.chromium.ui.base.WindowAndroid;
+
+/**
+ * Editor for payment instruments in Chrome/WebLayer using a GMS intent.
+ */
+public class AssistantPaymentInstrumentEditorGms implements AssistantPaymentInstrumentEditor {
+    private final WindowAndroid mWindowAndroid;
+    private final GmsIntegrator mGmsIntegrator;
+    private final byte[] mAddInstrumentActionToken;
+
+    public AssistantPaymentInstrumentEditorGms(Activity activity, WindowAndroid windowAndroid,
+            String accountEmail, byte[] addInstrumentactionToken) {
+        mWindowAndroid = windowAndroid;
+        mGmsIntegrator = new GmsIntegrator(accountEmail, activity);
+        mAddInstrumentActionToken = addInstrumentactionToken;
+    }
+
+    /**
+     * Edit the user's payment instruments.
+     *
+     * @param oldItem The item to be edited, can be null in which case a new item is created.
+     * @param doneCallback Called after the editor is closed, assuming that the item has been
+     *                     successfully edited. The callback will be called with the
+     *                     {@code oldItem} which can be null. The list of new items needs to be
+     *                     requested.
+     * @param cancelCallback Only called if the intent failed to be launched.
+     */
+    @Override
+    public void createOrEditItem(PaymentInstrumentModel oldItem,
+            Callback<PaymentInstrumentModel> doneCallback,
+            Callback<PaymentInstrumentModel> cancelCallback) {
+        Callback<Boolean> callback = success -> {
+            if (success) {
+                doneCallback.onResult(oldItem);
+            } else {
+                cancelCallback.onResult(oldItem);
+            }
+        };
+
+        mGmsIntegrator.launchAddInstrumentIntent(
+                mAddInstrumentActionToken, mWindowAndroid, callback);
+    }
+}
diff --git a/components/autofill_assistant/browser/actions/collect_user_data_action.cc b/components/autofill_assistant/browser/actions/collect_user_data_action.cc
index 9346fa8..a5813598 100644
--- a/components/autofill_assistant/browser/actions/collect_user_data_action.cc
+++ b/components/autofill_assistant/browser/actions/collect_user_data_action.cc
@@ -1475,6 +1475,9 @@
   }
 
   if (RequiresPaymentMethod(*collect_user_data_options_)) {
+    collect_user_data_options_->add_payment_instrument_action_token =
+        proto_data.add_payment_instrument_token();
+
     user_data->available_payment_instruments_.clear();
     for (const auto& payment_data :
          proto_data.available_payment_instruments()) {
diff --git a/components/autofill_assistant/browser/user_data.h b/components/autofill_assistant/browser/user_data.h
index df84f7f..101aa6c 100644
--- a/components/autofill_assistant/browser/user_data.h
+++ b/components/autofill_assistant/browser/user_data.h
@@ -311,6 +311,8 @@
   bool can_edit_contacts = true;
   bool use_gms_core_edit_dialogs = false;
 
+  absl::optional<std::string> add_payment_instrument_action_token;
+
   // If empty, terms and conditions should not be shown.
   std::string accept_terms_and_conditions_text;
   std::string terms_require_review_text;
diff --git a/components/back_forward_cache/back_forward_cache_disable.cc b/components/back_forward_cache/back_forward_cache_disable.cc
index 43b2a9de..7598bc29 100644
--- a/components/back_forward_cache/back_forward_cache_disable.cc
+++ b/components/back_forward_cache/back_forward_cache_disable.cc
@@ -45,10 +45,11 @@
 }
 
 content::BackForwardCache::DisabledReason DisabledReason(
-    DisabledReasonId reason_id) {
+    DisabledReasonId reason_id,
+    const std::string& context) {
   return content::BackForwardCache::DisabledReason(
       {content::BackForwardCache::DisabledSource::kEmbedder,
        static_cast<content::BackForwardCache::DisabledReasonType>(reason_id),
-       ReasonIdToString(reason_id)});
+       ReasonIdToString(reason_id), context});
 }
 }  // namespace back_forward_cache
diff --git a/components/back_forward_cache/back_forward_cache_disable.h b/components/back_forward_cache/back_forward_cache_disable.h
index 8e0f5dfc..d840a9f 100644
--- a/components/back_forward_cache/back_forward_cache_disable.h
+++ b/components/back_forward_cache/back_forward_cache_disable.h
@@ -12,7 +12,8 @@
 namespace back_forward_cache {
 // Constructs a chrome-specific DisabledReason
 content::BackForwardCache::DisabledReason DisabledReason(
-    DisabledReasonId reason_id);
+    DisabledReasonId reason_id,
+    const std::string& context = "");
 }  // namespace back_forward_cache
 
 #endif  // COMPONENTS_BACK_FORWARD_CACHE_BACK_FORWARD_CACHE_DISABLE_H_
diff --git a/components/blocked_content/safe_browsing_triggered_popup_blocker.cc b/components/blocked_content/safe_browsing_triggered_popup_blocker.cc
index 137ba1b..e7a6b90 100644
--- a/components/blocked_content/safe_browsing_triggered_popup_blocker.cc
+++ b/components/blocked_content/safe_browsing_triggered_popup_blocker.cc
@@ -18,6 +18,7 @@
 #include "components/user_prefs/user_prefs.h"
 #include "content/public/browser/back_forward_cache.h"
 #include "content/public/browser/browser_context.h"
+#include "content/public/browser/frame_type.h"
 #include "content/public/browser/navigation_handle.h"
 #include "content/public/browser/render_frame_host.h"
 #include "content/public/browser/web_contents.h"
@@ -115,8 +116,11 @@
 
 void SafeBrowsingTriggeredPopupBlocker::DidFinishNavigation(
     content::NavigationHandle* navigation_handle) {
-  if (!navigation_handle->IsInMainFrame())
+  if (!navigation_handle->IsInMainFrame() ||
+      navigation_handle->GetNavigatingFrameType() ==
+          content::FrameType::kFencedFrameRoot) {
     return;
+  }
 
   absl::optional<SubresourceFilterLevel> level;
   NavigationHandleData* data =
@@ -171,6 +175,11 @@
     const subresource_filter::SubresourceFilterSafeBrowsingClient::CheckResult&
         result) {
   DCHECK(navigation_handle->IsInMainFrame());
+  // TODO(crbug.com/1263541): Replace it with DCHECK.
+  if (navigation_handle->GetNavigatingFrameType() ==
+      content::FrameType::kFencedFrameRoot) {
+    return;
+  }
   absl::optional<safe_browsing::SubresourceFilterLevel> match_level;
   if (result.threat_type ==
       safe_browsing::SBThreatType::SB_THREAT_TYPE_SUBRESOURCE_FILTER) {
diff --git a/components/blocked_content/safe_browsing_triggered_popup_blocker_unittest.cc b/components/blocked_content/safe_browsing_triggered_popup_blocker_unittest.cc
index 1ad0bfbe..df7eb83 100644
--- a/components/blocked_content/safe_browsing_triggered_popup_blocker_unittest.cc
+++ b/components/blocked_content/safe_browsing_triggered_popup_blocker_unittest.cc
@@ -38,6 +38,7 @@
 #include "content/public/test/test_navigation_throttle_inserter.h"
 #include "content/public/test/test_renderer_host.h"
 #include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/public/common/features.h"
 #include "third_party/blink/public/mojom/frame/frame.mojom.h"
 #include "third_party/blink/public/mojom/window_features/window_features.mojom.h"
 #include "ui/base/page_transition_types.h"
@@ -548,4 +549,44 @@
   }
 }
 
+class SafeBrowsingTriggeredPopupBlockerFencedFrameTest
+    : public SafeBrowsingTriggeredPopupBlockerTest {
+ public:
+  SafeBrowsingTriggeredPopupBlockerFencedFrameTest() {
+    scoped_feature_list_.InitAndEnableFeatureWithParameters(
+        blink::features::kFencedFrames, {{"implementation_type", "mparch"}});
+  }
+  ~SafeBrowsingTriggeredPopupBlockerFencedFrameTest() override = default;
+
+ private:
+  base::test::ScopedFeatureList scoped_feature_list_;
+};
+
+// Ensures that the popup blocker is not triggered by a fenced frame.
+TEST_F(SafeBrowsingTriggeredPopupBlockerFencedFrameTest,
+       ShouldNotTriggerPopupBlocker) {
+  const GURL url("https://example.test/");
+  MarkUrlAsAbusiveEnforce(url);
+  NavigateAndCommit(url);
+
+  // The popup blocker is triggered for a primary page.
+  EXPECT_TRUE(
+      popup_blocker()->ShouldApplyAbusivePopupBlocker(main_rfh()->GetPage()));
+
+  content::RenderFrameHost* fenced_frame_root =
+      content::RenderFrameHostTester::For(main_rfh())->AppendFencedFrame();
+
+  // Navigate a fenced frame.
+  const GURL fenced_frame_url("https://fencedframe.test");
+  MarkUrlAsAbusiveEnforce(fenced_frame_url);
+  std::unique_ptr<content::NavigationSimulator> navigation_simulator =
+      content::NavigationSimulator::CreateForFencedFrame(fenced_frame_url,
+                                                         fenced_frame_root);
+  navigation_simulator->Commit();
+
+  // The popup blocker is not triggered for a fenced frame.
+  EXPECT_FALSE(popup_blocker()->ShouldApplyAbusivePopupBlocker(
+      fenced_frame_root->GetPage()));
+}
+
 }  // namespace blocked_content
diff --git a/components/commerce/core/commerce_feature_list.cc b/components/commerce/core/commerce_feature_list.cc
index 7e050393..1247da7 100644
--- a/components/commerce/core/commerce_feature_list.cc
+++ b/components/commerce/core/commerce_feature_list.cc
@@ -51,6 +51,7 @@
 
 const char kRetailCouponsWithCodeParam[] = "RetailCouponsWithCodeParam";
 
+// Params use for Discount Consent v2.
 const base::Feature kDiscountConsentV2{"DiscountConsentV2",
                                        base::FEATURE_DISABLED_BY_DEFAULT};
 
diff --git a/components/content_settings/browser/page_specific_content_settings.cc b/components/content_settings/browser/page_specific_content_settings.cc
index 85acbfe9..9b0d4d8 100644
--- a/components/content_settings/browser/page_specific_content_settings.cc
+++ b/components/content_settings/browser/page_specific_content_settings.cc
@@ -47,34 +47,6 @@
 namespace content_settings {
 namespace {
 
-bool ShouldSendUpdatedContentSettingsRulesToRenderer(
-    ContentSettingsType content_type) {
-  // ContentSettingsType::DEFAULT signals that multiple content settings may
-  // have been updated, e.g. by the PolicyProvider. This should always be sent
-  // to the renderer in case a relevant setting is updated.
-  if (content_type == ContentSettingsType::DEFAULT)
-    return true;
-
-  return RendererContentSettingRules::IsRendererContentSetting((content_type));
-}
-
-void MaybeSendRendererContentSettingsRules(
-    content::RenderFrameHost* rfh,
-    const HostContentSettingsMap* map,
-    PageSpecificContentSettings::Delegate* delegate) {
-  DCHECK_EQ(rfh, rfh->GetMainFrame());
-  // Only send a message to the renderer if it is initialised and not dead.
-  // Otherwise, the IPC messages will be queued in the browser process,
-  // potentially causing large memory leaks. See https://crbug.com/875937.
-  content::RenderProcessHost* process = rfh->GetProcess();
-  if (!process->IsInitializedAndNotDead())
-    return;
-
-  RendererContentSettingRules rules;
-  GetRendererContentSettingRules(map, &rules);
-  delegate->SetContentSettingRules(process, rules);
-}
-
 bool WillNavigationCreateNewPageSpecificContentSettingsOnCommit(
     content::NavigationHandle* navigation_handle) {
   return navigation_handle->IsInMainFrame() &&
@@ -199,20 +171,6 @@
     pscs->OnServiceWorkerAccessed(scope, allowed);
 }
 
-void PageSpecificContentSettings::WebContentsHandler::ReadyToCommitNavigation(
-    content::NavigationHandle* navigation_handle) {
-  if (!WillNavigationCreateNewPageSpecificContentSettingsOnCommit(
-          navigation_handle)) {
-    return;
-  }
-
-  // There may be content settings that were updated for the navigated URL.
-  // These would not have been sent before if we're navigating cross-origin.
-  // Ensure up to date rules are sent before navigation commits.
-  MaybeSendRendererContentSettingsRules(navigation_handle->GetRenderFrameHost(),
-                                        map_, delegate_.get());
-}
-
 void PageSpecificContentSettings::WebContentsHandler::DidFinishNavigation(
     content::NavigationHandle* navigation_handle) {
   if (!navigation_handle->HasCommitted())
@@ -875,12 +833,6 @@
     default:
       break;
   }
-
-  if (!ShouldSendUpdatedContentSettingsRulesToRenderer(content_type))
-    return;
-
-  MaybeSendRendererContentSettingsRules(&page().GetMainDocument(), map_,
-                                        delegate_);
 }
 
 void PageSpecificContentSettings::ClearContentSettingsChangedViaPageInfo() {
diff --git a/components/content_settings/browser/page_specific_content_settings.h b/components/content_settings/browser/page_specific_content_settings.h
index bc96f90..a225bc2 100644
--- a/components/content_settings/browser/page_specific_content_settings.h
+++ b/components/content_settings/browser/page_specific_content_settings.h
@@ -470,8 +470,6 @@
         content::RenderFrameHost* rfh);
 
     // content::WebContentsObserver overrides.
-    void ReadyToCommitNavigation(
-        content::NavigationHandle* navigation_handle) override;
     void DidFinishNavigation(
         content::NavigationHandle* navigation_handle) override;
     void OnCookiesAccessed(
diff --git a/components/content_settings/renderer/content_settings_agent_impl.cc b/components/content_settings/renderer/content_settings_agent_impl.cc
index 592f3774..4ba85310 100644
--- a/components/content_settings/renderer/content_settings_agent_impl.cc
+++ b/components/content_settings/renderer/content_settings_agent_impl.cc
@@ -191,10 +191,6 @@
 
 void ContentSettingsAgentImpl::DidCommitProvisionalLoad(
     ui::PageTransition transition) {
-  blink::WebLocalFrame* frame = render_frame()->GetWebFrame();
-  if (frame->Parent())
-    return;  // Not a top-level navigation.
-
   // Clear "block" flags for the new page. This needs to happen before any of
   // `allowScript()`, `allowScriptFromSource()`, `allowImage()`, or
   // `allowPlugins()` is called for the new page so that these functions can
@@ -202,6 +198,10 @@
   // "blocked".
   ClearBlockedContentSettings();
 
+  blink::WebLocalFrame* frame = render_frame()->GetWebFrame();
+  if (frame->Parent())
+    return;  // Not a top-level navigation.
+
   if (!base::FeatureList::IsEnabled(
           features::kNavigationThreadingOptimizations)) {
     // TODO(crbug.com/1187753): Remove this once it's verified it isn't needed.
diff --git a/components/metrics/structured/external_metrics_unittest.cc b/components/metrics/structured/external_metrics_unittest.cc
index 4446e14..fb6ff91 100644
--- a/components/metrics/structured/external_metrics_unittest.cc
+++ b/components/metrics/structured/external_metrics_unittest.cc
@@ -3,12 +3,14 @@
 // found in the LICENSE file.
 
 #include "components/metrics/structured/external_metrics.h"
+#include "components/metrics/structured/structured_metrics_features.h"
 
 #include <memory>
 
 #include "base/files/file_util.h"
 #include "base/files/scoped_temp_dir.h"
 #include "base/logging.h"
+#include "base/test/scoped_feature_list.h"
 #include "base/test/task_environment.h"
 #include "components/metrics/structured/storage.pb.h"
 #include "testing/gmock/include/gmock/gmock.h"
@@ -52,7 +54,15 @@
 
 class ExternalMetricsTest : public testing::Test {
  public:
-  void SetUp() override { ASSERT_TRUE(temp_dir_.CreateUniqueTempDir()); }
+  void SetUp() override {
+    ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
+
+    // TODO(b/181724341): Remove this when the bluetooth metrics feature is
+    // enabled by default.
+    scoped_feature_list_.InitWithFeatures(
+        /*enabled_features=*/{},
+        /*disabled_features=*/{kBluetoothSessionizedMetrics});
+  }
 
   void Init() {
     // We don't use the scheduling feature when testing ExternalMetrics, instead
@@ -86,6 +96,7 @@
 
   void Wait() { task_environment_.RunUntilIdle(); }
 
+  base::test::ScopedFeatureList scoped_feature_list_;
   base::ScopedTempDir temp_dir_;
   std::unique_ptr<ExternalMetrics> external_metrics_;
   absl::optional<EventsProto> proto_;
diff --git a/components/metrics/structured/structured_metrics_features.cc b/components/metrics/structured/structured_metrics_features.cc
index 4023d19..c98b74a 100644
--- a/components/metrics/structured/structured_metrics_features.cc
+++ b/components/metrics/structured/structured_metrics_features.cc
@@ -14,7 +14,7 @@
 
 // TODO(b/181724341): Remove this experimental once the feature is rolled out.
 const base::Feature kBluetoothSessionizedMetrics{
-    "BluetoothSessionizedMetrics", base::FEATURE_DISABLED_BY_DEFAULT};
+    "BluetoothSessionizedMetrics", base::FEATURE_ENABLED_BY_DEFAULT};
 
 const base::Feature kUseCrosApiInterface{"UseCrosApiInterface",
                                          base::FEATURE_DISABLED_BY_DEFAULT};
diff --git a/components/optimization_guide/core/BUILD.gn b/components/optimization_guide/core/BUILD.gn
index 5927f2cd..1e9b53d 100644
--- a/components/optimization_guide/core/BUILD.gn
+++ b/components/optimization_guide/core/BUILD.gn
@@ -184,6 +184,7 @@
   public_deps = [
     ":entities",
     ":model_executor",
+    "//components/leveldb_proto",
     "//components/optimization_guide:machine_learning_tflite_buildflags",
     "//third_party/re2",
   ]
@@ -202,7 +203,6 @@
   deps = [
     ":bloomfilter",
     "//base",
-    "//components/leveldb_proto",
     "//components/optimization_guide/proto:optimization_guide_proto",
     "//components/prefs",
     "//components/unified_consent",
@@ -223,6 +223,32 @@
   }
 }
 
+static_library("prediction") {
+  sources = [
+    "prediction_manager.cc",
+    "prediction_manager.h",
+    "prediction_model_download_manager.cc",
+    "prediction_model_download_manager.h",
+    "prediction_model_download_observer.h",
+  ]
+  deps = [
+    "//components/crx_file",
+    "//components/download/public/background_service:public",
+    "//components/optimization_guide/core",
+    "//components/optimization_guide/proto:optimization_guide_proto",
+    "//components/prefs",
+    "//components/services/unzip/public/cpp",
+    "//crypto",
+    "//net/traffic_annotation",
+    "//services/network/public/cpp",
+  ]
+  if (is_ios) {
+    deps += [ "//components/services/unzip:in_process" ]
+  } else {
+    deps += [ "//components/services/unzip/content" ]
+  }
+}
+
 static_library("test_support") {
   testonly = true
   sources = [
@@ -310,6 +336,8 @@
     "optimization_hints_component_update_listener_unittest.cc",
     "optimization_metadata_unittest.cc",
     "page_content_annotation_job_unittest.cc",
+    "prediction_manager_unittest.cc",
+    "prediction_model_download_manager_unittest.cc",
     "prediction_model_fetcher_unittest.cc",
     "store_update_data_unittest.cc",
     "url_pattern_with_wildcards_unittest.cc",
@@ -335,12 +363,17 @@
     ":bloomfilter",
     ":core",
     ":entities",
+    ":prediction",
     ":test_support",
+    ":unit_tests_bundle_data",
     "//base",
     "//base/test:test_support",
+    "//build:chromeos_buildflags",
+    "//components/download/public/background_service/test:test_support",
     "//components/leveldb_proto:test_support",
     "//components/optimization_guide/proto:optimization_guide_proto",
     "//components/prefs:test_support",
+    "//components/services/unzip:in_process",
     "//components/sync_preferences:test_support",
     "//components/ukm:test_support",
     "//components/unified_consent",
@@ -350,8 +383,13 @@
     "//services/network:test_support",
     "//testing/gmock",
     "//testing/gtest",
+    "//third_party/zlib/google:zip",
+    "//ui/base",
     "//url:url",
   ]
+  if (!is_ios) {
+    deps += [ "//components/services/unzip/content" ]
+  }
   if (build_with_tflite_lib) {
     deps += [
       "//third_party/abseil-cpp:absl",
@@ -363,6 +401,18 @@
   }
 }
 
+bundle_data("unit_tests_bundle_data") {
+  visibility = [ ":unit_tests" ]
+  testonly = true
+  sources = [
+    "//components/test/data/optimization_guide/bert_page_topics_model.tflite",
+    "//components/test/data/optimization_guide/invalid_model.crx3",
+    "//components/test/data/optimization_guide/simple_test.tflite",
+  ]
+  outputs = [ "{{bundle_resources_dir}}/" +
+              "{{source_root_relative_dir}}/{{source_file_part}}" ]
+}
+
 if (is_android) {
   java_cpp_enum("optimization_guide_generated_enums") {
     sources = [ "optimization_guide_decision.h" ]
diff --git a/components/optimization_guide/core/DEPS b/components/optimization_guide/core/DEPS
index 8ce99af0..941be9d 100644
--- a/components/optimization_guide/core/DEPS
+++ b/components/optimization_guide/core/DEPS
@@ -1,6 +1,19 @@
 include_rules = [
+  "+components/crx_file",
+  "+components/download/public/background_service",
+  "+components/services/unzip",
   "+components/ukm/test_ukm_recorder.h",
+  "+crypto",
+  "+mojo/public/cpp",
   "+services/metrics/public/cpp",
   "+third_party/zlib/google",
   "+ui/base/l10n",
 ]
+
+specific_include_rules = {
+  ".*_unittest\.cc": [
+    "+third_party/zlib/google/zip.h",
+    "+ui/base",
+  ]
+}
+
diff --git a/chrome/browser/optimization_guide/prediction/prediction_manager.cc b/components/optimization_guide/core/prediction_manager.cc
similarity index 94%
rename from chrome/browser/optimization_guide/prediction/prediction_manager.cc
rename to components/optimization_guide/core/prediction_manager.cc
index 2330d60b..342e959 100644
--- a/chrome/browser/optimization_guide/prediction/prediction_manager.cc
+++ b/components/optimization_guide/core/prediction_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/optimization_guide/prediction/prediction_manager.h"
+#include "components/optimization_guide/core/prediction_manager.h"
 
 #include <memory>
 #include <utility>
@@ -23,13 +23,6 @@
 #include "base/task/sequenced_task_runner.h"
 #include "base/task/thread_pool.h"
 #include "base/time/default_clock.h"
-#include "chrome/browser/browser_process.h"
-#include "chrome/browser/download/background_download_service_factory.h"
-#include "chrome/browser/optimization_guide/prediction/prediction_model_download_manager.h"
-#include "chrome/browser/profiles/profile.h"
-#include "chrome/browser/profiles/profile_key.h"
-#include "chrome/common/chrome_paths.h"
-#include "components/optimization_guide/content/browser/optimization_guide_decider.h"
 #include "components/optimization_guide/core/model_info.h"
 #include "components/optimization_guide/core/model_util.h"
 #include "components/optimization_guide/core/optimization_guide_constants.h"
@@ -40,14 +33,13 @@
 #include "components/optimization_guide/core/optimization_guide_prefs.h"
 #include "components/optimization_guide/core/optimization_guide_store.h"
 #include "components/optimization_guide/core/optimization_guide_switches.h"
+#include "components/optimization_guide/core/optimization_guide_util.h"
 #include "components/optimization_guide/core/optimization_target_model_observer.h"
+#include "components/optimization_guide/core/prediction_model_download_manager.h"
 #include "components/optimization_guide/core/prediction_model_fetcher_impl.h"
 #include "components/optimization_guide/core/store_update_data.h"
 #include "components/optimization_guide/proto/models.pb.h"
 #include "components/prefs/pref_service.h"
-#include "components/site_engagement/content/site_engagement_service.h"
-#include "content/public/browser/network_service_instance.h"
-#include "content/public/browser/web_contents.h"
 #include "mojo/public/cpp/bindings/remote.h"
 #include "services/network/public/cpp/shared_url_loader_factory.h"
 
@@ -205,16 +197,21 @@
     base::WeakPtr<OptimizationGuideStore> model_and_features_store,
     scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
     PrefService* pref_service,
-    Profile* profile,
-    OptimizationGuideLogger* optimization_guide_logger)
+    bool off_the_record,
+    const std::string& application_locale,
+    const base::FilePath& models_dir_path,
+    OptimizationGuideLogger* optimization_guide_logger,
+    BackgroundDownloadServiceProvider background_download_service_provider)
     : prediction_model_download_manager_(nullptr),
       model_and_features_store_(model_and_features_store),
       url_loader_factory_(url_loader_factory),
       optimization_guide_logger_(optimization_guide_logger),
       pref_service_(pref_service),
-      profile_(profile),
-      clock_(base::DefaultClock::GetInstance()) {
-  Initialize();
+      clock_(base::DefaultClock::GetInstance()),
+      off_the_record_(off_the_record),
+      application_locale_(application_locale),
+      models_dir_path_(models_dir_path) {
+  Initialize(std::move(background_download_service_provider));
 }
 
 PredictionManager::~PredictionManager() {
@@ -222,12 +219,14 @@
     prediction_model_download_manager_->RemoveObserver(this);
 }
 
-void PredictionManager::Initialize() {
+void PredictionManager::Initialize(
+    BackgroundDownloadServiceProvider background_download_service_provider) {
   if (model_and_features_store_) {
     model_and_features_store_->Initialize(
         switches::ShouldPurgeModelAndFeaturesStoreOnStartup(),
         base::BindOnce(&PredictionManager::OnStoreInitialized,
-                       ui_weak_ptr_factory_.GetWeakPtr()));
+                       ui_weak_ptr_factory_.GetWeakPtr(),
+                       std::move(background_download_service_provider)));
   }
 }
 
@@ -363,7 +362,7 @@
   if (switches::IsModelOverridePresent())
     return;
 
-  if (!ShouldFetchModels(profile_->IsOffTheRecord(), pref_service_))
+  if (!ShouldFetchModels(off_the_record_, pref_service_))
     return;
 
   // Models should not be fetched if there are no optimization targets
@@ -399,8 +398,8 @@
   // Active field trials convey some sort of user information, so
   // ensure that the user has opted into the right permissions before adding
   // these fields to the request.
-  if (IsUserPermittedToFetchFromRemoteOptimizationGuide(
-          profile_->IsOffTheRecord(), pref_service_)) {
+  if (IsUserPermittedToFetchFromRemoteOptimizationGuide(off_the_record_,
+                                                        pref_service_)) {
     google::protobuf::RepeatedPtrField<proto::FieldTrial> current_field_trials =
         GetActiveFieldTrialsAllowedForFetch();
     active_field_trials = std::vector<proto::FieldTrial>(
@@ -450,7 +449,7 @@
   bool fetch_initiated =
       prediction_model_fetcher_->FetchOptimizationGuideServiceModels(
           models_info, active_field_trials, proto::CONTEXT_BATCH_UPDATE_MODELS,
-          g_browser_process->GetApplicationLocale(),
+          application_locale_,
           base::BindOnce(&PredictionManager::OnModelsFetched,
                          ui_weak_ptr_factory_.GetWeakPtr()));
 
@@ -622,19 +621,22 @@
       "OptimizationGuide.PredictionManager.PredictionModelsStored", true);
 }
 
-void PredictionManager::OnStoreInitialized() {
+void PredictionManager::OnStoreInitialized(
+    BackgroundDownloadServiceProvider background_download_service_provider) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   store_is_ready_ = true;
   LOCAL_HISTOGRAM_BOOLEAN(
       "OptimizationGuide.PredictionManager.StoreInitialized", true);
 
   // Create the download manager here if we are allowed to.
-  if (features::IsModelDownloadingEnabled() && !profile_->IsOffTheRecord() &&
+  if (features::IsModelDownloadingEnabled() && !off_the_record_ &&
       !prediction_model_download_manager_) {
     prediction_model_download_manager_ =
         std::make_unique<PredictionModelDownloadManager>(
-            BackgroundDownloadServiceFactory::GetForKey(
-                profile_->GetProfileKey()),
+            background_download_service_provider
+                ? std::move(background_download_service_provider).Run()
+                : nullptr,
+            models_dir_path_,
             base::ThreadPool::CreateSequencedTaskRunner(
                 {base::MayBlock(), base::TaskPriority::BEST_EFFORT,
                  base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN}));
@@ -792,7 +794,7 @@
 }
 
 void PredictionManager::MaybeFetchModels() {
-  if (!ShouldFetchModels(profile_->IsOffTheRecord(), pref_service_))
+  if (!ShouldFetchModels(off_the_record_, pref_service_))
     return;
 
   // Add a slight delay to allow the rest of the browser startup process to
diff --git a/chrome/browser/optimization_guide/prediction/prediction_manager.h b/components/optimization_guide/core/prediction_manager.h
similarity index 86%
rename from chrome/browser/optimization_guide/prediction/prediction_manager.h
rename to components/optimization_guide/core/prediction_manager.h
index 73f019f1..787a7d66 100644
--- a/chrome/browser/optimization_guide/prediction/prediction_manager.h
+++ b/components/optimization_guide/core/prediction_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_OPTIMIZATION_GUIDE_PREDICTION_PREDICTION_MANAGER_H_
-#define CHROME_BROWSER_OPTIMIZATION_GUIDE_PREDICTION_PREDICTION_MANAGER_H_
+#ifndef COMPONENTS_OPTIMIZATION_GUIDE_CORE_PREDICTION_MANAGER_H_
+#define COMPONENTS_OPTIMIZATION_GUIDE_CORE_PREDICTION_MANAGER_H_
 
 #include <memory>
 #include <string>
@@ -12,25 +12,29 @@
 #include "base/containers/flat_map.h"
 #include "base/containers/flat_set.h"
 #include "base/containers/lru_cache.h"
+#include "base/files/file_path.h"
 #include "base/memory/raw_ptr.h"
 #include "base/memory/weak_ptr.h"
 #include "base/observer_list.h"
 #include "base/sequence_checker.h"
 #include "base/time/clock.h"
 #include "base/timer/timer.h"
-#include "chrome/browser/optimization_guide/prediction/prediction_model_download_observer.h"
 #include "components/optimization_guide/core/model_enums.h"
 #include "components/optimization_guide/core/optimization_guide_enums.h"
+#include "components/optimization_guide/core/prediction_model_download_observer.h"
 #include "components/optimization_guide/proto/models.pb.h"
 #include "url/origin.h"
 
+namespace download {
+class BackgroundDownloadService;
+}  // namespace download
+
 namespace network {
 class SharedURLLoaderFactory;
 }  // namespace network
 
 class OptimizationGuideLogger;
 class PrefService;
-class Profile;
 
 namespace optimization_guide {
 
@@ -46,12 +50,21 @@
 // for an OptimizationTarget.
 class PredictionManager : public PredictionModelDownloadObserver {
  public:
+  // BackgroundDownloadService is only available once the profile is fully
+  // initialized and that cannot be done as part of |Initialize|. Get a provider
+  // to retrieve the service when it is needed.
+  typedef base::OnceCallback<download::BackgroundDownloadService*(void)>
+      BackgroundDownloadServiceProvider;
+
   PredictionManager(
       base::WeakPtr<OptimizationGuideStore> model_and_features_store,
       scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
       PrefService* pref_service,
-      Profile* profile,
-      OptimizationGuideLogger* optimization_guide_logger);
+      bool off_the_record,
+      const std::string& application_locale,
+      const base::FilePath& models_dir_path,
+      OptimizationGuideLogger* optimization_guide_logger,
+      BackgroundDownloadServiceProvider background_dowload_service_provider);
 
   PredictionManager(const PredictionManager&) = delete;
   PredictionManager& operator=(const PredictionManager&) = delete;
@@ -125,9 +138,11 @@
  private:
   friend class PredictionManagerTestBase;
 
-  // Called on construction to initialize the prediction model and host model
-  // features store, and register as an observer to the network quality tracker.
-  void Initialize();
+  // Called on construction to initialize the prediction model.
+  // |background_dowload_service_provider| can provide the
+  // BackgroundDownloadService if needed to download models.
+  void Initialize(
+      BackgroundDownloadServiceProvider background_dowload_service_provider);
 
   // Called to make a request to fetch models from the remote Optimization Guide
   // Service. Used to fetch models for the registered optimization targets.
@@ -145,7 +160,8 @@
   // initialized. The prediction manager can load models from
   // the store for registered optimization targets. |store_is_ready_| is set to
   // true.
-  void OnStoreInitialized();
+  void OnStoreInitialized(
+      BackgroundDownloadServiceProvider background_dowload_service_provider);
 
   // Callback run after prediction models are stored in
   // |model_and_features_store_|.
@@ -255,9 +271,6 @@
   // A reference to the PrefService for this profile. Not owned.
   raw_ptr<PrefService> pref_service_ = nullptr;
 
-  // A reference to the profile. Not owned.
-  raw_ptr<Profile> profile_ = nullptr;
-
   // The timer used to schedule fetching prediction models and host model
   // features from the remote Optimization Guide Service.
   base::OneShotTimer fetch_timer_;
@@ -273,6 +286,15 @@
   // for use.
   bool host_model_features_loaded_ = false;
 
+  // Whether the profile for this PredictionManager is off the record.
+  bool off_the_record_ = false;
+
+  // The locale of the application.
+  std::string application_locale_;
+
+  // The path to the directory containing the models.
+  base::FilePath models_dir_path_;
+
   SEQUENCE_CHECKER(sequence_checker_);
 
   // Used to get |weak_ptr_| to self on the UI thread.
@@ -281,4 +303,4 @@
 
 }  // namespace optimization_guide
 
-#endif  // CHROME_BROWSER_OPTIMIZATION_GUIDE_PREDICTION_PREDICTION_MANAGER_H_
+#endif  // COMPONENTS_OPTIMIZATION_GUIDE_CORE_PREDICTION_MANAGER_H_
diff --git a/chrome/browser/optimization_guide/prediction/prediction_manager_unittest.cc b/components/optimization_guide/core/prediction_manager_unittest.cc
similarity index 95%
rename from chrome/browser/optimization_guide/prediction/prediction_manager_unittest.cc
rename to components/optimization_guide/core/prediction_manager_unittest.cc
index 9aea2cd..4807081 100644
--- a/chrome/browser/optimization_guide/prediction/prediction_manager_unittest.cc
+++ b/components/optimization_guide/core/prediction_manager_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/optimization_guide/prediction/prediction_manager.h"
+#include "components/optimization_guide/core/prediction_manager.h"
 
 #include <map>
 #include <memory>
@@ -15,14 +15,10 @@
 #include "base/test/gtest_util.h"
 #include "base/test/metrics/histogram_tester.h"
 #include "base/test/scoped_feature_list.h"
+#include "base/test/task_environment.h"
 #include "base/time/time.h"
 #include "build/build_config.h"
 #include "build/chromeos_buildflags.h"
-#include "chrome/browser/bitmap_fetcher/bitmap_fetcher.h"
-#include "chrome/browser/browser_process.h"
-#include "chrome/browser/optimization_guide/optimization_guide_web_contents_observer.h"
-#include "chrome/browser/optimization_guide/prediction/prediction_model_download_manager.h"
-#include "chrome/test/base/testing_profile.h"
 #include "components/leveldb_proto/testing/fake_db.h"
 #include "components/optimization_guide/core/model_util.h"
 #include "components/optimization_guide/core/optimization_guide_features.h"
@@ -32,6 +28,7 @@
 #include "components/optimization_guide/core/optimization_guide_test_util.h"
 #include "components/optimization_guide/core/optimization_guide_util.h"
 #include "components/optimization_guide/core/optimization_target_model_observer.h"
+#include "components/optimization_guide/core/prediction_model_download_manager.h"
 #include "components/optimization_guide/core/prediction_model_fetcher.h"
 #include "components/optimization_guide/core/prediction_model_fetcher_impl.h"
 #include "components/optimization_guide/core/proto_database_provider_test_base.h"
@@ -39,9 +36,6 @@
 #include "components/optimization_guide/proto/models.pb.h"
 #include "components/prefs/testing_pref_service.h"
 #include "components/variations/scoped_variations_ids_provider.h"
-#include "content/public/test/browser_task_environment.h"
-#include "content/public/test/test_web_contents_factory.h"
-#include "content/public/test/web_contents_tester.h"
 #include "services/network/public/cpp/shared_url_loader_factory.h"
 #include "services/network/public/cpp/weak_wrapper_shared_url_loader_factory.h"
 #include "services/network/test/test_network_connection_tracker.h"
@@ -114,8 +108,10 @@
     : public PredictionModelDownloadManager {
  public:
   explicit FakePredictionModelDownloadManager(
+      const base::FilePath& models_dir_path,
       scoped_refptr<base::SequencedTaskRunner> task_runner)
       : PredictionModelDownloadManager(/*download_service=*/nullptr,
+                                       models_dir_path,
                                        task_runner) {}
   ~FakePredictionModelDownloadManager() override = default;
 
@@ -342,12 +338,19 @@
       base::WeakPtr<OptimizationGuideStore> model_and_features_store,
       scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
       PrefService* pref_service,
-      Profile* profile)
-      : PredictionManager(model_and_features_store,
-                          url_loader_factory,
-                          pref_service,
-                          profile,
-                          /*optimization_guide_logger=*/nullptr) {}
+      bool off_the_record,
+      const std::string& application_locale,
+      const base::FilePath& models_dir_path)
+      : PredictionManager(
+            model_and_features_store,
+            url_loader_factory,
+            pref_service,
+            off_the_record,
+            application_locale,
+            models_dir_path,
+            /*optimization_guide_logger=*/nullptr,
+            /*background_download_service_provider=*/
+            base::OnceCallback<download::BackgroundDownloadService*()>()) {}
 
   ~TestPredictionManager() override = default;
 };
@@ -365,7 +368,6 @@
 
   void SetUp() override {
     ProtoDatabaseProviderTestBase::SetUp();
-    web_contents_factory_ = std::make_unique<content::TestWebContentsFactory>();
 
     pref_service_ = std::make_unique<TestingPrefServiceSimple>();
     prefs::RegisterProfilePrefs(pref_service_->registry());
@@ -387,7 +389,7 @@
     model_and_features_store_ = CreateModelAndHostModelFeaturesStore();
     prediction_manager_ = std::make_unique<TestPredictionManager>(
         model_and_features_store_->AsWeakPtr(), url_loader_factory_,
-        pref_service_.get(), &testing_profile_);
+        pref_service_.get(), false, "en-US", temp_dir());
     prediction_manager_->SetClockForTesting(task_environment_.GetMockClock());
   }
 
@@ -436,7 +438,7 @@
   FakePredictionModelDownloadManager* prediction_model_download_manager()
       const {
     return static_cast<FakePredictionModelDownloadManager*>(
-        prediction_manager()->prediction_model_download_manager());
+        temp_dir(), prediction_manager()->prediction_model_download_manager());
   }
 
   TestOptimizationGuideStore* models_and_features_store() const {
@@ -450,16 +452,12 @@
 
   TestingPrefServiceSimple* pref_service() const { return pref_service_.get(); }
 
-  TestingProfile* profile() { return &testing_profile_; }
-
   void RunUntilIdle() {
     task_environment_.RunUntilIdle();
     base::RunLoop().RunUntilIdle();
   }
 
-  content::BrowserTaskEnvironment* task_environment() {
-    return &task_environment_;
-  }
+  base::test::TaskEnvironment* task_environment() { return &task_environment_; }
 
   void SetOptimizationGuideFetchingPrefEnabled(bool enabled) {
     pref_service_->SetBoolean(prefs::kOptimizationGuideFetchingEnabled,
@@ -473,7 +471,7 @@
   base::test::ScopedFeatureList feature_list_;
 
  private:
-  content::BrowserTaskEnvironment task_environment_{
+  base::test::TaskEnvironment task_environment_{
       base::test::TaskEnvironment::MainThreadType::UI,
       base::test::TaskEnvironment::TimeSource::MOCK_TIME};
   StoreEntryMap db_store_;
@@ -481,9 +479,7 @@
   std::unique_ptr<TestPredictionManager> prediction_manager_;
   scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory_;
   network::TestURLLoaderFactory test_url_loader_factory_;
-  TestingProfile testing_profile_;
   std::unique_ptr<TestingPrefServiceSimple> pref_service_;
-  std::unique_ptr<content::TestWebContentsFactory> web_contents_factory_;
 };
 
 class PredictionManagerRemoteFetchingDisabledTest
@@ -517,8 +513,11 @@
   PredictionManagerTest() {
     // This needs to be done before any tasks are run that might check if a
     // feature is enabled, to avoid tsan errors.
-    feature_list_.InitAndEnableFeature(
-        features::kRemoteOptimizationGuideFetching);
+
+    feature_list_.InitWithFeatures(
+        {features::kRemoteOptimizationGuideFetching,
+         features::kOptimizationGuideModelDownloading},
+        {});
   }
 
  private:
@@ -864,7 +863,7 @@
           PredictionModelFetcherEndState::kFetchSuccessWithModels));
   prediction_manager()->SetPredictionModelDownloadManagerForTesting(
       std::make_unique<FakePredictionModelDownloadManager>(
-          task_environment()->GetMainThreadTaskRunner()));
+          temp_dir(), task_environment()->GetMainThreadTaskRunner()));
   prediction_model_download_manager()->SetAvailableForDownloads(false);
 
   FakeOptimizationTargetModelObserver observer;
@@ -889,7 +888,7 @@
           PredictionModelFetcherEndState::kFetchSuccessWithModels));
   prediction_manager()->SetPredictionModelDownloadManagerForTesting(
       std::make_unique<FakePredictionModelDownloadManager>(
-          task_environment()->GetMainThreadTaskRunner()));
+          temp_dir(), task_environment()->GetMainThreadTaskRunner()));
 
   FakeOptimizationTargetModelObserver observer;
   prediction_manager()->AddObserverForOptimizationTargetModel(
@@ -983,6 +982,9 @@
   prediction_manager()->SetPredictionModelFetcherForTesting(
       BuildTestPredictionModelFetcher(
           PredictionModelFetcherEndState::kFetchFailed));
+  prediction_manager()->SetPredictionModelDownloadManagerForTesting(
+      std::make_unique<FakePredictionModelDownloadManager>(
+          temp_dir(), task_environment()->GetMainThreadTaskRunner()));
 
   FakeOptimizationTargetModelObserver observer;
   prediction_manager()->AddObserverForOptimizationTargetModel(
@@ -1007,8 +1009,9 @@
   prediction_manager()->SetPredictionModelFetcherForTesting(
       BuildTestPredictionModelFetcher(
           PredictionModelFetcherEndState::kFetchSuccessWithModels));
-
-  g_browser_process->SetApplicationLocale("en-US");
+  prediction_manager()->SetPredictionModelDownloadManagerForTesting(
+      std::make_unique<FakePredictionModelDownloadManager>(
+          temp_dir(), task_environment()->GetMainThreadTaskRunner()));
 
   FakeOptimizationTargetModelObserver observer;
   prediction_manager()->AddObserverForOptimizationTargetModel(
diff --git a/chrome/browser/optimization_guide/prediction/prediction_model_download_manager.cc b/components/optimization_guide/core/prediction_model_download_manager.cc
similarity index 94%
rename from chrome/browser/optimization_guide/prediction/prediction_model_download_manager.cc
rename to components/optimization_guide/core/prediction_model_download_manager.cc
index e15777f8..5c0e11d 100644
--- a/chrome/browser/optimization_guide/prediction/prediction_model_download_manager.cc
+++ b/components/optimization_guide/core/prediction_model_download_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/optimization_guide/prediction/prediction_model_download_manager.h"
+#include "components/optimization_guide/core/prediction_model_download_manager.h"
 
 #include "base/bind.h"
 #include "base/containers/flat_set.h"
@@ -18,8 +18,6 @@
 #include "base/task/post_task.h"
 #include "base/task/thread_pool.h"
 #include "build/build_config.h"
-#include "chrome/browser/optimization_guide/prediction/prediction_model_download_observer.h"
-#include "chrome/common/chrome_paths.h"
 #include "components/crx_file/crx_verifier.h"
 #include "components/download/public/background_service/background_download_service.h"
 #include "components/optimization_guide/core/model_util.h"
@@ -27,11 +25,17 @@
 #include "components/optimization_guide/core/optimization_guide_features.h"
 #include "components/optimization_guide/core/optimization_guide_switches.h"
 #include "components/optimization_guide/core/optimization_guide_util.h"
-#include "components/services/unzip/content/unzip_service.h"
+#include "components/optimization_guide/core/prediction_model_download_observer.h"
 #include "components/services/unzip/public/cpp/unzip.h"
 #include "crypto/sha2.h"
 #include "net/traffic_annotation/network_traffic_annotation.h"
 
+#if BUILDFLAG(IS_IOS)
+#include "components/services/unzip/in_process_unzipper.h"  // nogncheck
+#else
+#include "components/services/unzip/content/unzip_service.h"  // nogncheck
+#endif
+
 namespace optimization_guide {
 
 namespace {
@@ -95,10 +99,12 @@
 
 PredictionModelDownloadManager::PredictionModelDownloadManager(
     download::BackgroundDownloadService* download_service,
+    const base::FilePath& models_dir_path,
     scoped_refptr<base::SequencedTaskRunner> background_task_runner)
     : download_service_(download_service),
       is_available_for_downloads_(true),
       api_key_(features::GetOptimizationGuideServiceAPIKey()),
+      models_dir_path_(models_dir_path),
       background_task_runner_(background_task_runner) {}
 
 PredictionModelDownloadManager::~PredictionModelDownloadManager() = default;
@@ -269,8 +275,13 @@
   if (!unzip_paths)
     return;
 
+#if BUILDFLAG(IS_IOS)
+  auto unzipper = unzip::LaunchInProcessUnzipper();
+#else
+  auto unzipper = unzip::LaunchUnzipper();
+#endif
   unzip::Unzip(
-      unzip::LaunchUnzipper(), unzip_paths->first, unzip_paths->second,
+      std::move(unzipper), unzip_paths->first, unzip_paths->second,
       base::BindOnce(&PredictionModelDownloadManager::OnDownloadUnzipped,
                      ui_weak_ptr_factory_.GetWeakPtr(), unzip_paths->first,
                      unzip_paths->second));
@@ -296,7 +307,7 @@
   background_task_runner_->PostTaskAndReplyWithResult(
       FROM_HERE,
       base::BindOnce(&PredictionModelDownloadManager::ProcessUnzippedContents,
-                     unzipped_dir_path),
+                     models_dir_path_, unzipped_dir_path),
       base::BindOnce(&PredictionModelDownloadManager::NotifyModelReady,
                      ui_weak_ptr_factory_.GetWeakPtr()));
 }
@@ -304,6 +315,7 @@
 // static
 absl::optional<proto::PredictionModel>
 PredictionModelDownloadManager::ProcessUnzippedContents(
+    const base::FilePath& model_dir_path,
     const base::FilePath& unzipped_dir_path) {
   // Clean up temp dir when this function finishes.
   base::SequencedTaskRunnerHandle::Get()->PostTask(
@@ -330,9 +342,7 @@
     return absl::nullopt;
   }
 
-  base::FilePath models_dir;
-  if (!base::PathService::Get(chrome::DIR_OPTIMIZATION_GUIDE_PREDICTION_MODELS,
-                              &models_dir)) {
+  if (model_dir_path.empty()) {
     RecordPredictionModelDownloadStatus(
         PredictionModelDownloadStatus::kOptGuideDirectoryDoesNotExist);
 
@@ -341,7 +351,8 @@
 
   // Move each packaged file away from temp directory into a new directory.
 
-  base::FilePath store_dir = GetDirectoryForModelInfo(models_dir, model_info);
+  base::FilePath store_dir =
+      GetDirectoryForModelInfo(model_dir_path, model_info);
   if (!base::CreateDirectory(store_dir)) {
     RecordPredictionModelDownloadStatus(
         PredictionModelDownloadStatus::kCouldNotCreateDirectory);
diff --git a/chrome/browser/optimization_guide/prediction/prediction_model_download_manager.h b/components/optimization_guide/core/prediction_model_download_manager.h
similarity index 92%
rename from chrome/browser/optimization_guide/prediction/prediction_model_download_manager.h
rename to components/optimization_guide/core/prediction_model_download_manager.h
index 84f09f09..5a29f2a3 100644
--- a/chrome/browser/optimization_guide/prediction/prediction_model_download_manager.h
+++ b/components/optimization_guide/core/prediction_model_download_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_OPTIMIZATION_GUIDE_PREDICTION_PREDICTION_MODEL_DOWNLOAD_MANAGER_H_
-#define CHROME_BROWSER_OPTIMIZATION_GUIDE_PREDICTION_PREDICTION_MODEL_DOWNLOAD_MANAGER_H_
+#ifndef COMPONENTS_OPTIMIZATION_GUIDE_CORE_PREDICTION_MODEL_DOWNLOAD_MANAGER_H_
+#define COMPONENTS_OPTIMIZATION_GUIDE_CORE_PREDICTION_MODEL_DOWNLOAD_MANAGER_H_
 
 #include <map>
 #include <set>
@@ -34,6 +34,7 @@
  public:
   PredictionModelDownloadManager(
       download::BackgroundDownloadService* download_service,
+      const base::FilePath& models_dir_path,
       scoped_refptr<base::SequencedTaskRunner> background_task_runner);
   virtual ~PredictionModelDownloadManager();
   PredictionModelDownloadManager(const PredictionModelDownloadManager&) =
@@ -111,6 +112,7 @@
   // Must be called on the background thread, as it performs file I/O. This is a
   // stateless func to avoid needing weird lifetime stuff.
   static absl::optional<proto::PredictionModel> ProcessUnzippedContents(
+      const base::FilePath& model_dir_path,
       const base::FilePath& unzipped_dir_path);
 
   // Notifies |observers_| that a model is ready.
@@ -138,6 +140,9 @@
   // Whether the download should be verified. Should only be false for testing.
   bool should_verify_download_ = true;
 
+  // The path to the dir containing models.
+  base::FilePath models_dir_path_;
+
   // Background thread where download file processing should be performed.
   scoped_refptr<base::SequencedTaskRunner> background_task_runner_;
 
@@ -152,4 +157,4 @@
 
 }  // namespace optimization_guide
 
-#endif  // CHROME_BROWSER_OPTIMIZATION_GUIDE_PREDICTION_PREDICTION_MODEL_DOWNLOAD_MANAGER_H_
+#endif  // COMPONENTS_OPTIMIZATION_GUIDE_CORE_PREDICTION_MODEL_DOWNLOAD_MANAGER_H_
diff --git a/chrome/browser/optimization_guide/prediction/prediction_model_download_manager_unittest.cc b/components/optimization_guide/core/prediction_model_download_manager_unittest.cc
similarity index 93%
rename from chrome/browser/optimization_guide/prediction/prediction_model_download_manager_unittest.cc
rename to components/optimization_guide/core/prediction_model_download_manager_unittest.cc
index 207c2b5..6d1b2b3 100644
--- a/chrome/browser/optimization_guide/prediction/prediction_model_download_manager_unittest.cc
+++ b/components/optimization_guide/core/prediction_model_download_manager_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/optimization_guide/prediction/prediction_model_download_manager.h"
+#include "components/optimization_guide/core/prediction_model_download_manager.h"
 
 #include "base/files/file_util.h"
 #include "base/files/scoped_temp_dir.h"
@@ -14,19 +14,22 @@
 #include "base/test/scoped_feature_list.h"
 #include "base/test/task_environment.h"
 #include "build/build_config.h"
-#include "chrome/browser/optimization_guide/prediction/prediction_model_download_observer.h"
 #include "components/download/public/background_service/test/mock_download_service.h"
 #include "components/optimization_guide/core/model_util.h"
 #include "components/optimization_guide/core/optimization_guide_enums.h"
 #include "components/optimization_guide/core/optimization_guide_features.h"
 #include "components/optimization_guide/core/optimization_guide_switches.h"
 #include "components/optimization_guide/core/optimization_guide_util.h"
-#include "components/services/unzip/content/unzip_service.h"
+#include "components/optimization_guide/core/prediction_model_download_observer.h"
 #include "components/services/unzip/in_process_unzipper.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 #include "third_party/zlib/google/zip.h"
 
+#if !BUILDFLAG(IS_IOS)
+#include "components/services/unzip/content/unzip_service.h"  // nogncheck
+#endif
+
 namespace optimization_guide {
 
 using ::testing::_;
@@ -66,15 +69,18 @@
   ~PredictionModelDownloadManagerTest() override = default;
 
   void SetUp() override {
-    ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
+    ASSERT_TRUE(temp_download_dir_.CreateUniqueTempDir());
+    ASSERT_TRUE(temp_models_dir_.CreateUniqueTempDir());
     mock_download_service_ =
         std::make_unique<download::test::MockDownloadService>();
     download_manager_ = std::make_unique<PredictionModelDownloadManager>(
-        mock_download_service_.get(),
+        mock_download_service_.get(), temp_models_dir_.GetPath(),
         task_environment_.GetMainThreadTaskRunner());
 
+#if !BUILDFLAG(IS_IOS)
     unzip::SetUnzipperLaunchOverrideForTesting(
         base::BindRepeating(&unzip::LaunchInProcessUnzipper));
+#endif
   }
 
   void TearDown() override {
@@ -127,20 +133,22 @@
     base::PathService::Get(base::DIR_SOURCE_ROOT, &path);
     switch (file_status) {
       case PredictionModelDownloadFileStatus::kUnverifiedFile:
-        return temp_dir_.GetPath().AppendASCII("unverified.crx3");
+        return temp_download_dir_.GetPath().AppendASCII("unverified.crx3");
       case PredictionModelDownloadFileStatus::kVerifiedCrxWithInvalidPublisher:
-        return temp_dir_.GetPath().AppendASCII("invalidpublisher.crx3");
+        return temp_download_dir_.GetPath().AppendASCII(
+            "invalidpublisher.crx3");
       case PredictionModelDownloadFileStatus::kVerifiedCrxWithNoFiles:
-        return temp_dir_.GetPath().AppendASCII("nofiles.crx3");
+        return temp_download_dir_.GetPath().AppendASCII("nofiles.crx3");
       case PredictionModelDownloadFileStatus::kVerifiedCrxWithBadModelInfoFile:
-        return temp_dir_.GetPath().AppendASCII("badmodelinfo.crx3");
+        return temp_download_dir_.GetPath().AppendASCII("badmodelinfo.crx3");
       case PredictionModelDownloadFileStatus::kVerifiedCrxWithInvalidModelInfo:
-        return temp_dir_.GetPath().AppendASCII("invalidmodelinfo.crx3");
+        return temp_download_dir_.GetPath().AppendASCII(
+            "invalidmodelinfo.crx3");
       case PredictionModelDownloadFileStatus::
           kVerfiedCrxWithValidModelInfoNoModelFile:
-        return temp_dir_.GetPath().AppendASCII("nomodel.crx3");
+        return temp_download_dir_.GetPath().AppendASCII("nomodel.crx3");
       case PredictionModelDownloadFileStatus::kVerifiedCrxWithGoodModelFiles:
-        return temp_dir_.GetPath().AppendASCII("good.crx3");
+        return temp_download_dir_.GetPath().AppendASCII("good.crx3");
     }
   }
 
@@ -188,17 +196,19 @@
     }
 
     if (status == PredictionModelDownloadFileStatus::kVerifiedCrxWithNoFiles) {
-      base::FilePath invalid_crx_model = source_root_dir.AppendASCII("chrome")
-                                             .AppendASCII("test")
-                                             .AppendASCII("data")
-                                             .AppendASCII("optimization_guide")
-                                             .AppendASCII("invalid_model.crx3");
+      base::FilePath invalid_crx_model =
+          source_root_dir.AppendASCII("components")
+              .AppendASCII("test")
+              .AppendASCII("data")
+              .AppendASCII("optimization_guide")
+              .AppendASCII("invalid_model.crx3");
       ASSERT_TRUE(base::CopyFile(invalid_crx_model,
                                  GetFilePathForDownloadFileStatus(status)));
       return;
     }
 
-    base::FilePath zip_dir = temp_dir_.GetPath().AppendASCII("zip_dir");
+    base::FilePath zip_dir =
+        temp_download_dir_.GetPath().AppendASCII("zip_dir");
     ASSERT_TRUE(base::CreateDirectory(zip_dir));
     if (status ==
         PredictionModelDownloadFileStatus::kVerifiedCrxWithBadModelInfoFile) {
@@ -229,7 +239,8 @@
   }
 
   base::test::TaskEnvironment task_environment_;
-  base::ScopedTempDir temp_dir_;
+  base::ScopedTempDir temp_download_dir_;
+  base::ScopedTempDir temp_models_dir_;
   std::unique_ptr<download::test::MockDownloadService> mock_download_service_;
   std::unique_ptr<PredictionModelDownloadManager> download_manager_;
 };
diff --git a/chrome/browser/optimization_guide/prediction/prediction_model_download_observer.h b/components/optimization_guide/core/prediction_model_download_observer.h
similarity index 69%
rename from chrome/browser/optimization_guide/prediction/prediction_model_download_observer.h
rename to components/optimization_guide/core/prediction_model_download_observer.h
index b67a050..d3a8172 100644
--- a/chrome/browser/optimization_guide/prediction/prediction_model_download_observer.h
+++ b/components/optimization_guide/core/prediction_model_download_observer.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_OPTIMIZATION_GUIDE_PREDICTION_PREDICTION_MODEL_DOWNLOAD_OBSERVER_H_
-#define CHROME_BROWSER_OPTIMIZATION_GUIDE_PREDICTION_PREDICTION_MODEL_DOWNLOAD_OBSERVER_H_
+#ifndef COMPONENTS_OPTIMIZATION_GUIDE_CORE_PREDICTION_MODEL_DOWNLOAD_OBSERVER_H_
+#define COMPONENTS_OPTIMIZATION_GUIDE_CORE_PREDICTION_MODEL_DOWNLOAD_OBSERVER_H_
 
 #include "base/observer_list_types.h"
 #include "components/optimization_guide/proto/models.pb.h"
@@ -20,4 +20,4 @@
 
 }  // namespace optimization_guide
 
-#endif  // CHROME_BROWSER_OPTIMIZATION_GUIDE_PREDICTION_PREDICTION_MODEL_DOWNLOAD_OBSERVER_H_
+#endif  // COMPONENTS_OPTIMIZATION_GUIDE_CORE_PREDICTION_MODEL_DOWNLOAD_OBSERVER_H_
diff --git a/components/search/BUILD.gn b/components/search/BUILD.gn
index df271d3..ae69d747d 100644
--- a/components/search/BUILD.gn
+++ b/components/search/BUILD.gn
@@ -14,6 +14,7 @@
 
   deps = [
     "//base",
+    "//components/commerce/core:feature_list",
     "//components/google/core/common",
     "//components/history/core/browser",
     "//components/keyed_service/core",
diff --git a/components/search/DEPS b/components/search/DEPS
index 4186840..45c457a 100644
--- a/components/search/DEPS
+++ b/components/search/DEPS
@@ -1,6 +1,7 @@
 include_rules = [
   "+components/bookmarks/browser",
   "+components/bookmarks/test",
+  "+components/commerce",
   "+components/google/core",
   "+components/history/core",
   "+components/keyed_service/core",
diff --git a/components/search/ntp_features.cc b/components/search/ntp_features.cc
index 0b9d18a..cf3453107 100644
--- a/components/search/ntp_features.cc
+++ b/components/search/ntp_features.cc
@@ -10,6 +10,7 @@
 #include "base/strings/string_split.h"
 #include "base/time/time.h"
 #include "build/build_config.h"
+#include "components/commerce/core/commerce_feature_list.h"
 
 namespace ntp_features {
 
@@ -176,6 +177,79 @@
 const char kRealboxMatchSearchboxThemeParam[] =
     "RealboxMatchSearchboxThemeParam";
 
+// Params for Discount Consent V2 in the NTP Cart module.
+const char kNtpChromeCartModuleDiscountConsentNtpVariationParam[] =
+    "discount-consent-ntp-variation";
+const base::FeatureParam<int> kNtpChromeCartModuleDiscountConsentNtpVariation{
+    &commerce::kDiscountConsentV2,
+    kNtpChromeCartModuleDiscountConsentNtpVariationParam, 0};
+
+// String change variation params.
+const char kNtpChromeCartModuleDiscountConsentStringChangeContentParam[] =
+    "string-change-content";
+const base::FeatureParam<std::string>
+    kNtpChromeCartModuleDiscountConsentStringChangeContent{
+        &commerce::kDiscountConsentV2,
+        kNtpChromeCartModuleDiscountConsentStringChangeContentParam, ""};
+
+// Discount consent v2 step 1 params.
+const char
+    kNtpChromeCartModuleDiscountConsentNtpStepOneUseStaticContentParam[] =
+        "step-one-use-static-content";
+const base::FeatureParam<bool>
+    kNtpChromeCartModuleDiscountConsentNtpStepOneUseStaticContent{
+        &commerce::kDiscountConsentV2,
+        kNtpChromeCartModuleDiscountConsentNtpStepOneUseStaticContentParam,
+        true};
+const char kNtpChromeCartModuleDiscountConsentNtpStepOneStaticContentParam[] =
+    "step-one-static-content";
+const base::FeatureParam<std::string>
+    kNtpChromeCartModuleDiscountConsentNtpStepOneStaticContent{
+        &commerce::kDiscountConsentV2,
+        kNtpChromeCartModuleDiscountConsentNtpStepOneStaticContentParam, ""};
+const char kNtpChromeCartModuleDiscountConsentNtpStepOneContentOneCartParam[] =
+    "step-one-one-cart-content";
+const base::FeatureParam<std::string>
+    kNtpChromeCartModuleDiscountConsentNtpStepOneContentOneCart{
+        &commerce::kDiscountConsentV2,
+        kNtpChromeCartModuleDiscountConsentNtpStepOneContentOneCartParam, ""};
+const char kNtpChromeCartModuleDiscountConsentNtpStepOneContentTwoCartsParam[] =
+    "step-one-two-carts-content";
+const base::FeatureParam<std::string>
+    kNtpChromeCartModuleDiscountConsentNtpStepOneContentTwoCarts{
+        &commerce::kDiscountConsentV2,
+        kNtpChromeCartModuleDiscountConsentNtpStepOneContentTwoCartsParam, ""};
+const char
+    kNtpChromeCartModuleDiscountConsentNtpStepOneContentThreeCartsParam[] =
+        "step-one-three-carts-content";
+const base::FeatureParam<std::string>
+    kNtpChromeCartModuleDiscountConsentNtpStepOneContentThreeCarts{
+        &commerce::kDiscountConsentV2,
+        kNtpChromeCartModuleDiscountConsentNtpStepOneContentThreeCartsParam,
+        ""};
+
+// Discount consent v2 step 2 params.
+const char kNtpChromeCartModuleDiscountConsentNtpStepTwoContentParam[] =
+    "step-two-content";
+const base::FeatureParam<std::string>
+    kNtpChromeCartModuleDiscountConsentNtpStepTwoContent{
+        &commerce::kDiscountConsentV2,
+        kNtpChromeCartModuleDiscountConsentNtpStepTwoContentParam, ""};
+const char
+    kNtpChromeCartModuleDiscountConsentInlineStepTwoDifferentColorParam[] =
+        "step-two-different-color";
+const base::FeatureParam<bool>
+    kNtpChromeCartModuleDiscountConsentInlineStepTwoDifferentColor{
+        &commerce::kDiscountConsentV2,
+        kNtpChromeCartModuleDiscountConsentInlineStepTwoDifferentColorParam,
+        false};
+const char kNtpChromeCartModuleDiscountConsentNtpDialogContentTitleParam[] =
+    "dialog-content-title";
+const base::FeatureParam<std::string>
+    kNtpChromeCartModuleDiscountConsentNtpDialogContentTitle{
+        &commerce::kDiscountConsentV2,
+        kNtpChromeCartModuleDiscountConsentNtpDialogContentTitleParam, ""};
+
 base::TimeDelta GetModulesLoadTimeout() {
   std::string param_value = base::GetFieldTrialParamValueByFeature(
       kModules, kNtpModulesLoadTimeoutMillisecondsParam);
diff --git a/components/search/ntp_features.h b/components/search/ntp_features.h
index f1cbb9d..e3a1946 100644
--- a/components/search/ntp_features.h
+++ b/components/search/ntp_features.h
@@ -9,6 +9,7 @@
 #include <vector>
 
 #include "base/feature_list.h"
+#include "base/metrics/field_trial_params.h"
 
 namespace base {
 class TimeDelta;
@@ -109,6 +110,75 @@
 // Parameter determining the variations of searchbox theme matching.
 extern const char kRealboxMatchSearchboxThemeParam[];
 
+// The following are Feature params for Discount user consent v2.
+// This indicates the Discount Consent v2 variation on the NTP Cart module.
+enum class DiscountConsentNtpVariation {
+  kDefault = 0,
+  kStringChange = 1,
+  kInline = 2,
+  kDialog = 3,
+};
+
+// Param indicates the ConsentV2 variation. See DiscountConsentNtpVariation
+// enum.
+extern const char kNtpChromeCartModuleDiscountConsentNtpVariationParam[];
+extern const base::FeatureParam<int>
+    kNtpChromeCartModuleDiscountConsentNtpVariation;
+
+// String change variation params. This string is replacing the content string
+// of the v1 consent.
+extern const char kNtpChromeCartModuleDiscountConsentStringChangeContentParam[];
+extern const base::FeatureParam<std::string>
+    kNtpChromeCartModuleDiscountConsentStringChangeContent;
+
+// The following are discount consent step 1 params.
+// This indicates whether the content in step 1 is a static string that does not
+// contain any merchant names.
+extern const char
+    kNtpChromeCartModuleDiscountConsentNtpStepOneUseStaticContentParam[];
+extern const base::FeatureParam<bool>
+    kNtpChromeCartModuleDiscountConsentNtpStepOneUseStaticContent;
+// This the content string use in step 1 if
+// kNtpChromeCartModuleDiscountConsentNtpStepOneUseStaticContent.Get() is true.
+extern const char
+    kNtpChromeCartModuleDiscountConsentNtpStepOneStaticContentParam[];
+extern const base::FeatureParam<std::string>
+    kNtpChromeCartModuleDiscountConsentNtpStepOneStaticContent;
+// This is a string template that takes in one merchant name, and it's used when
+// there is only 1 Chrome Cart.
+extern const char
+    kNtpChromeCartModuleDiscountConsentNtpStepOneContentOneCartParam[];
+extern const base::FeatureParam<std::string>
+    kNtpChromeCartModuleDiscountConsentNtpStepOneContentOneCart;
+// This is a string template that takes in two merchant names, and it's used
+// when there are only 2 Chrome Carts.
+extern const char
+    kNtpChromeCartModuleDiscountConsentNtpStepOneContentTwoCartsParam[];
+extern const base::FeatureParam<std::string>
+    kNtpChromeCartModuleDiscountConsentNtpStepOneContentTwoCarts;
+// This is a string template that takes in two merchant names, and it's used
+// when there are 3 or more Chrome Carts.
+extern const char
+    kNtpChromeCartModuleDiscountConsentNtpStepOneContentThreeCartsParam[];
+extern const base::FeatureParam<std::string>
+    kNtpChromeCartModuleDiscountConsentNtpStepOneContentThreeCarts;
+
+// The following are discount consent step 2 params.
+// This is the content string used in step 2. This is the actual consent string.
+extern const char kNtpChromeCartModuleDiscountConsentNtpStepTwoContentParam[];
+extern const base::FeatureParam<std::string>
+    kNtpChromeCartModuleDiscountConsentNtpStepTwoContent;
+// This is used to indicate whether the backgound-color of step 2 should change.
+extern const char
+    kNtpChromeCartModuleDiscountConsentInlineStepTwoDifferentColorParam[];
+extern const base::FeatureParam<bool>
+    kNtpChromeCartModuleDiscountConsentInlineStepTwoDifferentColor;
+// This is the content title use in the dialog consent.
+extern const char
+    kNtpChromeCartModuleDiscountConsentNtpDialogContentTitleParam[];
+extern const base::FeatureParam<std::string>
+    kNtpChromeCartModuleDiscountConsentNtpDialogContentTitle;
+
 // Returns the timeout after which the load of a module should be aborted.
 base::TimeDelta GetModulesLoadTimeout();
 
diff --git a/components/segmentation_platform/internal/BUILD.gn b/components/segmentation_platform/internal/BUILD.gn
index 89c183c..7f6e41630 100644
--- a/components/segmentation_platform/internal/BUILD.gn
+++ b/components/segmentation_platform/internal/BUILD.gn
@@ -217,9 +217,18 @@
       "execution/model_execution_manager_impl_unittest.cc",
       "execution/segmentation_model_executor_unittest.cc",
     ]
+    deps += [ ":unit_tests_bundle_data" ]
   }
 }
 
+bundle_data("unit_tests_bundle_data") {
+  visibility = [ ":unit_tests" ]
+  testonly = true
+  sources = [ "//components/test/data/segmentation_platform/adder.tflite" ]
+  outputs = [ "{{bundle_resources_dir}}/" +
+              "{{source_root_relative_dir}}/{{source_file_part}}" ]
+}
+
 if (is_android) {
   android_library("internal_java") {
     visibility = [ "//chrome/android:chrome_all_java" ]
diff --git a/components/soda/BUILD.gn b/components/soda/BUILD.gn
index 1912b23..bc859e7 100644
--- a/components/soda/BUILD.gn
+++ b/components/soda/BUILD.gn
@@ -38,8 +38,7 @@
 
     deps += [
       "//ash/constants",
-      "//chromeos/dbus/dlcservice:dlcservice",
-      "//chromeos/dbus/dlcservice:dlcservice_proto",
+      "//chromeos/dbus/dlcservice",
       "//ui/base",
     ]
   }
diff --git a/components/soda/soda_installer_impl_chromeos.cc b/components/soda/soda_installer_impl_chromeos.cc
index c36bda0..7582987c 100644
--- a/components/soda/soda_installer_impl_chromeos.cc
+++ b/components/soda/soda_installer_impl_chromeos.cc
@@ -8,7 +8,6 @@
 #include "base/feature_list.h"
 #include "base/metrics/histogram_functions.h"
 #include "base/numerics/safe_conversions.h"
-#include "chromeos/dbus/dlcservice/dlcservice.pb.h"
 #include "chromeos/dbus/dlcservice/dlcservice_client.h"
 #include "components/live_caption/pref_names.h"
 #include "components/prefs/pref_service.h"
@@ -53,10 +52,8 @@
   soda_progress_ = 0.0;
 
   // Install SODA DLC.
-  dlcservice::InstallRequest install_request;
-  install_request.set_id(kSodaDlcName);
   chromeos::DlcserviceClient::Get()->Install(
-      install_request,
+      kSodaDlcName,
       base::BindOnce(&SodaInstallerImplChromeOS::OnSodaInstalled,
                      base::Unretained(this), base::Time::Now()),
       base::BindRepeating(&SodaInstallerImplChromeOS::OnSodaProgress,
@@ -81,10 +78,8 @@
 
   language_pack_progress_.insert({LanguageCode::kEnUs, 0.0});
 
-  dlcservice::InstallRequest install_request;
-  install_request.set_id(kSodaEnglishUsDlcName);
   chromeos::DlcserviceClient::Get()->Install(
-      install_request,
+      kSodaEnglishUsDlcName,
       base::BindOnce(&SodaInstallerImplChromeOS::OnLanguageInstalled,
                      base::Unretained(this), LanguageCode::kEnUs,
                      base::Time::Now()),
diff --git a/components/sync_bookmarks/bookmark_model_type_processor.cc b/components/sync_bookmarks/bookmark_model_type_processor.cc
index 09c5f16..0416ac5 100644
--- a/components/sync_bookmarks/bookmark_model_type_processor.cc
+++ b/components/sync_bookmarks/bookmark_model_type_processor.cc
@@ -192,12 +192,6 @@
     return;
   }
 
-  // Before applying incremental updates, run a quirk to mitigate some data
-  // corruption issue introduced by crbug.com/1231450.
-  for (syncer::UpdateResponseData& update : updates) {
-    MaybeFixGuidInSpecificsDueToPastBug(*bookmark_tracker_, &update.entity);
-  }
-
   // Incremental updates.
   ScopedRemoteUpdateBookmarks update_bookmarks(
       bookmark_model_, bookmark_undo_service_, bookmark_model_observer_.get());
diff --git a/components/sync_bookmarks/bookmark_specifics_conversions.cc b/components/sync_bookmarks/bookmark_specifics_conversions.cc
index cf417f2..4f7192c 100644
--- a/components/sync_bookmarks/bookmark_specifics_conversions.cc
+++ b/components/sync_bookmarks/bookmark_specifics_conversions.cc
@@ -27,8 +27,6 @@
 #include "components/sync/protocol/entity_data.h"
 #include "components/sync/protocol/entity_specifics.pb.h"
 #include "components/sync_bookmarks/switches.h"
-#include "components/sync_bookmarks/synced_bookmark_tracker.h"
-#include "components/sync_bookmarks/synced_bookmark_tracker_entity.h"
 #include "ui/gfx/favicon_size.h"
 #include "url/gurl.h"
 
@@ -556,46 +554,4 @@
                                          originator_client_item_id);
 }
 
-void MaybeFixGuidInSpecificsDueToPastBug(const SyncedBookmarkTracker& tracker,
-                                         syncer::EntityData* update_entity) {
-  DCHECK(update_entity);
-
-  // Permanent entities and tombstones have no GUID to fix.
-  if (!update_entity->server_defined_unique_tag.empty() ||
-      update_entity->is_deleted()) {
-    return;
-  }
-
-  // If the GUID in specifics is populated (inferred or otherwise), there's
-  // nothing to populate.
-  if (!update_entity->specifics.bookmark().guid().empty()) {
-    return;
-  }
-
-  // The bug that motivates this function (crbug.com/1231450) only affected
-  // bookmarks created with a client tag hash. Skip all other updates.
-  if (update_entity->client_tag_hash.value().empty()) {
-    return;
-  }
-
-  const SyncedBookmarkTrackerEntity* const tracked_entity =
-      tracker.GetEntityForSyncId(update_entity->id);
-  if (!tracked_entity || !tracked_entity->bookmark_node()) {
-    // The entity is not tracked locally or it has been deleted, so the GUID is
-    // unknown.
-    return;
-  }
-
-  // Reaching this point should guarantee that the local GUID is correct, but
-  // to double check, let's verify that client tag hash matches the GUID.
-  const base::GUID local_guid = tracked_entity->bookmark_node()->guid();
-  if (update_entity->client_tag_hash !=
-      SyncedBookmarkTracker::GetClientTagHashFromGUID(local_guid)) {
-    return;
-  }
-
-  update_entity->specifics.mutable_bookmark()->set_guid(
-      local_guid.AsLowercaseString());
-}
-
 }  // namespace sync_bookmarks
diff --git a/components/sync_bookmarks/bookmark_specifics_conversions.h b/components/sync_bookmarks/bookmark_specifics_conversions.h
index fd91b34..d42084b 100644
--- a/components/sync_bookmarks/bookmark_specifics_conversions.h
+++ b/components/sync_bookmarks/bookmark_specifics_conversions.h
@@ -36,8 +36,6 @@
 
 namespace sync_bookmarks {
 
-class SyncedBookmarkTracker;
-
 // Canonicalize |node_title| similar to legacy client's implementation by
 // truncating and the appending ' ' in some cases.
 std::string FullTitleToLegacyCanonicalizedTitle(const std::string& node_title);
@@ -104,12 +102,6 @@
                              const std::string& originator_cache_guid,
                              const std::string& originator_client_item_id);
 
-// Quirk to work around data corruption issues due to crbug.com/1231450. This
-// logic can likely be cleaned up after a few milestones and depending on UMA
-// metric Sync.BookmarkGUIDSource2. |update_entity| must not be null.
-void MaybeFixGuidInSpecificsDueToPastBug(const SyncedBookmarkTracker& tracker,
-                                         syncer::EntityData* update_entity);
-
 }  // namespace sync_bookmarks
 
 #endif  // COMPONENTS_SYNC_BOOKMARKS_BOOKMARK_SPECIFICS_CONVERSIONS_H_
diff --git a/components/sync_bookmarks/bookmark_specifics_conversions_unittest.cc b/components/sync_bookmarks/bookmark_specifics_conversions_unittest.cc
index 8f9640df..ff8c073 100644
--- a/components/sync_bookmarks/bookmark_specifics_conversions_unittest.cc
+++ b/components/sync_bookmarks/bookmark_specifics_conversions_unittest.cc
@@ -917,37 +917,6 @@
                                        /*originator_client_item_id=*/""));
 }
 
-TEST(BookmarkSpecificsConversionsTest, ShouldFixGuidInSpecificsDueToPastBug) {
-  auto tracker = SyncedBookmarkTracker::CreateEmpty(sync_pb::ModelTypeState());
-
-  const std::string kSyncId = "SYNC_ID";
-  const base::GUID kGuid = base::GUID::GenerateRandomV4();
-
-  sync_pb::EntitySpecifics specifics;
-  *specifics.mutable_bookmark()->mutable_unique_position() =
-      RandomUniquePosition();
-
-  bookmarks::BookmarkNode node(/*id=*/1, kGuid, GURL());
-  const SyncedBookmarkTrackerEntity* entity =
-      tracker->Add(&node, kSyncId, /*server_version=*/0,
-                   /*creation_time=*/base::Time(), specifics);
-  ASSERT_THAT(entity, NotNull());
-
-  // Mimic in incoming update with a client tag hash but not GUID in specifics.
-  syncer::EntityData update_entity;
-  update_entity.id = kSyncId;
-  update_entity.client_tag_hash =
-      SyncedBookmarkTracker::GetClientTagHashFromGUID(kGuid);
-  // Populate at least one field in specifics so it's not considered a
-  // tombstone.
-  update_entity.specifics.mutable_bookmark()->set_creation_time_us(1);
-
-  MaybeFixGuidInSpecificsDueToPastBug(*tracker, &update_entity);
-
-  EXPECT_THAT(update_entity.specifics.bookmark().guid(),
-              Eq(kGuid.AsLowercaseString()));
-}
-
 }  // namespace
 
 }  // namespace sync_bookmarks
diff --git a/chrome/test/data/optimization_guide/invalid_model.crx3 b/components/test/data/optimization_guide/invalid_model.crx3
similarity index 100%
rename from chrome/test/data/optimization_guide/invalid_model.crx3
rename to components/test/data/optimization_guide/invalid_model.crx3
Binary files differ
diff --git a/components/translate/core/language_detection/BUILD.gn b/components/translate/core/language_detection/BUILD.gn
index 7acda21..2669f176 100644
--- a/components/translate/core/language_detection/BUILD.gn
+++ b/components/translate/core/language_detection/BUILD.gn
@@ -74,4 +74,15 @@
     "//third_party/icu",
     "//url",
   ]
+  if (build_with_tflite_lib) {
+    deps += [ ":unit_tests_bundle_data" ]
+  }
+}
+
+bundle_data("unit_tests_bundle_data") {
+  visibility = [ ":unit_tests" ]
+  testonly = true
+  sources = [ "//components/test/data/translate/valid_model.tflite" ]
+  outputs = [ "{{bundle_resources_dir}}/" +
+              "{{source_root_relative_dir}}/{{source_file_part}}" ]
 }
diff --git a/content/browser/BUILD.gn b/content/browser/BUILD.gn
index 60c1258c..c393d78 100644
--- a/content/browser/BUILD.gn
+++ b/content/browser/BUILD.gn
@@ -3111,13 +3111,6 @@
       "profiling_utils.cc",
     ]
   }
-
-  if (is_chromeos_lacros) {
-    sources += [
-      "accessibility/browser_accessibility_state_impl_lacros.cc",
-      "accessibility/browser_accessibility_state_impl_lacros.h",
-    ]
-  }
 }
 
 if (is_android) {
diff --git a/content/browser/accessibility/browser_accessibility_state_impl.cc b/content/browser/accessibility/browser_accessibility_state_impl.cc
index 375cd67a..7e47abd4 100644
--- a/content/browser/accessibility/browser_accessibility_state_impl.cc
+++ b/content/browser/accessibility/browser_accessibility_state_impl.cc
@@ -14,7 +14,6 @@
 #include "base/no_destructor.h"
 #include "base/task/thread_pool.h"
 #include "build/build_config.h"
-#include "build/chromeos_buildflags.h"
 #include "content/browser/renderer_host/render_widget_host_impl.h"
 #include "content/browser/web_contents/web_contents_impl.h"
 #include "content/public/browser/browser_task_traits.h"
@@ -69,9 +68,8 @@
   return BrowserAccessibilityStateImpl::GetInstance();
 }
 
-// On Android, Mac, Lacros, and Windows there are platform-specific subclasses.
-#if !BUILDFLAG(IS_ANDROID) && !BUILDFLAG(IS_WIN) && !BUILDFLAG(IS_MAC) && \
-    !BUILDFLAG(IS_CHROMEOS_LACROS)
+// On Android, Mac, and Windows there are platform-specific subclasses.
+#if !BUILDFLAG(IS_ANDROID) && !BUILDFLAG(IS_WIN) && !BUILDFLAG(IS_MAC)
 // static
 BrowserAccessibilityStateImpl* BrowserAccessibilityStateImpl::GetInstance() {
   static base::NoDestructor<BrowserAccessibilityStateImpl> instance;
diff --git a/content/browser/accessibility/browser_accessibility_state_impl_lacros.cc b/content/browser/accessibility/browser_accessibility_state_impl_lacros.cc
deleted file mode 100644
index fdbcae7..0000000
--- a/content/browser/accessibility/browser_accessibility_state_impl_lacros.cc
+++ /dev/null
@@ -1,35 +0,0 @@
-// Copyright 2022 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "content/browser/accessibility/browser_accessibility_state_impl_lacros.h"
-
-#include "base/no_destructor.h"
-
-namespace content {
-
-BrowserAccessibilityStateImplLacros::BrowserAccessibilityStateImplLacros()
-    : crosapi_pref_observer_(
-          crosapi::mojom::PrefPath::kAccessibilitySpokenFeedbackEnabled,
-          base::BindRepeating(
-              &BrowserAccessibilityStateImplLacros::OnSpokenFeedbackPrefChanged,
-              base::Unretained(this))) {}
-
-BrowserAccessibilityStateImplLacros::~BrowserAccessibilityStateImplLacros() =
-    default;
-
-void BrowserAccessibilityStateImplLacros::OnSpokenFeedbackPrefChanged(
-    base::Value value) {
-  if (value.GetIfBool().value_or(false))
-    AddAccessibilityModeFlags(ui::AXMode::kScreenReader);
-  else
-    RemoveAccessibilityModeFlags(ui::AXMode::kScreenReader);
-}
-
-// static
-BrowserAccessibilityStateImpl* BrowserAccessibilityStateImpl::GetInstance() {
-  static base::NoDestructor<BrowserAccessibilityStateImplLacros> instance;
-  return &*instance;
-}
-
-}  // namespace content
diff --git a/content/browser/accessibility/browser_accessibility_state_impl_lacros.h b/content/browser/accessibility/browser_accessibility_state_impl_lacros.h
deleted file mode 100644
index 68c1588..0000000
--- a/content/browser/accessibility/browser_accessibility_state_impl_lacros.h
+++ /dev/null
@@ -1,33 +0,0 @@
-// Copyright 2022 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CONTENT_BROWSER_ACCESSIBILITY_BROWSER_ACCESSIBILITY_STATE_IMPL_LACROS_H_
-#define CONTENT_BROWSER_ACCESSIBILITY_BROWSER_ACCESSIBILITY_STATE_IMPL_LACROS_H_
-
-#include "base/values.h"
-#include "chromeos/lacros/crosapi_pref_observer.h"
-#include "content/browser/accessibility/browser_accessibility_state_impl.h"
-
-namespace content {
-
-class BrowserAccessibilityStateImplLacros
-    : public BrowserAccessibilityStateImpl {
- public:
-  BrowserAccessibilityStateImplLacros();
-  ~BrowserAccessibilityStateImplLacros() override;
-
-  BrowserAccessibilityStateImplLacros(
-      const BrowserAccessibilityStateImplLacros&) = delete;
-  BrowserAccessibilityStateImplLacros& operator=(
-      const BrowserAccessibilityStateImplLacros&) = delete;
-
- private:
-  void OnSpokenFeedbackPrefChanged(base::Value value);
-
-  CrosapiPrefObserver crosapi_pref_observer_;
-};
-
-}  // namespace content
-
-#endif  // CONTENT_BROWSER_ACCESSIBILITY_BROWSER_ACCESSIBILITY_STATE_IMPL_LACROS_H_
diff --git a/content/browser/devtools/protocol/page_handler.cc b/content/browser/devtools/protocol/page_handler.cc
index 75ec2991..1232949 100644
--- a/content/browser/devtools/protocol/page_handler.cc
+++ b/content/browser/devtools/protocol/page_handler.cc
@@ -1792,13 +1792,16 @@
     } else if (reason == BackForwardCacheMetrics::NotRestoredReason::
                              kDisableForRenderFrameHostCalled) {
       for (auto disabled_reason : disabled_reasons) {
-        reasons->emplace_back(
+        auto reason =
             Page::BackForwardCacheNotRestoredExplanation::Create()
                 .SetType(
                     MapDisableForRenderFrameHostReasonToType(disabled_reason))
                 .SetReason(
                     DisableForRenderFrameHostReasonToProtocol(disabled_reason))
-                .Build());
+                .Build();
+        if (!disabled_reason.context.empty())
+          reason->SetContext(disabled_reason.context);
+        reasons->emplace_back(std::move(reason));
       }
     } else {
       reasons->emplace_back(
diff --git a/content/browser/file_system_access/file_system_access_access_handle_host_impl.h b/content/browser/file_system_access/file_system_access_access_handle_host_impl.h
index 70b6895..4d1ab34 100644
--- a/content/browser/file_system_access/file_system_access_access_handle_host_impl.h
+++ b/content/browser/file_system_access/file_system_access_access_handle_host_impl.h
@@ -24,7 +24,7 @@
 class FileSystemAccessAccessHandleHostImpl
     : public blink::mojom::FileSystemAccessAccessHandleHost {
  public:
-  // Crates an AccessHandleHost that acts as an exclusive write lock on the
+  // Creates an AccessHandleHost that acts as an exclusive write lock on the
   // file. AccessHandleHosts should only be created via the
   // FileSystemAccessManagerImpl.
   FileSystemAccessAccessHandleHostImpl(
diff --git a/content/browser/file_system_access/file_system_access_file_delegate_host_impl.h b/content/browser/file_system_access/file_system_access_file_delegate_host_impl.h
index f95ec668..86eab734 100644
--- a/content/browser/file_system_access/file_system_access_file_delegate_host_impl.h
+++ b/content/browser/file_system_access/file_system_access_file_delegate_host_impl.h
@@ -15,7 +15,8 @@
 namespace content {
 
 // Browser side implementation of the FileSystemAccessFileDelegateHost mojom
-// interface. Instances of this class are owned by the
+// interface, which facilitates file operations for Access Handles in incognito
+// mode. Instances of this class are owned by the
 // FileSystemAccessAccessHandleHostImpl instance of the associated URL, which
 // constructs it.
 class FileSystemAccessFileDelegateHostImpl
diff --git a/content/browser/file_system_access/file_system_access_file_handle_impl.cc b/content/browser/file_system_access/file_system_access_file_handle_impl.cc
index b2d08384..b8097b8 100644
--- a/content/browser/file_system_access/file_system_access_file_handle_impl.cc
+++ b/content/browser/file_system_access/file_system_access_file_handle_impl.cc
@@ -210,7 +210,7 @@
     std::move(callback).Run(
         file_system_access_error::FromStatus(
             FileSystemAccessStatus::kInvalidState,
-            "Access handles may only be created on temporary file systems"),
+            "Access Handles may only be created on temporary file systems"),
         blink::mojom::FileSystemAccessAccessHandleFilePtr(),
         mojo::NullRemote());
     return;
diff --git a/content/browser/file_system_access/file_system_access_manager_impl.cc b/content/browser/file_system_access/file_system_access_manager_impl.cc
index b265ba0..93b2e46 100644
--- a/content/browser/file_system_access/file_system_access_manager_impl.cc
+++ b/content/browser/file_system_access/file_system_access_manager_impl.cc
@@ -1462,7 +1462,7 @@
 
   int64_t overallocation = allocated_file_size - file_info.size;
   DCHECK_GE(overallocation, 0)
-      << "An AccessHandle should not use more capacity than allocated.";
+      << "An Access Handle should not use more capacity than allocated.";
 
   context_->quota_manager_proxy()->NotifyStorageModified(
       storage::QuotaClientType::kFileSystem, url.storage_key(),
diff --git a/content/browser/interest_group/auction_runner.cc b/content/browser/interest_group/auction_runner.cc
index 32cb22c5..63339ba 100644
--- a/content/browser/interest_group/auction_runner.cc
+++ b/content/browser/interest_group/auction_runner.cc
@@ -40,7 +40,7 @@
 
 namespace {
 
-constexpr base::TimeDelta kMaxPerBuyerTimeout = base::Milliseconds(500);
+constexpr base::TimeDelta kMaxTimeout = base::Milliseconds(500);
 
 // All URLs received from worklets must be valid HTTPS URLs. It's up to callers
 // to call ReportBadMessage() on invalid URLs.
@@ -738,7 +738,7 @@
       config_->auction_ad_config_non_shared_params.Clone(),
       bid_raw->interest_group->owner, bid_raw->render_url,
       bid_raw->ad_components ? *bid_raw->ad_components : std::vector<GURL>(),
-      bid_raw->bid_duration.InMilliseconds(),
+      bid_raw->bid_duration.InMilliseconds(), SellerTimeout(),
       base::BindOnce(&Auction::OnBidScored, weak_ptr_factory_.GetWeakPtr(),
                      std::move(bid)));
 
@@ -839,16 +839,23 @@
   if (per_buyer_timeouts.has_value()) {
     auto it =
         per_buyer_timeouts.value().find(state->bidder.interest_group.owner);
-    if (it != per_buyer_timeouts.value().end()) {
-      // Any per buyer timeout higher than kMaxPerBuyerTimeout ms will be
-      // clamped to kMaxPerBuyerTimeout ms.
-      return std::min(it->second, kMaxPerBuyerTimeout);
-    }
+    if (it != per_buyer_timeouts.value().end())
+      return std::min(it->second, kMaxTimeout);
   }
   const auto& all_buyers_timeout =
       config_->auction_ad_config_non_shared_params->all_buyers_timeout;
   if (all_buyers_timeout.has_value())
-    return std::min(all_buyers_timeout.value(), kMaxPerBuyerTimeout);
+    return std::min(all_buyers_timeout.value(), kMaxTimeout);
+  return absl::nullopt;
+}
+
+absl::optional<base::TimeDelta> AuctionRunner::Auction::SellerTimeout() {
+  if (config_->auction_ad_config_non_shared_params->seller_timeout
+          .has_value()) {
+    return std::min(
+        config_->auction_ad_config_non_shared_params->seller_timeout.value(),
+        kMaxTimeout);
+  }
   return absl::nullopt;
 }
 
diff --git a/content/browser/interest_group/auction_runner.h b/content/browser/interest_group/auction_runner.h
index 2744ad1..ce924873 100644
--- a/content/browser/interest_group/auction_runner.h
+++ b/content/browser/interest_group/auction_runner.h
@@ -502,6 +502,7 @@
 
     absl::optional<std::string> PerBuyerSignals(const BidState* state);
     absl::optional<base::TimeDelta> PerBuyerTimeout(const BidState* state);
+    absl::optional<base::TimeDelta> SellerTimeout();
 
     // If there are no `outstanding_bids_`, completes the bidding and scoring
     // phase.
diff --git a/content/browser/interest_group/auction_runner_unittest.cc b/content/browser/interest_group/auction_runner_unittest.cc
index c651281..95af3f8 100644
--- a/content/browser/interest_group/auction_runner_unittest.cc
+++ b/content/browser/interest_group/auction_runner_unittest.cc
@@ -293,6 +293,8 @@
       }
       if (auctionConfig.sellerSignals["url"] != decisionLogicUrl)
         throw new Error("Wrong sellerSignals");
+      if (typeof auctionConfig.sellerTimeout !== "number")
+        throw new Error("auctionConfig.sellerTimeout is not a number. huh");
       if (browserSignals.topWindowHostname !== 'publisher1.com')
         throw new Error("wrong topWindowHostname");
       if ("joinCount" in browserSignals)
@@ -464,7 +466,7 @@
     // per_buyer_timeout passed to GenerateBid() should not be empty, because
     // auction_config's all_buyers_timeout (which is the key of '*' in
     // perBuyerTimeouts) is set in the AuctionRunnerTest.
-    EXPECT_TRUE(per_buyer_timeout.has_value());
+    ASSERT_TRUE(per_buyer_timeout.has_value());
     if (bidder_worklet_non_shared_params->name == kBidder1Name) {
       // Any per buyer timeout in auction_config higher than 500 ms should be
       // clamped to 500 ms by the AuctionRunner before passed to GenerateBid(),
@@ -639,11 +641,18 @@
                const GURL& browser_signal_render_url,
                const std::vector<GURL>& browser_signal_ad_components,
                uint32_t browser_signal_bidding_duration_msecs,
+               const absl::optional<base::TimeDelta> seller_timeout,
                ScoreAdCallback score_ad_callback) override {
     // SendPendingSignalsRequests() should only be called once all ads are
     // scored.
     EXPECT_FALSE(send_pending_signals_requests_called_);
 
+    ASSERT_TRUE(seller_timeout.has_value());
+    // seller_timeout in auction_config higher than 500 ms should be clamped to
+    // 500 ms by the AuctionRunner before passed to ScoreAd(), and
+    // auction_config's seller_timeout is 1000 ms so it should be 500 ms here.
+    EXPECT_EQ(seller_timeout.value(), base::Milliseconds(500));
+
     ScoreAdParams score_ad_params;
     score_ad_params.callback = std::move(score_ad_callback);
     score_ad_params.bid = bid;
@@ -1060,6 +1069,8 @@
     auction_config->auction_ad_config_non_shared_params->seller_signals =
         base::StringPrintf(R"({"url": "%s"})",
                            seller_decision_logic_url.spec().c_str());
+    auction_config->auction_ad_config_non_shared_params->seller_timeout =
+        base::Milliseconds(1000);
 
     base::flat_map<url::Origin, std::string> per_buyer_signals;
     // Use a combination of bidder and seller values, so can make sure bidders
diff --git a/content/browser/interest_group/auction_worklet_manager_unittest.cc b/content/browser/interest_group/auction_worklet_manager_unittest.cc
index b4b6ad3..c616afc 100644
--- a/content/browser/interest_group/auction_worklet_manager_unittest.cc
+++ b/content/browser/interest_group/auction_worklet_manager_unittest.cc
@@ -263,6 +263,7 @@
                const GURL& browser_signal_render_url,
                const std::vector<GURL>& browser_signal_ad_components,
                uint32_t browser_signal_bidding_duration_msecs,
+               const absl::optional<base::TimeDelta> seller_timeout,
                ScoreAdCallback score_ad_callback) override {
     NOTREACHED();
   }
diff --git a/content/browser/interest_group/interest_group_browsertest.cc b/content/browser/interest_group/interest_group_browsertest.cc
index db8b14007..12d7573 100644
--- a/content/browser/interest_group/interest_group_browsertest.cc
+++ b/content/browser/interest_group/interest_group_browsertest.cc
@@ -2077,6 +2077,7 @@
     interestGroupBuyers: [$1],
     auctionSignals: {x: 1},
     sellerSignals: {yet: 'more', info: 1},
+    sellerTimeout: 200,
     perBuyerSignals: {$1: {even: 'more', x: 4.5}},
     perBuyerTimeouts: {$1: 100, '*': 150}
                 })",
@@ -3548,6 +3549,7 @@
     interestGroupBuyers: [$4, $5],
     auctionSignals: {so: 'I', hear: ['you', 'like', 'json']},
     sellerSignals: {signals: 'from', the: ['seller']},
+    sellerTimeout: 200,
     perBuyerSignals: {$4: {signalsForBuyer: 1}, $5: {signalsForBuyer: 2}},
     perBuyerTimeouts: {$4: 110, $5: 120, '*': 150}
   });
@@ -3626,6 +3628,7 @@
     trustedScoringSignalsUrl: $3,
     auctionSignals: ["top-level auction signals"],
     sellerSignals: ["top-level seller signals"],
+    sellerTimeout: 300,
     perBuyerSignals: {$7: ["top-level buyer signals"]},
     perBuyerTimeouts: {$7: 110, '*': 150},
     componentAuctions: [{
@@ -3635,6 +3638,7 @@
       interestGroupBuyers: [$7],
       auctionSignals: ["component auction signals"],
       sellerSignals: ["component seller signals"],
+      sellerTimeout: 200,
       perBuyerSignals: {$7: ["component buyer signals"]},
       perBuyerTimeouts: {$7: 200},
     }],
@@ -4031,7 +4035,7 @@
 // Bidders' generateBid() scripts that run forever should timeout. They will not
 // affect other bidders or fail the auction.
 IN_PROC_BROWSER_TEST_F(InterestGroupBrowserTest,
-                       RunAdAuctionWithPerBuyerTimeouts) {
+                       RunAdAuctionWithCustomPerBuyerTimeouts) {
   const char kHostA[] = "a.test";
   const char kHostB[] = "b.test";
   // Navigate to other bidder site, and add an interest group.
@@ -4095,6 +4099,36 @@
   }
 }
 
+IN_PROC_BROWSER_TEST_F(InterestGroupBrowserTest,
+                       RunAdAuctionWithCustomSellerTimeout) {
+  const char kHostA[] = "a.test";
+  GURL test_url = https_server_->GetURL(kHostA, "/page_with_iframe.html");
+  ASSERT_TRUE(NavigateToURL(shell(), test_url));
+  url::Origin test_origin = url::Origin::Create(test_url);
+  GURL ad_url = https_server_->GetURL(kHostA, "/echo?render_cars");
+
+  EXPECT_TRUE(JoinInterestGroupAndWaitInJs(
+      /*owner=*/test_origin,
+      /*name=*/"cars",
+      /*bidding_url=*/
+      https_server_->GetURL(kHostA, "/interest_group/bidding_logic.js"),
+      /*ads=*/{{{ad_url, /*metadata=*/absl::nullopt}}}));
+
+  // The auction fails, since seller's scoreAd() script times out after 1 ms.
+  EXPECT_EQ(
+      nullptr,
+      RunAuctionAndWait(JsReplace(
+          R"({
+    seller: $1,
+    decisionLogicUrl: $2,
+    interestGroupBuyers: [$1],
+    sellerTimeout: 1,
+                })",
+          test_origin,
+          https_server_->GetURL(
+              "a.test", "/interest_group/decision_logic_loop_forever.js"))));
+}
+
 // This test exercises the interest group and ad auction services directly,
 // rather than via Blink, to ensure that those services running in the browser
 // implement important security checks (Blink may also perform its own
diff --git a/content/browser/renderer_host/back_forward_cache_can_store_document_result.cc b/content/browser/renderer_host/back_forward_cache_can_store_document_result.cc
index 4827e8f..ab9089f 100644
--- a/content/browser/renderer_host/back_forward_cache_can_store_document_result.cc
+++ b/content/browser/renderer_host/back_forward_cache_can_store_document_result.cc
@@ -238,8 +238,9 @@
     const std::set<BackForwardCache::DisabledReason>& reasons) {
   std::vector<std::string> descriptions;
   for (const auto& reason : reasons) {
-    descriptions.push_back(base::StringPrintf(
-        "%d:%d:%s", reason.source, reason.id, reason.description.c_str()));
+    descriptions.push_back(
+        base::StringPrintf("%d:%d:%s:%s", reason.source, reason.id,
+                           reason.description.c_str(), reason.context.c_str()));
   }
   return base::JoinString(descriptions, ", ");
 }
diff --git a/content/browser/renderer_host/render_frame_host_delegate.cc b/content/browser/renderer_host/render_frame_host_delegate.cc
index b8ab1bd6..00a32dae 100644
--- a/content/browser/renderer_host/render_frame_host_delegate.cc
+++ b/content/browser/renderer_host/render_frame_host_delegate.cc
@@ -89,8 +89,7 @@
     blink::mojom::FullscreenOptionsPtr options) {}
 
 bool RenderFrameHostDelegate::ShouldRouteMessageEvent(
-    RenderFrameHostImpl* target_rfh,
-    SiteInstance* source_site_instance) const {
+    RenderFrameHostImpl* target_rfh) const {
   return false;
 }
 
diff --git a/content/browser/renderer_host/render_frame_host_delegate.h b/content/browser/renderer_host/render_frame_host_delegate.h
index a970151..caafe39 100644
--- a/content/browser/renderer_host/render_frame_host_delegate.h
+++ b/content/browser/renderer_host/render_frame_host_delegate.h
@@ -325,9 +325,7 @@
   // Let the delegate decide whether postMessage should be delivered to
   // |target_rfh| from a source frame in the given SiteInstance.  This defaults
   // to false and overrides the RenderFrameHost's decision if true.
-  virtual bool ShouldRouteMessageEvent(
-      RenderFrameHostImpl* target_rfh,
-      SiteInstance* source_site_instance) const;
+  virtual bool ShouldRouteMessageEvent(RenderFrameHostImpl* target_rfh) const;
 
   // Ensure that |source_rfh| has swapped-out RenderViews and
   // RenderFrameProxies for itself and for all frames on its opener chain in
diff --git a/content/browser/renderer_host/render_frame_proxy_host.cc b/content/browser/renderer_host/render_frame_proxy_host.cc
index f1bc9e0..578a9ac 100644
--- a/content/browser/renderer_host/render_frame_proxy_host.cc
+++ b/content/browser/renderer_host/render_frame_proxy_host.cc
@@ -533,8 +533,7 @@
   // performed here once OOPIF support in <webview> is further along.
   SiteInstanceImpl* target_site_instance = target_rfh->GetSiteInstance();
   if (!target_site_instance->IsRelatedSiteInstance(GetSiteInstance()) &&
-      !target_rfh->delegate()->ShouldRouteMessageEvent(target_rfh,
-                                                       GetSiteInstance())) {
+      !target_rfh->delegate()->ShouldRouteMessageEvent(target_rfh)) {
     return;
   }
 
diff --git a/content/browser/renderer_host/render_widget_host_view_mac.mm b/content/browser/renderer_host/render_widget_host_view_mac.mm
index 09ada3b..a0112bd 100644
--- a/content/browser/renderer_host/render_widget_host_view_mac.mm
+++ b/content/browser/renderer_host/render_widget_host_view_mac.mm
@@ -1022,6 +1022,10 @@
 gfx::Rect RenderWidgetHostViewMac::GetFirstRectForCompositionRange(
     const gfx::Range& range,
     gfx::Range* actual_range) {
+  TRACE_EVENT1("ime",
+               "RenderWidgetHostViewMac::GetFirstRectForCompositionRange",
+               "range", range.ToString());
+
   const TextInputManager::CompositionRangeInfo* composition_info =
       GetCompositionRangeInfo();
   if (!composition_info)
@@ -1098,8 +1102,9 @@
 
   DCHECK(rect);
   // This exists to make IMEs more responsive, see http://crbug.com/115920
-  TRACE_EVENT0("browser",
-               "RenderWidgetHostViewMac::GetFirstRectForCharacterRange");
+  TRACE_EVENT1("ime",
+               "RenderWidgetHostViewMac::GetCachedFirstRectForCharacterRange",
+               "requested range", requested_range.ToString());
 
   const TextInputManager::TextSelection* selection = GetTextSelection();
   if (!selection)
@@ -1117,6 +1122,10 @@
         GetTextInputManager()->GetTextSelectionBounds();
     if (text_selection_bound) {
       *rect = text_selection_bound.value();
+      TRACE_EVENT1(
+          "ime",
+          "RenderWidgetHostViewMac::GetCachedFirstRectForCharacterRange",
+          "GetTextSelectionBounds", rect->ToString());
       return true;
     }
 
@@ -1124,6 +1133,9 @@
     *rect = GetTextInputManager()
                 ->GetSelectionRegion(GetFocusedWidget()->GetView())
                 ->caret_rect;
+    TRACE_EVENT1(
+        "ime", "RenderWidgetHostViewMac::GetCachedFirstRectForCharacterRange",
+        "caret_rect", rect->ToString());
     return true;
   }
 
@@ -1138,6 +1150,9 @@
     *rect = GetTextInputManager()
                 ->GetSelectionRegion(GetFocusedWidget()->GetView())
                 ->first_selection_rect;
+    TRACE_EVENT1(
+        "ime", "RenderWidgetHostViewMac::GetCachedFirstRectForCharacterRange",
+        "first_selection_rect", rect->ToString());
     return true;
   }
 
@@ -1157,6 +1172,11 @@
   gfx::Range ui_actual_range;
   *rect = GetFirstRectForCompositionRange(request_range_in_composition,
                                           &ui_actual_range);
+
+  TRACE_EVENT1("ime",
+               "RenderWidgetHostViewMac::GetCachedFirstRectForCharacterRange",
+               "GetFirstRectForCompositionRange", rect->ToString());
+
   if (actual_range) {
     *actual_range =
         gfx::Range(composition_info->range.start() + ui_actual_range.start(),
@@ -1922,6 +1942,9 @@
     gfx::Rect* rect,
     gfx::Range* actual_range,
     bool* success) {
+  TRACE_EVENT1("ime", "RenderWidgetHostViewMac::SyncGetFirstRectForRange",
+               "requested range", requested_range.ToString());
+
   *actual_range = requested_range;
   if (!GetFocusedWidget()) {
     *success = false;
diff --git a/content/browser/web_contents/web_contents_impl.cc b/content/browser/web_contents/web_contents_impl.cc
index 7d20301d..e2c036f 100644
--- a/content/browser/web_contents/web_contents_impl.cc
+++ b/content/browser/web_contents/web_contents_impl.cc
@@ -7593,11 +7593,9 @@
 }
 
 bool WebContentsImpl::ShouldRouteMessageEvent(
-    RenderFrameHostImpl* target_rfh,
-    SiteInstance* source_site_instance) const {
-  OPTIONAL_TRACE_EVENT2("content", "WebContentsImpl::ShouldRouteMessageEvent",
-                        "render_frame_host", target_rfh, "source_site_instance",
-                        source_site_instance);
+    RenderFrameHostImpl* target_rfh) const {
+  OPTIONAL_TRACE_EVENT1("content", "WebContentsImpl::ShouldRouteMessageEvent",
+                        "render_frame_host", target_rfh);
   // Allow the message if this WebContents is dedicated to a browser plugin
   // guest.
   // Note: This check means that an embedder could theoretically receive a
diff --git a/content/browser/web_contents/web_contents_impl.h b/content/browser/web_contents/web_contents_impl.h
index 5020c66..cbfb336 100644
--- a/content/browser/web_contents/web_contents_impl.h
+++ b/content/browser/web_contents/web_contents_impl.h
@@ -661,9 +661,7 @@
       RenderFrameHostImpl* rfh,
       bool is_fullscreen,
       blink::mojom::FullscreenOptionsPtr options) override;
-  bool ShouldRouteMessageEvent(
-      RenderFrameHostImpl* target_rfh,
-      SiteInstance* source_site_instance) const override;
+  bool ShouldRouteMessageEvent(RenderFrameHostImpl* target_rfh) const override;
   void EnsureOpenerProxiesExist(RenderFrameHostImpl* source_rfh) override;
   std::unique_ptr<WebUIImpl> CreateWebUIForRenderFrameHost(
       RenderFrameHostImpl* frame_host,
diff --git a/content/browser/web_package/web_bundle_utils.cc b/content/browser/web_package/web_bundle_utils.cc
index 53ead15..7ffe49aa3 100644
--- a/content/browser/web_package/web_bundle_utils.cc
+++ b/content/browser/web_package/web_bundle_utils.cc
@@ -113,21 +113,19 @@
 
 GURL GetSynthesizedUrlForWebBundle(const GURL& web_bundle_file_url,
                                    const GURL& url_in_bundles) {
-  url::Replacements<char> replacements;
+  GURL::Replacements replacements;
 
-  url::Replacements<char> clear_ref;
+  GURL::Replacements clear_ref;
   clear_ref.ClearRef();
   std::string query_string = url_in_bundles.ReplaceComponents(clear_ref).spec();
-  url::Component new_query(0, query_string.size());
-  replacements.SetQuery(query_string.c_str(), new_query);
+  replacements.SetQueryStr(query_string);
 
   if (!url_in_bundles.has_ref()) {
     replacements.ClearRef();
     return web_bundle_file_url.ReplaceComponents(replacements);
   }
-  url::Component new_ref(0, url_in_bundles.ref().size());
   std::string ref_string = url_in_bundles.ref();
-  replacements.SetRef(ref_string.c_str(), new_ref);
+  replacements.SetRefStr(ref_string);
   return web_bundle_file_url.ReplaceComponents(replacements);
 }
 
diff --git a/content/public/browser/back_forward_cache.h b/content/public/browser/back_forward_cache.h
index 655dbba..f4faa390 100644
--- a/content/public/browser/back_forward_cache.h
+++ b/content/public/browser/back_forward_cache.h
@@ -51,13 +51,16 @@
   typedef uint16_t DisabledReasonType;
   static const uint16_t kDisabledReasonTypeBits = 16;
 
-  // Represents a reason to disable back-forward cache, given by a source. It
-  // preserves the string that accompanied it, however the string is ignored for
-  // <, == and !=.
+  // Represents a reason to disable back-forward cache, given by a |source|.
+  // |context| is arbitrary context that will be preserved and passed through,
+  // e.g. an extension ID responsible for disabling BFCache that can be shown in
+  // passed devtools. It preserves the |description| and |context| that
+  // accompany it, however they are ignored for <, == and !=.
   struct CONTENT_EXPORT DisabledReason {
     const BackForwardCache::DisabledSource source;
     const BackForwardCache::DisabledReasonType id;
     const std::string description;
+    const std::string context;
 
     bool operator<(const DisabledReason&) const;
     bool operator==(const DisabledReason&) const;
diff --git a/content/services/auction_worklet/bidder_worklet_unittest.cc b/content/services/auction_worklet/bidder_worklet_unittest.cc
index cd783e7..5df3e20 100644
--- a/content/services/auction_worklet/bidder_worklet_unittest.cc
+++ b/content/services/auction_worklet/bidder_worklet_unittest.cc
@@ -167,7 +167,7 @@
   }
 
   // Configures `url_loader_factory_` to return a generateBid() script with the
-  // specified return line Then runs the script, expecting the provided result.
+  // specified return line. Then runs the script, expecting the provided result.
   void RunGenerateBidWithReturnValueExpectingResult(
       const std::string& raw_return_value,
       mojom::BidderWorkletBidPtr expected_bid,
@@ -184,7 +184,7 @@
   }
 
   // Configures `url_loader_factory_` to return a script with the specified
-  // Javascript Then runs the script, expecting the provided result.
+  // Javascript. Then runs the script, expecting the provided result.
   void RunGenerateBidWithJavascriptExpectingResult(
       const std::string& javascript,
       mojom::BidderWorkletBidPtr expected_bid,
@@ -2062,8 +2062,10 @@
             v8_helper->set_script_timeout_for_testing(script_timeout);
           },
           v8_helper_, kScriptTimeout));
-  per_buyer_timeout_ = base::Milliseconds(20);
+  // Make sure set_script_timeout_for_testing is called.
+  task_environment_.RunUntilIdle();
 
+  per_buyer_timeout_ = base::Milliseconds(20);
   RunGenerateBidWithJavascriptExpectingResult(
       CreateGenerateBidScript(/*raw_return_value=*/"", R"(while (1))"),
       /*expected_bid=*/mojom::BidderWorkletBidPtr(),
diff --git a/content/services/auction_worklet/public/mojom/bidder_worklet.mojom b/content/services/auction_worklet/public/mojom/bidder_worklet.mojom
index 8a85d81..bb547e5 100644
--- a/content/services/auction_worklet/public/mojom/bidder_worklet.mojom
+++ b/content/services/auction_worklet/public/mojom/bidder_worklet.mojom
@@ -111,9 +111,10 @@
   //  publisher page and provided to all interest groups with the same owner
   //  as the one specified `interest_group`.
   //
-  // `per_buyer_timeout` Restrict the runtime (in milliseconds) of particular
-  //  buyer's bidding scripts. Null if not provided by the publisher page. Null
-  //  will be passed to the worklet in that case.
+  // `per_buyer_timeout` Restrict the runtime of particular buyer's bidding
+  //  scripts. Any timeout higher than 500 ms will be clamped to 500 ms before
+  //  passing in as `per_buyer_timeout`. Null if not provided by the publisher
+  //  page. Null will be passed to the worklet in that case.
   //
   // `browser_signal_seller_origin` The origin of the seller script running
   //  the auction. Typically a valid, non-opaque HTTPS origin.
diff --git a/content/services/auction_worklet/public/mojom/seller_worklet.mojom b/content/services/auction_worklet/public/mojom/seller_worklet.mojom
index 9b60b94..04f8246c 100644
--- a/content/services/auction_worklet/public/mojom/seller_worklet.mojom
+++ b/content/services/auction_worklet/public/mojom/seller_worklet.mojom
@@ -50,6 +50,11 @@
   //  took to generate the bid. Taken as milliseconds to reduce granularity of
   //  timing information passed to an untrusted process.
   //
+  // `seller_timeout` Restrict the runtime of the seller's scoring script. Any
+  //  timeout higher than 500 ms will be clamped to 500 ms before passing in as
+  //  `seller_timeout`. Null if not provided by the publisher page. Null will be
+  //  passed to the worklet in that case.
+  //
   // Returns:
   // `score` Non-negative score the SellerWorklet assigns to the bid. A value
   //  of 0 indicates either an error running the script, or that the script
@@ -85,7 +90,8 @@
           url.mojom.Origin browser_signal_interest_group_owner,
           url.mojom.Url browser_signal_render_url,
           array<url.mojom.Url> browser_signal_ad_component_render_urls,
-          uint32 browser_signal_bidding_duration_msecs) =>
+          uint32 browser_signal_bidding_duration_msecs,
+          mojo_base.mojom.TimeDelta? seller_timeout) =>
               (double score,
               uint32 scoring_signals_data_version,
               bool has_scoring_signals_data_version,
diff --git a/content/services/auction_worklet/seller_worklet.cc b/content/services/auction_worklet/seller_worklet.cc
index 3d160e6..d414d43 100644
--- a/content/services/auction_worklet/seller_worklet.cc
+++ b/content/services/auction_worklet/seller_worklet.cc
@@ -51,7 +51,7 @@
 //  'decisionLogicUrl': 'https://www.example-ssp.com/seller.js',
 //  'trustedScoringSignalsUrl': ...,
 //  'interestGroupBuyers': ['https://www.example-dsp.com', 'https://buyer2.com',
-//  ...], 'auctionSignals': {...}, 'sellerSignals': {...},
+//  ...], 'auctionSignals': {...}, 'sellerSignals': {...}, 'sellerTimeout': 100,
 //  'perBuyerSignals': {'https://www.example-dsp.com': {...},
 //                      'https://www.another-buyer.com': {...},
 //                       ...},
@@ -103,6 +103,16 @@
     return false;
   }
 
+  if (auction_ad_config_non_shared_params.seller_timeout.has_value() &&
+      !v8_helper->InsertJsonValue(
+          context, "sellerTimeout",
+          base::NumberToString(
+              auction_ad_config_non_shared_params.seller_timeout.value()
+                  .InMilliseconds()),
+          auction_config_value)) {
+    return false;
+  }
+
   if (auction_ad_config_non_shared_params.per_buyer_signals.has_value()) {
     v8::Local<v8::Object> per_buyer_value = v8::Object::New(isolate);
     for (const auto& kv :
@@ -204,6 +214,7 @@
     const GURL& browser_signal_render_url,
     const std::vector<GURL>& browser_signal_ad_components,
     uint32_t browser_signal_bidding_duration_msecs,
+    const absl::optional<base::TimeDelta> seller_timeout,
     ScoreAdCallback callback) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(user_sequence_checker_);
 
@@ -222,6 +233,7 @@
   }
   score_ad_task->browser_signal_bidding_duration_msecs =
       browser_signal_bidding_duration_msecs;
+  score_ad_task->seller_timeout = seller_timeout;
   score_ad_task->callback = std::move(callback);
 
   // If `trusted_signals_request_manager_` exists, there's a trusted scoring
@@ -327,6 +339,7 @@
     const GURL& browser_signal_render_url,
     const std::vector<std::string>& browser_signal_ad_components,
     uint32_t browser_signal_bidding_duration_msecs,
+    const absl::optional<base::TimeDelta> seller_timeout,
     ScoreAdCallbackInternal callback) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(v8_sequence_checker_);
   AuctionV8Helper::FullIsolateScope isolate_scope(v8_helper_.get());
@@ -420,8 +433,7 @@
       *debug_id_, "beforeSellerWorkletScoringStart");
   if (!v8_helper_
            ->RunScript(context, worklet_script_.Get(isolate), debug_id_.get(),
-                       "scoreAd", args, /*script_timeout=*/absl::nullopt,
-                       errors_out)
+                       "scoreAd", args, std::move(seller_timeout), errors_out)
            .ToLocal(&score_ad_result)) {
     PostScoreAdCallbackToUserThread(
         std::move(callback), /*score=*/0,
@@ -681,6 +693,7 @@
           std::move(task->browser_signal_render_url),
           std::move(task->browser_signal_ad_components),
           task->browser_signal_bidding_duration_msecs,
+          std::move(task->seller_timeout),
           base::BindOnce(&SellerWorklet::DeliverScoreAdCallbackOnUserThread,
                          weak_ptr_factory_.GetWeakPtr(), task)));
 }
diff --git a/content/services/auction_worklet/seller_worklet.h b/content/services/auction_worklet/seller_worklet.h
index c54187f..3cb85be 100644
--- a/content/services/auction_worklet/seller_worklet.h
+++ b/content/services/auction_worklet/seller_worklet.h
@@ -82,6 +82,7 @@
                const GURL& browser_signal_render_url,
                const std::vector<GURL>& browser_signal_ad_components,
                uint32_t browser_signal_bidding_duration_msecs,
+               const absl::optional<base::TimeDelta> seller_timeout,
                ScoreAdCallback callback) override;
   void SendPendingSignalsRequests() override;
   void ReportResult(blink::mojom::AuctionAdConfigNonSharedParamsPtr
@@ -118,6 +119,7 @@
     // ScoringSignals code with BidderWorklets.
     std::vector<std::string> browser_signal_ad_components;
     uint32_t browser_signal_bidding_duration_msecs;
+    absl::optional<base::TimeDelta> seller_timeout;
 
     ScoreAdCallback callback;
 
@@ -190,6 +192,7 @@
                  const GURL& browser_signal_render_url,
                  const std::vector<std::string>& browser_signal_ad_components,
                  uint32_t browser_signal_bidding_duration_msecs,
+                 const absl::optional<base::TimeDelta> seller_timeout,
                  ScoreAdCallbackInternal callback);
 
     void ReportResult(blink::mojom::AuctionAdConfigNonSharedParamsPtr
diff --git a/content/services/auction_worklet/seller_worklet_unittest.cc b/content/services/auction_worklet/seller_worklet_unittest.cc
index 1ef10f3..fe12c87 100644
--- a/content/services/auction_worklet/seller_worklet_unittest.cc
+++ b/content/services/auction_worklet/seller_worklet_unittest.cc
@@ -95,7 +95,12 @@
 
 class SellerWorkletTest : public testing::Test {
  public:
-  SellerWorkletTest() { SetDefaultParameters(); }
+  explicit SellerWorkletTest(
+      base::test::TaskEnvironment::TimeSource time_mode =
+          base::test::TaskEnvironment::TimeSource::MOCK_TIME)
+      : task_environment_(time_mode) {
+    SetDefaultParameters();
+  }
 
   ~SellerWorkletTest() override = default;
 
@@ -141,6 +146,7 @@
     browser_signal_ad_components_.clear();
     browser_signal_bidding_duration_msecs_ = 0;
     browser_signal_desireability_ = 1;
+    seller_timeout_ = absl::nullopt;
   }
 
   // Configures `url_loader_factory_` to return a script with the specified
@@ -194,8 +200,7 @@
   }
 
   // Configures `url_loader_factory_` to return the provided script, and then
-  // runs its generate_bid() function. Then runs the script, expecting the
-  // provided result.
+  // runs its score_ad() function, expecting the provided result.
   void RunScoreAdWithJavascriptExpectingResult(
       const std::string& javascript,
       double expected_score,
@@ -228,6 +233,7 @@
         ad_metadata_, bid_, auction_ad_config_non_shared_params_.Clone(),
         browser_signal_interest_group_owner_, browser_signal_render_url_,
         browser_signal_ad_components_, browser_signal_bidding_duration_msecs_,
+        seller_timeout_,
         base::BindOnce(
             [](double expected_score,
                absl::optional<uint32_t> expected_data_version,
@@ -260,6 +266,7 @@
         ad_metadata_, bid_, auction_ad_config_non_shared_params_.Clone(),
         browser_signal_interest_group_owner_, browser_signal_render_url_,
         browser_signal_ad_components_, browser_signal_bidding_duration_msecs_,
+        seller_timeout_,
         base::BindOnce([](double score, uint32_t data_version,
                           bool has_data_version,
                           const absl::optional<GURL>& debug_loss_report_url,
@@ -462,8 +469,7 @@
       disconnect_run_loop_->Quit();
   }
 
-  base::test::TaskEnvironment task_environment_{
-      base::test::TaskEnvironment::TimeSource::MOCK_TIME};
+  base::test::TaskEnvironment task_environment_;
 
   // Arguments passed to score_bid() and report_result(). Arguments common to
   // both of them use the same field.
@@ -482,6 +488,7 @@
   uint32_t browser_signal_bidding_duration_msecs_;
   double browser_signal_desireability_;
   absl::optional<uint32_t> browser_signal_data_version_;
+  absl::optional<base::TimeDelta> seller_timeout_;
 
   // Reuseable run loop for disconnection errors.
   std::unique_ptr<base::RunLoop> disconnect_run_loop_;
@@ -1348,6 +1355,8 @@
       R"({"is_auction_signals": true})";
   auction_ad_config_non_shared_params_->seller_signals =
       R"({"is_seller_signals": true})";
+  auction_ad_config_non_shared_params_->seller_timeout =
+      base::Milliseconds(200);
   base::flat_map<url::Origin, std::string> per_buyer_signals;
   per_buyer_signals[url::Origin::Create(GURL("https://a.com"))] =
       R"({"signals_a": "A"})";
@@ -1370,6 +1379,7 @@
       R"("interestGroupBuyers":["https://buyer1.com","https://another-buyer.com"],)"
       R"("auctionSignals":{"is_auction_signals":true},)"
       R"("sellerSignals":{"is_seller_signals":true},)"
+      R"("sellerTimeout":200,)"
       R"("perBuyerSignals":{"https://a.com":{"signals_a":"A"},)"
       R"("https://b.com":{"signals_b":"B"}},)"
       R"("perBuyerTimeouts":{"https://a.com":100,"*":150}})";
@@ -1453,6 +1463,7 @@
           ad_metadata_, bid_, auction_ad_config_non_shared_params_.Clone(),
           browser_signal_interest_group_owner_, browser_signal_render_url_,
           browser_signal_ad_components_, browser_signal_bidding_duration_msecs_,
+          seller_timeout_,
           base::BindLambdaForTesting(
               [&run_loop](double score, uint32_t data_version,
                           bool has_data_version,
@@ -1499,6 +1510,7 @@
       ad_metadata_, bid_, auction_ad_config_non_shared_params_.Clone(),
       browser_signal_interest_group_owner_, browser_signal_render_url_,
       browser_signal_ad_components_, browser_signal_bidding_duration_msecs_,
+      seller_timeout_,
       base::BindOnce([](double score, uint32_t data_version,
                         bool has_data_version,
                         const absl::optional<GURL>& debug_loss_report_url,
@@ -2066,6 +2078,35 @@
       /*expected_debug_win_report_url=*/absl::nullopt);
 }
 
+class SellerWorkletRealTimeTest : public SellerWorkletTest {
+ public:
+  SellerWorkletRealTimeTest()
+      : SellerWorkletTest(
+            base::test::TaskEnvironment::TimeSource::SYSTEM_TIME) {}
+};
+
+TEST_F(SellerWorkletRealTimeTest, ScoreAdTimedOut) {
+  // Use a very long default script timeout, and a short seller timeout, so
+  // that if the seller script with endless loop times out, we know that the
+  // seller timeout overwrote the default script timeout and worked.
+  const base::TimeDelta kScriptTimeout = base::Days(360);
+  v8_helper_->v8_runner()->PostTask(
+      FROM_HERE,
+      base::BindOnce(
+          [](scoped_refptr<AuctionV8Helper> v8_helper,
+             const base::TimeDelta script_timeout) {
+            v8_helper->set_script_timeout_for_testing(script_timeout);
+          },
+          v8_helper_, kScriptTimeout));
+  // Make sure set_script_timeout_for_testing is called.
+  task_environment_.RunUntilIdle();
+
+  seller_timeout_ = base::Milliseconds(20);
+  RunScoreAdWithJavascriptExpectingResult(
+      CreateScoreAdScript(/*raw_return_value=*/"", R"(while (1))"), 0,
+      {"https://url.test/ execution of `scoreAd` timed out."});
+}
+
 class SellerWorkletBiddingAndScoringDebugReportingAPIEnabledTest
     : public SellerWorkletTest {
  public:
@@ -2244,6 +2285,7 @@
         ad_metadata_, i + 1, auction_ad_config_non_shared_params_.Clone(),
         browser_signal_interest_group_owner_, browser_signal_render_url_,
         browser_signal_ad_components_, browser_signal_bidding_duration_msecs_,
+        seller_timeout_,
         base::BindLambdaForTesting(
             [&run_loop](double score, uint32_t data_version,
                         bool has_data_version,
diff --git a/content/test/data/interest_group/component_auction_component_decision_argument_validator.js b/content/test/data/interest_group/component_auction_component_decision_argument_validator.js
index 6677279..7e02168c 100644
--- a/content/test/data/interest_group/component_auction_component_decision_argument_validator.js
+++ b/content/test/data/interest_group/component_auction_component_decision_argument_validator.js
@@ -53,6 +53,8 @@
   const sellerSignalsJson = JSON.stringify(auctionConfig.sellerSignals);
   if (sellerSignalsJson !== '["component seller signals"]')
     throw 'Wrong sellerSignals ' + auctionConfig.sellerSignalsJson;
+  if (auctionConfig.sellerTimeout !== 200)
+    throw 'Wrong sellerTimeout ' + auctionConfig.sellerTimeout;
   const perBuyerSignalsJson = JSON.stringify(auctionConfig.perBuyerSignals);
   if (!perBuyerSignalsJson.includes('a.test') ||
       !perBuyerSignalsJson.includes('["component buyer signals"]')) {
diff --git a/content/test/data/interest_group/component_auction_top_level_decision_argument_validator.js b/content/test/data/interest_group/component_auction_top_level_decision_argument_validator.js
index a1a5b44..2bef3bd 100644
--- a/content/test/data/interest_group/component_auction_top_level_decision_argument_validator.js
+++ b/content/test/data/interest_group/component_auction_top_level_decision_argument_validator.js
@@ -56,6 +56,8 @@
   const sellerSignalsJson = JSON.stringify(auctionConfig.sellerSignals);
   if (sellerSignalsJson !== '["top-level seller signals"]')
     throw 'Wrong sellerSignals ' + auctionConfig.sellerSignalsJSON;
+  if (auctionConfig.sellerTimeout !== 300)
+    throw 'Wrong sellerTimeout ' + auctionConfig.sellerTimeout;
   const perBuyerSignalsJson = JSON.stringify(auctionConfig.perBuyerSignals);
   if (!perBuyerSignalsJson.includes('a.test') ||
       !perBuyerSignalsJson.includes('["top-level buyer signals"]')) {
diff --git a/content/test/data/interest_group/decision_argument_validator.js b/content/test/data/interest_group/decision_argument_validator.js
index 1670e36d..c1205b25 100644
--- a/content/test/data/interest_group/decision_argument_validator.js
+++ b/content/test/data/interest_group/decision_argument_validator.js
@@ -47,6 +47,8 @@
   const sellerSignalsJSON = JSON.stringify(auctionConfig.sellerSignals);
   if (sellerSignalsJSON !== '{"signals":"from","the":["seller"]}')
     throw 'Wrong sellerSignals ' + auctionConfig.sellerSignalsJSON;
+  if (auctionConfig.sellerTimeout !== 200)
+    throw 'Wrong sellerTimeout ' + auctionConfig.sellerTimeout;
   const perBuyerSignalsJson = JSON.stringify(auctionConfig.perBuyerSignals);
   if (!perBuyerSignalsJson.includes('a.test') ||
       !perBuyerSignalsJson.includes('{"signalsForBuyer":1}')) {
diff --git a/content/test/data/interest_group/decision_logic_loop_forever.js b/content/test/data/interest_group/decision_logic_loop_forever.js
new file mode 100644
index 0000000..329d313
--- /dev/null
+++ b/content/test/data/interest_group/decision_logic_loop_forever.js
@@ -0,0 +1,18 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+function scoreAd(
+  adMetadata, bid, auctionConfig, trustedScoringSignals, browserSignals) {
+  while (1);
+  return bid;
+}
+
+function reportResult(auctionConfig, browserSignals) {
+  sendReportTo(auctionConfig.seller + '/echoall?report_seller');
+  return {
+    'success': true,
+    'signalsForWinner': {'signalForWinner': 1},
+    'reportUrl': auctionConfig.seller + '/report_seller',
+  };
+}
diff --git a/content/test/data/interest_group/decision_logic_loop_forever.js.mock-http-headers b/content/test/data/interest_group/decision_logic_loop_forever.js.mock-http-headers
new file mode 100644
index 0000000..051fe406
--- /dev/null
+++ b/content/test/data/interest_group/decision_logic_loop_forever.js.mock-http-headers
@@ -0,0 +1,3 @@
+HTTP/1.1 200 OK
+Content-Type: Application/Javascript
+X-Allow-FLEDGE: true
diff --git a/content/test/gpu/gpu_tests/gpu_integration_test.py b/content/test/gpu/gpu_tests/gpu_integration_test.py
index ec410692..d013a86 100644
--- a/content/test/gpu/gpu_tests/gpu_integration_test.py
+++ b/content/test/gpu/gpu_tests/gpu_integration_test.py
@@ -40,6 +40,7 @@
   _cached_expectations = None
   _also_run_disabled_tests = False
   _disable_log_uploads = False
+  _extra_intel_device_id_with_overlays = None
 
   # Several of the tests in this directory need to be able to relaunch
   # the browser on demand with a new set of command line arguments
@@ -105,6 +106,9 @@
         default=False,
         help=('Whether a test filter has been applied. Can be used as a proxy '
               'for whether this is a retry without patch on a trybot.'))
+    parser.add_option('--extra-intel-device-id-with-overlays',
+                      dest='extra_intel_device_id_with_overlays',
+                      help='The extra Intel device id with overlays')
 
   @classmethod
   def GenerateBrowserArgs(cls, additional_args):
@@ -232,6 +236,8 @@
   @classmethod
   def GenerateTestCases__RunGpuTest(cls, options):
     cls._disable_log_uploads = options.disable_log_uploads
+    cls._extra_intel_device_id_with_overlays = (
+        options.extra_intel_device_id_with_overlays)
     for test_name, url, args in cls.GenerateGpuTests(options):
       yield test_name, (url, test_name, args)
 
@@ -603,6 +609,12 @@
         if gpu_device_id in _SUPPORTED_WIN_AMD_GPUS_WITH_NV12_OVERLAYS:
           config['nv12_overlay_support'] = 'SCALING'
       elif gpu_vendor_id == 0x8086:
+        if self._extra_intel_device_id_with_overlays:
+          extra_device_id = int(self._extra_intel_device_id_with_overlays, 16)
+          _SUPPORTED_WIN_INTEL_GPUS.append(extra_device_id)
+          _SUPPORTED_WIN_INTEL_GPUS_WITH_YUY2_OVERLAYS.append(extra_device_id)
+          _SUPPORTED_WIN_INTEL_GPUS_WITH_NV12_OVERLAYS.append(extra_device_id)
+
         assert gpu_device_id in _SUPPORTED_WIN_INTEL_GPUS
         gpu_device_and_driver = ('%x-' + gpu.driver_version) % gpu_device_id
         if gpu_device_id in _SUPPORTED_WIN_INTEL_GPUS_WITH_YUY2_OVERLAYS:
diff --git a/content/test/gpu/gpu_tests/test_expectations/webgl2_conformance_expectations.txt b/content/test/gpu/gpu_tests/test_expectations/webgl2_conformance_expectations.txt
index 213e5da..6ab67767 100644
--- a/content/test/gpu/gpu_tests/test_expectations/webgl2_conformance_expectations.txt
+++ b/content/test/gpu/gpu_tests/test_expectations/webgl2_conformance_expectations.txt
@@ -332,8 +332,6 @@
 crbug.com/angleproject/6430 [ mac passthrough angle-metal ] WebglExtension_WEBGL_multi_draw_instanced_base_vertex_base_instance [ Failure ]
 crbug.com/angleproject/6430 [ mac passthrough angle-metal ] conformance/extensions/s3tc-and-rgtc.html [ Failure ]
 crbug.com/angleproject/6430 [ mac passthrough angle-metal ] conformance/extensions/webgl-compressed-texture-s3tc-srgb.html [ Failure ]
-crbug.com/angleproject/6430 [ mac passthrough angle-metal ] conformance/ogles/GL/build/build_009_to_016.html [ Failure ]
-crbug.com/angleproject/6430 [ mac passthrough angle-metal ] conformance/ogles/GL/build/build_017_to_024.html [ Failure ]
 crbug.com/angleproject/6430 [ mac passthrough angle-metal ] conformance2/rendering/fs-color-type-mismatch-color-buffer-type.html [ Failure ]
 crbug.com/angleproject/6430 [ mac passthrough angle-metal ] deqp/functional/gles3/fboinvalidate/sub.html [ Failure ]
 crbug.com/angleproject/6430 [ mac passthrough angle-metal ] deqp/functional/gles3/fboinvalidate/whole.html [ Failure ]
diff --git a/device/bluetooth/BUILD.gn b/device/bluetooth/BUILD.gn
index 2ddedaa..49780a30 100644
--- a/device/bluetooth/BUILD.gn
+++ b/device/bluetooth/BUILD.gn
@@ -207,10 +207,6 @@
     deps += [ "//chromeos/constants" ]
   }
 
-  if (is_chromeos_ash || is_chromeos_lacros) {
-    deps += [ "//chromeos/constants" ]
-  }
-
   if (is_chromeos_ash) {
     deps += [ "//ash/constants" ]
   }
diff --git a/docs/adding_to_third_party.md b/docs/adding_to_third_party.md
index 8128306..70a161e 100644
--- a/docs/adding_to_third_party.md
+++ b/docs/adding_to_third_party.md
@@ -203,7 +203,9 @@
 * Lastly, if all other steps are complete, get a positive code review from a
   member of [//third_party/OWNERS](../third_party/OWNERS) to land the change.
 
-Please send separate emails to the eng review and security lists.
+Please send separate emails to the eng review and security@chromium.org.
+You can skip the eng review and security@chromium.org when you are only moving
+existing directories in Chromium to //third_party/.
 
 Subsequent changes don't normally require third-party-owners or security
 approval; you can modify the code as much as you want. When you update code, be
diff --git a/extensions/browser/api/messaging/extension_message_port.cc b/extensions/browser/api/messaging/extension_message_port.cc
index d4b3314..4cc25e84 100644
--- a/extensions/browser/api/messaging/extension_message_port.cc
+++ b/extensions/browser/api/messaging/extension_message_port.cc
@@ -449,19 +449,27 @@
 
   for (const IPCTarget& target : targets) {
     // Frames in the BackForwardCache are not allowed to receive messages (or
-    // even have them queued). In such a case, we evict the frame from the cache
+    // even have them queued). In such a case, we evict the page from the cache
     // and "drop" the message (See comment in `DidFinishNavigation()`).
     // Note: Since this will cause the frame to be deleted, we do this here
     // instead of in the loop above to avoid modifying `frames_` while it is
     // being iterated.
+    //
+    // This could cause the same page to be evicted multiple times if it has
+    // multiple frames receiving this message. This is harmless as the reason is
+    // the same in every case. Also multiple extensions may send messages before
+    // the page is actually evicted. The last one will be the one the user
+    // sees. It is not worth the effort to present all of them to the user. It's
+    // unlikely they will see the same one every time and if they do, when they
+    // fix that one, they will see the others.
     if (target.render_frame_host &&
         target.render_frame_host->GetLifecycleState() ==
             content::RenderFrameHost::LifecycleState::kInBackForwardCache) {
       content::BackForwardCache::DisableForRenderFrameHost(
-          target.render_frame_host,
-          back_forward_cache::DisabledReason(
-              back_forward_cache::DisabledReasonId::
-                  kExtensionSentMessageToCachedFrame));
+          target.render_frame_host, back_forward_cache::DisabledReason(
+                                        back_forward_cache::DisabledReasonId::
+                                            kExtensionSentMessageToCachedFrame,
+                                        /*context=*/extension_id_));
       continue;
     }
 
diff --git a/infra/config/generated/luci/cr-buildbucket.cfg b/infra/config/generated/luci/cr-buildbucket.cfg
index ea47208..f2af63a1 100644
--- a/infra/config/generated/luci/cr-buildbucket.cfg
+++ b/infra/config/generated/luci/cr-buildbucket.cfg
@@ -3883,10 +3883,10 @@
     builders {
       name: "Dawn Mac x64 Builder"
       swarming_host: "chromium-swarm.appspot.com"
-      dimensions: "builder:Dawn Mac x64 Builder"
+      dimensions: "builderless:1"
       dimensions: "cpu:x86-64"
       dimensions: "os:Mac"
-      dimensions: "pool:luci.chromium.ci"
+      dimensions: "pool:luci.chromium.gpu.ci"
       exe {
         cipd_package: "infra/chromium/bootstrapper/${platform}"
         cipd_version: "latest"
@@ -3960,10 +3960,10 @@
     builders {
       name: "Dawn Mac x64 DEPS Builder"
       swarming_host: "chromium-swarm.appspot.com"
-      dimensions: "builder:Dawn Mac x64 DEPS Builder"
+      dimensions: "builderless:1"
       dimensions: "cpu:x86-64"
       dimensions: "os:Mac"
-      dimensions: "pool:luci.chromium.ci"
+      dimensions: "pool:luci.chromium.gpu.ci"
       exe {
         cipd_package: "infra/chromium/bootstrapper/${platform}"
         cipd_version: "latest"
@@ -6491,10 +6491,10 @@
     builders {
       name: "GPU FYI Mac Builder"
       swarming_host: "chromium-swarm.appspot.com"
-      dimensions: "builder:GPU FYI Mac Builder"
+      dimensions: "builderless:1"
       dimensions: "cpu:x86-64"
       dimensions: "os:Mac"
-      dimensions: "pool:luci.chromium.ci"
+      dimensions: "pool:luci.chromium.gpu.ci"
       exe {
         cipd_package: "infra/chromium/bootstrapper/${platform}"
         cipd_version: "latest"
@@ -6571,10 +6571,10 @@
     builders {
       name: "GPU FYI Mac Builder (asan)"
       swarming_host: "chromium-swarm.appspot.com"
-      dimensions: "builder:GPU FYI Mac Builder (asan)"
+      dimensions: "builderless:1"
       dimensions: "cpu:x86-64"
       dimensions: "os:Mac"
-      dimensions: "pool:luci.chromium.ci"
+      dimensions: "pool:luci.chromium.gpu.ci"
       exe {
         cipd_package: "infra/chromium/bootstrapper/${platform}"
         cipd_version: "latest"
@@ -6651,10 +6651,10 @@
     builders {
       name: "GPU FYI Mac Builder (dbg)"
       swarming_host: "chromium-swarm.appspot.com"
-      dimensions: "builder:GPU FYI Mac Builder (dbg)"
+      dimensions: "builderless:1"
       dimensions: "cpu:x86-64"
       dimensions: "os:Mac"
-      dimensions: "pool:luci.chromium.ci"
+      dimensions: "pool:luci.chromium.gpu.ci"
       exe {
         cipd_package: "infra/chromium/bootstrapper/${platform}"
         cipd_version: "latest"
@@ -6731,10 +6731,10 @@
     builders {
       name: "GPU FYI Mac arm64 Builder"
       swarming_host: "chromium-swarm.appspot.com"
-      dimensions: "builder:GPU FYI Mac arm64 Builder"
+      dimensions: "builderless:1"
       dimensions: "cpu:x86-64"
       dimensions: "os:Mac"
-      dimensions: "pool:luci.chromium.ci"
+      dimensions: "pool:luci.chromium.gpu.ci"
       exe {
         cipd_package: "infra/chromium/bootstrapper/${platform}"
         cipd_version: "latest"
@@ -7467,10 +7467,10 @@
     builders {
       name: "GPU Mac Builder"
       swarming_host: "chromium-swarm.appspot.com"
-      dimensions: "builder:GPU Mac Builder"
+      dimensions: "builderless:1"
       dimensions: "cpu:x86-64"
       dimensions: "os:Mac"
-      dimensions: "pool:luci.chromium.ci"
+      dimensions: "pool:luci.chromium.gpu.ci"
       exe {
         cipd_package: "infra/chromium/bootstrapper/${platform}"
         cipd_version: "latest"
@@ -7547,10 +7547,10 @@
     builders {
       name: "GPU Mac Builder (dbg)"
       swarming_host: "chromium-swarm.appspot.com"
-      dimensions: "builder:GPU Mac Builder (dbg)"
+      dimensions: "builderless:1"
       dimensions: "cpu:x86-64"
       dimensions: "os:Mac"
-      dimensions: "pool:luci.chromium.ci"
+      dimensions: "pool:luci.chromium.gpu.ci"
       exe {
         cipd_package: "infra/chromium/bootstrapper/${platform}"
         cipd_version: "latest"
@@ -29213,10 +29213,10 @@
     builders {
       name: "ios-angle-builder"
       swarming_host: "chromium-swarm.appspot.com"
-      dimensions: "builder:ios-angle-builder"
+      dimensions: "builderless:1"
       dimensions: "cpu:x86-64"
       dimensions: "os:Mac"
-      dimensions: "pool:luci.chromium.ci"
+      dimensions: "pool:luci.chromium.gpu.ci"
       exe {
         cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build"
         cipd_version: "refs/heads/main"
@@ -35806,10 +35806,10 @@
     builders {
       name: "mac-angle-chromium-builder"
       swarming_host: "chromium-swarm.appspot.com"
-      dimensions: "builder:mac-angle-chromium-builder"
+      dimensions: "builderless:1"
       dimensions: "cpu:x86-64"
       dimensions: "os:Mac"
-      dimensions: "pool:luci.chromium.ci"
+      dimensions: "pool:luci.chromium.gpu.ci"
       exe {
         cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build"
         cipd_version: "refs/heads/main"
@@ -37373,10 +37373,10 @@
     builders {
       name: "mac-swangle-chromium-x64"
       swarming_host: "chromium-swarm.appspot.com"
-      dimensions: "builder:mac-swangle-chromium-x64"
+      dimensions: "builderless:1"
       dimensions: "cpu:x86-64"
       dimensions: "os:Mac"
-      dimensions: "pool:luci.chromium.ci"
+      dimensions: "pool:luci.chromium.gpu.ci"
       exe {
         cipd_package: "infra/chromium/bootstrapper/${platform}"
         cipd_version: "latest"
diff --git a/infra/config/lib/ci.star b/infra/config/lib/ci.star
index 2198a7e..00c2c937 100644
--- a/infra/config/lib/ci.star
+++ b/infra/config/lib/ci.star
@@ -219,8 +219,8 @@
     This sets mac-specific defaults that are common to GPU-related builder
     groups.
     """
+    kwargs.setdefault("builderless", True)
     kwargs.setdefault("os", os.MAC_ANY)
-    kwargs.setdefault("pool", ci.DEFAULT_POOL)
     return ci.builder(name = name, **kwargs)
 
 def _gpu_windows_builder(*, name, **kwargs):
diff --git a/ios/chrome/app/BUILD.gn b/ios/chrome/app/BUILD.gn
index effb4eb1..b2ddec05 100644
--- a/ios/chrome/app/BUILD.gn
+++ b/ios/chrome/app/BUILD.gn
@@ -200,6 +200,7 @@
     "//components/policy/core/common",
     "//ios/chrome/app:tests_hook",
     "//ios/chrome/app/application_delegate:app_state_header",
+    "//ios/chrome/app/application_delegate:application_delegate_internal",
     "//ios/chrome/browser",
     "//ios/chrome/browser/policy",
     "//ios/chrome/browser/ui/first_run:utils",
diff --git a/ios/chrome/app/application_delegate/app_state_unittest.mm b/ios/chrome/app/application_delegate/app_state_unittest.mm
index 0e32d28..8f9a5f2 100644
--- a/ios/chrome/app/application_delegate/app_state_unittest.mm
+++ b/ios/chrome/app/application_delegate/app_state_unittest.mm
@@ -523,6 +523,8 @@
 
   base::TimeTicks now = base::TimeTicks::Now();
   [[[getStartupInformationMock() stub] andReturnValue:@YES] isColdStart];
+  [[getStartupInformationMock() stub] setIsFirstRun:YES];
+  [[[getStartupInformationMock() stub] andReturnValue:@YES] isFirstRun];
   [[[getStartupInformationMock() stub] andDo:^(NSInvocation* invocation) {
     [invocation setReturnValue:(void*)&now];
   }] appLaunchTime];
@@ -544,7 +546,6 @@
 
   swizzleSafeModeShouldStart(YES);
 
-
   // Action.
   BOOL result = [appState requiresHandlingAfterLaunchWithOptions:launchOptions
                                                  stateBackground:NO];
@@ -578,6 +579,8 @@
       @{UIApplicationLaunchOptionsSourceApplicationKey : sourceApplication};
 
   [[[getStartupInformationMock() stub] andReturnValue:@YES] isColdStart];
+  [[getStartupInformationMock() stub] setIsFirstRun:YES];
+  [[[getStartupInformationMock() stub] andReturnValue:@YES] isFirstRun];
 
   [[[getWindowMock() stub] andReturn:nil] rootViewController];
 
@@ -708,6 +711,8 @@
 // Tests that -applicationWillEnterForeground resets components as needed.
 TEST_F(AppStateTest, applicationWillEnterForeground) {
   swizzleSafeModeShouldStart(NO);
+  [[getStartupInformationMock() stub] setIsFirstRun:YES];
+  [[[getStartupInformationMock() stub] andReturnValue:@YES] isFirstRun];
 
   // Setup.
   ios::TestChromeBrowserProvider::GetTestProvider()
@@ -778,6 +783,8 @@
   swizzleSafeModeShouldStart(NO);
 
   [[[getStartupInformationMock() stub] andReturnValue:@YES] isColdStart];
+  [[getStartupInformationMock() stub] setIsFirstRun:YES];
+  [[[getStartupInformationMock() stub] andReturnValue:@YES] isFirstRun];
 
   // Actions.
   [getAppStateWithMock() applicationWillEnterForeground:application
@@ -791,6 +798,8 @@
 // Tests that -applicationDidEnterBackground calls the metrics mediator.
 TEST_F(AppStateTest, applicationDidEnterBackgroundIncognito) {
   swizzleSafeModeShouldStart(NO);
+  [[getStartupInformationMock() stub] setIsFirstRun:YES];
+  [[[getStartupInformationMock() stub] andReturnValue:@YES] isFirstRun];
 
   // Setup.
   ScopedKeyWindow scopedKeyWindow;
@@ -837,6 +846,8 @@
 // never been in a Foreground stage.
 TEST_F(AppStateTest, applicationDidEnterBackgroundStageBackground) {
   swizzleSafeModeShouldStart(NO);
+  [[getStartupInformationMock() stub] setIsFirstRun:YES];
+  [[[getStartupInformationMock() stub] andReturnValue:@YES] isFirstRun];
 
   // Setup.
   ScopedKeyWindow scopedKeyWindow;
diff --git a/ios/chrome/app/enterprise_app_agent.mm b/ios/chrome/app/enterprise_app_agent.mm
index 825d451..f74a507 100644
--- a/ios/chrome/app/enterprise_app_agent.mm
+++ b/ios/chrome/app/enterprise_app_agent.mm
@@ -9,6 +9,7 @@
 #import "components/policy/core/common/cloud/machine_level_user_cloud_policy_manager.h"
 #import "components/policy/core/common/policy_namespace.h"
 #import "ios/chrome/app/application_delegate/app_state.h"
+#include "ios/chrome/app/application_delegate/startup_information.h"
 #import "ios/chrome/app/enterprise_loading_screen_view_controller.h"
 #import "ios/chrome/app/tests_hook.h"
 #include "ios/chrome/browser/application_context.h"
@@ -74,6 +75,17 @@
 
 #pragma mark - AppStateObserver
 
+- (void)appState:(AppState*)appState
+    willTransitionToInitStage:(InitStage)nextInitStage {
+  if (nextInitStage != InitStageEnterprise) {
+    return;
+  }
+
+  // Ensure to have the information available on time.
+  self.appState.startupInformation.isFirstRun =
+      ShouldPresentFirstRunExperience();
+}
+
 - (void)appState:(AppState*)appState sceneConnected:(SceneState*)sceneState {
   [sceneState addObserver:self];
 }
@@ -192,7 +204,7 @@
       machineLevelUserCloudPolicyManager =
           self.policyConnector->machine_level_user_cloud_policy_manager();
 
-  return ShouldPresentFirstRunExperience() &&
+  return self.appState.startupInformation.isFirstRun &&
          self.policyConnector->chrome_browser_cloud_management_controller()
              ->IsEnabled() &&
          machineLevelUserCloudPolicyManager &&
diff --git a/ios/google_internal/frameworks/chrome_internal_dynamic_framework.ios.zip.sha1 b/ios/google_internal/frameworks/chrome_internal_dynamic_framework.ios.zip.sha1
index 6daf8ea..39bd42b 100644
--- a/ios/google_internal/frameworks/chrome_internal_dynamic_framework.ios.zip.sha1
+++ b/ios/google_internal/frameworks/chrome_internal_dynamic_framework.ios.zip.sha1
@@ -1 +1 @@
-5f6d9d6c69e4d81f0d6b9917a7da16bdbf53b534
\ No newline at end of file
+144d31ef85d9908ec552e5ad76aa3ab0ed199c19
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/chrome_internal_dynamic_framework.iossimulator.zip.sha1 b/ios/google_internal/frameworks/chrome_internal_dynamic_framework.iossimulator.zip.sha1
index 19440e9..747efc0 100644
--- a/ios/google_internal/frameworks/chrome_internal_dynamic_framework.iossimulator.zip.sha1
+++ b/ios/google_internal/frameworks/chrome_internal_dynamic_framework.iossimulator.zip.sha1
@@ -1 +1 @@
-22dd58fb4ee41fece1bb399c83284cf29ecb1213
\ No newline at end of file
+39102119a6c26dee837eb0384e4fcdb76795308d
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.ios.zip.sha1 b/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.ios.zip.sha1
index 4d88b70d..986e4ee1 100644
--- a/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.ios.zip.sha1
+++ b/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.ios.zip.sha1
@@ -1 +1 @@
-e35144ca5a4b65c63fe2ea9d0f69446a3955b0f8
\ No newline at end of file
+ffad931760efb4aaad272aa7cb4069c145f898ac
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.iossimulator.zip.sha1 b/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.iossimulator.zip.sha1
index 759f323d..0c6c87f6 100644
--- a/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.iossimulator.zip.sha1
+++ b/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.iossimulator.zip.sha1
@@ -1 +1 @@
-b82756c93b03c964190ece139c5eb8339e5775dc
\ No newline at end of file
+17bdf3233e57cd95fff84003d69379793c9146b7
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.ios.zip.sha1 b/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.ios.zip.sha1
index 5b6043c2..c3d550e 100644
--- a/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.ios.zip.sha1
+++ b/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.ios.zip.sha1
@@ -1 +1 @@
-bd41fa8eb600c32e60ce7d897f97b805df3d9fdb
\ No newline at end of file
+a04f6dd0b0cb6514a0efd24015bb6160963efb5a
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.iossimulator.zip.sha1 b/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.iossimulator.zip.sha1
index b8ff0cd8..1ef08a9 100644
--- a/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.iossimulator.zip.sha1
+++ b/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.iossimulator.zip.sha1
@@ -1 +1 @@
-05f54f34fd89b73d78bc2a6c05fc60e01a141895
\ No newline at end of file
+fa8d623b33cfdda448de0c69d2b3fc32ce886f57
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/remoting_internal_dynamic_framework.ios.zip.sha1 b/ios/google_internal/frameworks/remoting_internal_dynamic_framework.ios.zip.sha1
index 8f4122c..bbadeaf 100644
--- a/ios/google_internal/frameworks/remoting_internal_dynamic_framework.ios.zip.sha1
+++ b/ios/google_internal/frameworks/remoting_internal_dynamic_framework.ios.zip.sha1
@@ -1 +1 @@
-7a97318c76783dfb27ad35e8230c7a7b657f7bdf
\ No newline at end of file
+e5323bf840b10d6636b4368dee04c81fed5f07a7
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/remoting_internal_dynamic_framework.iossimulator.zip.sha1 b/ios/google_internal/frameworks/remoting_internal_dynamic_framework.iossimulator.zip.sha1
index 6f04140f..a8be1b22 100644
--- a/ios/google_internal/frameworks/remoting_internal_dynamic_framework.iossimulator.zip.sha1
+++ b/ios/google_internal/frameworks/remoting_internal_dynamic_framework.iossimulator.zip.sha1
@@ -1 +1 @@
-5fecc8a9cdfb895cabcbe87eda13205869048754
\ No newline at end of file
+ee8d55521a4140fa2efdc9655bc9074e95be51c1
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.ios.zip.sha1 b/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.ios.zip.sha1
index a89908e..f211e8f 100644
--- a/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.ios.zip.sha1
+++ b/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.ios.zip.sha1
@@ -1 +1 @@
-ab15c4758b1fa20d5ed75f08d3bb3409fbc1a68b
\ No newline at end of file
+0fa1f3773a57ce1732e5436e158fc83bf865fc76
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.iossimulator.zip.sha1 b/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.iossimulator.zip.sha1
index 80cad9a..47c4020 100644
--- a/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.iossimulator.zip.sha1
+++ b/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.iossimulator.zip.sha1
@@ -1 +1 @@
-9900918877b9630de1395c35f72e77540679aabb
\ No newline at end of file
+a07a42101446c82adc398cb9f8d613869df195a8
\ No newline at end of file
diff --git a/media/base/media_switches.cc b/media/base/media_switches.cc
index 0fb175a..bf836e9 100644
--- a/media/base/media_switches.cc
+++ b/media/base/media_switches.cc
@@ -527,7 +527,7 @@
     "VaapiH264TemporalLayerEncoding", base::FEATURE_ENABLED_BY_DEFAULT};
 // Enable VP8 temporal layer encoding with HW encoder on ChromeOS.
 const base::Feature kVaapiVp8TemporalLayerHWEncoding{
-    "VaapiVp8TemporalLayerEncoding", base::FEATURE_ENABLED_BY_DEFAULT};
+    "VaapiVp8TemporalLayerEncoding", base::FEATURE_DISABLED_BY_DEFAULT};
 // Enable VP9 k-SVC encoding with HW encoder for webrtc use case on ChromeOS.
 const base::Feature kVaapiVp9kSVCHWEncoding{"VaapiVp9kSVCHWEncoding",
                                             base::FEATURE_ENABLED_BY_DEFAULT};
diff --git a/media/gpu/test/video_encoder/video_encoder_test_environment.cc b/media/gpu/test/video_encoder/video_encoder_test_environment.cc
index 93f5c4b..adde0b2 100644
--- a/media/gpu/test/video_encoder/video_encoder_test_environment.cc
+++ b/media/gpu/test/video_encoder/video_encoder_test_environment.cc
@@ -165,6 +165,8 @@
 #if BUILDFLAG(IS_CHROMEOS) && BUILDFLAG(USE_VAAPI)
   // TODO(crbug.com/1186051): remove once enabled by default.
   combined_enabled_features.push_back(media::kVaapiVp9kSVCHWEncoding);
+  // TODO(b/202926617): remove once enabled by default.
+  combined_enabled_features.push_back(media::kVaapiVp8TemporalLayerHWEncoding);
 #endif
 
   const uint32_t bitrate = encode_bitrate.value_or(
diff --git a/media/gpu/v4l2/v4l2_video_encode_accelerator.cc b/media/gpu/v4l2/v4l2_video_encode_accelerator.cc
index 46ac323..e5ca25b 100644
--- a/media/gpu/v4l2/v4l2_video_encode_accelerator.cc
+++ b/media/gpu/v4l2/v4l2_video_encode_accelerator.cc
@@ -162,9 +162,13 @@
 
 V4L2VideoEncodeAccelerator::InputFrameInfo::~InputFrameInfo() = default;
 
+// static
+base::AtomicRefCount V4L2VideoEncodeAccelerator::num_instances_(0);
+
 V4L2VideoEncodeAccelerator::V4L2VideoEncodeAccelerator(
     scoped_refptr<V4L2Device> device)
-    : child_task_runner_(base::ThreadTaskRunnerHandle::Get()),
+    : can_use_encoder_(num_instances_.Increment() < kMaxNumOfInstances),
+      child_task_runner_(base::ThreadTaskRunnerHandle::Get()),
       native_input_mode_(false),
       output_buffer_byte_size_(0),
       output_format_fourcc_(0),
@@ -192,6 +196,8 @@
   DCHECK_CALLED_ON_VALID_SEQUENCE(encoder_sequence_checker_);
   DCHECK(!device_poll_thread_.IsRunning());
   VLOGF(2);
+
+  num_instances_.Decrement();
 }
 
 bool V4L2VideoEncodeAccelerator::Initialize(const Config& config,
@@ -202,6 +208,11 @@
   TRACE_EVENT0("media,gpu", "V4L2VEA::Initialize");
   VLOGF(2) << ": " << config.AsHumanReadableString();
 
+  if (!can_use_encoder_) {
+    VLOGF(1) << "Too many encoders are allocated";
+    return false;
+  }
+
   // V4L2VEA doesn't support temporal layers but we let it pass here to support
   // simulcast.
   if (config.HasSpatialLayer()) {
@@ -1331,11 +1342,8 @@
       return false;
   }
 
-  // Keep |gmb_handle| alive as long as |frame| is alive so that fds passed
-  // to the driver are valid during encoding.
-  frame->AddDestructionObserver(
-      base::BindOnce([](gfx::GpuMemoryBufferHandle) {}, std::move(gmb_handle)));
-
+  // Keep |frame| in |input_record| so that a client doesn't use |frame| until
+  // a driver finishes using it, that is, VIDIOC_DQBUF is called.
   InputRecord& input_record = input_buffer_map_[buffer_id];
   input_record.frame = frame;
   input_record.ip_output_buffer_index = frame_info.ip_output_buffer_index;
diff --git a/media/gpu/v4l2/v4l2_video_encode_accelerator.h b/media/gpu/v4l2/v4l2_video_encode_accelerator.h
index a8f0693..b51c4ab5 100644
--- a/media/gpu/v4l2/v4l2_video_encode_accelerator.h
+++ b/media/gpu/v4l2/v4l2_video_encode_accelerator.h
@@ -260,6 +260,14 @@
   // Initializes input_memory_type_.
   bool InitInputMemoryType(const Config& config);
 
+  // Having too many encoder instances at once may cause us to run out of FDs
+  // and subsequently crash (crbug.com/1289465). To avoid that, we limit the
+  // maximum number of encoder instances that can exist at once.
+  // |num_instances_| tracks that number.
+  static constexpr int kMaxNumOfInstances = 10;
+  static base::AtomicRefCount num_instances_;
+  const bool can_use_encoder_;
+
   // Our original calling task runner for the child thread and its checker.
   const scoped_refptr<base::SingleThreadTaskRunner> child_task_runner_;
   SEQUENCE_CHECKER(child_sequence_checker_);
diff --git a/media/gpu/vaapi/vaapi_video_encode_accelerator.cc b/media/gpu/vaapi/vaapi_video_encode_accelerator.cc
index 6aa8af8..7ebeecd 100644
--- a/media/gpu/vaapi/vaapi_video_encode_accelerator.cc
+++ b/media/gpu/vaapi/vaapi_video_encode_accelerator.cc
@@ -139,6 +139,9 @@
   const off_t offset;
 };
 
+// static
+base::AtomicRefCount VaapiVideoEncodeAccelerator::num_instances_(0);
+
 VideoEncodeAccelerator::SupportedProfiles
 VaapiVideoEncodeAccelerator::GetSupportedProfiles() {
   if (IsConfiguredForTesting())
@@ -147,7 +150,8 @@
 }
 
 VaapiVideoEncodeAccelerator::VaapiVideoEncodeAccelerator()
-    : output_buffer_byte_size_(0),
+    : can_use_encoder_(num_instances_.Increment() < kMaxNumOfInstances),
+      output_buffer_byte_size_(0),
       state_(kUninitialized),
       child_task_runner_(base::ThreadTaskRunnerHandle::Get()),
       // TODO(akahuang): Change to use SequencedTaskRunner to see if the
@@ -173,8 +177,11 @@
 VaapiVideoEncodeAccelerator::~VaapiVideoEncodeAccelerator() {
   VLOGF(2);
   DCHECK_CALLED_ON_VALID_SEQUENCE(encoder_sequence_checker_);
+
   base::trace_event::MemoryDumpManager::GetInstance()->UnregisterDumpProvider(
       this);
+
+  num_instances_.Decrement();
 }
 
 bool VaapiVideoEncodeAccelerator::Initialize(const Config& config,
@@ -188,6 +195,11 @@
     return false;
   }
 
+  if (!can_use_encoder_) {
+    VLOGF(1) << "Too many encoders are allocated";
+    return false;
+  }
+
   client_ptr_factory_.reset(new base::WeakPtrFactory<Client>(client));
   client_ = client_ptr_factory_->GetWeakPtr();
 
diff --git a/media/gpu/vaapi/vaapi_video_encode_accelerator.h b/media/gpu/vaapi/vaapi_video_encode_accelerator.h
index 6a5a827..5e846974a 100644
--- a/media/gpu/vaapi/vaapi_video_encode_accelerator.h
+++ b/media/gpu/vaapi/vaapi_video_encode_accelerator.h
@@ -212,6 +212,14 @@
     return !supported_profiles_for_testing_.empty();
   }
 
+  // Having too many encoder instances at once may cause us to run out of FDs
+  // and subsequently crash (crbug.com/1289465). To avoid that, we limit the
+  // maximum number of encoder instances that can exist at once.
+  // |num_instances_| tracks that number.
+  static constexpr int kMaxNumOfInstances = 10;
+  static base::AtomicRefCount num_instances_;
+  const bool can_use_encoder_;
+
   // The unchanged values are filled upon the construction. The varied values
   // are filled properly during encoding.
   VideoEncoderInfo encoder_info_;
diff --git a/media/gpu/vaapi/vp8_vaapi_video_encoder_delegate.cc b/media/gpu/vaapi/vp8_vaapi_video_encoder_delegate.cc
index 953abfb1..ffa207d 100644
--- a/media/gpu/vaapi/vp8_vaapi_video_encoder_delegate.cc
+++ b/media/gpu/vaapi/vp8_vaapi_video_encoder_delegate.cc
@@ -11,6 +11,8 @@
 #include "base/containers/contains.h"
 #include "base/memory/ref_counted_memory.h"
 #include "base/strings/string_number_conversions.h"
+#include "build/build_config.h"
+#include "media/base/media_switches.h"
 #include "media/gpu/gpu_video_encode_accelerator_helpers.h"
 #include "media/gpu/macros.h"
 #include "media/gpu/vaapi/vaapi_common.h"
@@ -287,12 +289,24 @@
 
   if (config.HasTemporalLayer()) {
     CHECK_EQ(config.spatial_layers.size(), 1u);
-    num_temporal_layers_ = config.spatial_layers[0].num_of_temporal_layers;
-    if (num_temporal_layers_ > kMaxSupportedVP8TemporalLayers ||
-        num_temporal_layers_ < kMinSupportedVP8TemporalLayers) {
-      DVLOGF(1) << "Unsupported number of temporal layers: "
-                << base::strict_cast<size_t>(num_temporal_layers_);
-      return false;
+    // TODO(b/202926617): Remove once VP8 TL encoding is enabled by default.
+    const bool enable_vp8_tl_encoding =
+#if defined(ARCH_CPU_X86_FAMILY) && BUILDFLAG(IS_CHROMEOS)
+        base::FeatureList::IsEnabled(kVaapiVp8TemporalLayerHWEncoding);
+#else
+        false;
+#endif
+    if (enable_vp8_tl_encoding) {
+      num_temporal_layers_ = config.spatial_layers[0].num_of_temporal_layers;
+      if (num_temporal_layers_ > kMaxSupportedVP8TemporalLayers ||
+          num_temporal_layers_ < kMinSupportedVP8TemporalLayers) {
+        VLOGF(1) << "Unsupported number of temporal layers: "
+                 << base::strict_cast<size_t>(num_temporal_layers_);
+        return false;
+      }
+    } else {
+      DVLOGF(2) << "Ignoring temporal layer encoding request";
+      num_temporal_layers_ = 1;
     }
   }
 
@@ -304,7 +318,12 @@
 
   Reset();
 
-  auto initial_bitrate_allocation = AllocateBitrateForDefaultEncoding(config);
+  VideoBitrateAllocation initial_bitrate_allocation;
+  if (num_temporal_layers_ > 1)
+    initial_bitrate_allocation = AllocateBitrateForDefaultEncoding(config);
+  else
+    initial_bitrate_allocation.SetBitrate(0, 0, config.bitrate.target());
+
   current_params_.max_qp = kMaxQPForSoftwareRateCtrl;
 
   // |rate_ctrl_| might be injected for tests.
diff --git a/media/mojo/mojom/BUILD.gn b/media/mojo/mojom/BUILD.gn
index 648df0e..f9d78ea 100644
--- a/media/mojo/mojom/BUILD.gn
+++ b/media/mojo/mojom/BUILD.gn
@@ -494,6 +494,14 @@
           cpp = "::media::SVCScalabilityMode"
         },
         {
+          mojom = "media.mojom.VariableBitrate"
+          cpp = "::media::Bitrate"
+        },
+        {
+          mojom = "media.mojom.ConstantBitrate"
+          cpp = "::media::Bitrate"
+        },
+        {
           mojom = "media.mojom.Bitrate"
           cpp = "::media::Bitrate"
         },
diff --git a/media/mojo/mojom/video_encode_accelerator.mojom b/media/mojo/mojom/video_encode_accelerator.mojom
index f2e6ed3..a6abaca0 100644
--- a/media/mojo/mojom/video_encode_accelerator.mojom
+++ b/media/mojo/mojom/video_encode_accelerator.mojom
@@ -72,20 +72,23 @@
   uint8 num_of_temporal_layers;
 };
 
-// This defines a mojo transport format for media::Bitrate
-struct Bitrate {
-  // See media::Bitrate::Mode
-  [Extensible]
-  enum Mode {
-    [Default] kConstant,
-    kVariable
-  };
+// This defines a mojo transport format for a media::Bitrate of type kConstant.
+// The default target here matches that in media::Bitrate.
+struct ConstantBitrate {
+  uint32 target = 0;
+};
 
-  // The defaults here match those in media::Bitrate (CBR, which has no peak).
-  // Target must be set for all bitrates.
-  Mode mode = kConstant;
-  uint32 target;
-  uint32 peak = 0;
+// This defines a mojo transport format for a media::Bitrate of type kVariable.
+// The default target here matches that in media::Bitrate.
+struct VariableBitrate {
+  uint32 target = 0;
+  uint32 peak;
+};
+
+// This defines a mojo transport format for media::Bitrate.
+union Bitrate {
+  ConstantBitrate constant;
+  VariableBitrate variable;
 };
 
 // This defines a mojo transport format for
diff --git a/media/mojo/mojom/video_encode_accelerator_mojom_traits.cc b/media/mojo/mojom/video_encode_accelerator_mojom_traits.cc
index 41b4578b..0eb6fb91 100644
--- a/media/mojo/mojom/video_encode_accelerator_mojom_traits.cc
+++ b/media/mojo/mojom/video_encode_accelerator_mojom_traits.cc
@@ -329,48 +329,48 @@
 }
 
 // static
-media::mojom::Bitrate_Mode
-EnumTraits<media::mojom::Bitrate_Mode, media::Bitrate::Mode>::ToMojom(
-    media::Bitrate::Mode input) {
-  switch (input) {
+bool StructTraits<media::mojom::ConstantBitrateDataView, media::Bitrate>::Read(
+    media::mojom::ConstantBitrateDataView input,
+    media::Bitrate* output) {
+  *output = media::Bitrate::ConstantBitrate(input.target());
+  return true;
+}
+
+// static
+bool StructTraits<media::mojom::VariableBitrateDataView, media::Bitrate>::Read(
+    media::mojom::VariableBitrateDataView input,
+    media::Bitrate* output) {
+  if (input.target() > input.peak())
+    return false;
+  if (input.peak() == 0u)
+    return false;
+  *output = media::Bitrate::VariableBitrate(input.target(), input.peak());
+  return true;
+}
+
+// static
+media::mojom::BitrateDataView::Tag
+UnionTraits<media::mojom::BitrateDataView, media::Bitrate>::GetTag(
+    const media::Bitrate& input) {
+  switch (input.mode()) {
     case media::Bitrate::Mode::kConstant:
-      return media::mojom::Bitrate_Mode::kConstant;
+      return media::mojom::BitrateDataView::Tag::kConstant;
     case media::Bitrate::Mode::kVariable:
-      return media::mojom::Bitrate_Mode::kVariable;
+      return media::mojom::BitrateDataView::Tag::kVariable;
   }
   NOTREACHED();
-  return media::mojom::Bitrate_Mode::kConstant;
+  return media::mojom::BitrateDataView::Tag::kConstant;
 }
 
 // static
-bool EnumTraits<media::mojom::Bitrate_Mode, media::Bitrate::Mode>::FromMojom(
-    media::mojom::Bitrate_Mode input,
-    media::Bitrate::Mode* output) {
-  switch (input) {
-    case media::mojom::Bitrate_Mode::kConstant:
-      *output = media::Bitrate::Mode::kConstant;
-      return true;
-    case media::mojom::Bitrate_Mode::kVariable:
-      *output = media::Bitrate::Mode::kVariable;
-      return true;
-  }
-  NOTREACHED();
-  return false;
-}
-
-// static
-bool StructTraits<media::mojom::BitrateDataView, media::Bitrate>::Read(
+bool UnionTraits<media::mojom::BitrateDataView, media::Bitrate>::Read(
     media::mojom::BitrateDataView input,
     media::Bitrate* output) {
-  switch (input.mode()) {
-    case media::mojom::Bitrate_Mode::kConstant:
-      if (input.peak() != 0u)
-        return false;
-      *output = media::Bitrate::ConstantBitrate(input.target());
-      return true;
-    case media::mojom::Bitrate_Mode::kVariable:
-      *output = media::Bitrate::VariableBitrate(input.target(), input.peak());
-      return true;
+  switch (input.tag()) {
+    case media::mojom::BitrateDataView::Tag::kConstant:
+      return input.ReadConstant(output);
+    case media::mojom::BitrateDataView::Tag::kVariable:
+      return input.ReadVariable(output);
   }
 
   NOTREACHED();
@@ -397,10 +397,8 @@
   media::Bitrate bitrate;
   if (!input.ReadBitrate(&bitrate))
     return false;
-  if (bitrate.mode() == media::Bitrate::Mode::kConstant &&
-      bitrate.peak() != 0u) {
-    return false;
-  }
+  DCHECK((bitrate.mode() == media::Bitrate::Mode::kConstant) ==
+         (bitrate.peak() == 0u));
 
   absl::optional<uint32_t> initial_framerate;
   if (input.has_initial_framerate())
diff --git a/media/mojo/mojom/video_encode_accelerator_mojom_traits.h b/media/mojo/mojom/video_encode_accelerator_mojom_traits.h
index cefc974..a2b7662 100644
--- a/media/mojo/mojom/video_encode_accelerator_mojom_traits.h
+++ b/media/mojo/mojom/video_encode_accelerator_mojom_traits.h
@@ -10,6 +10,7 @@
 #include "media/mojo/mojom/media_types.mojom-shared.h"
 #include "media/mojo/mojom/video_encode_accelerator.mojom-shared.h"
 #include "media/video/video_encode_accelerator.h"
+#include "mojo/public/cpp/bindings/union_traits.h"
 #include "ui/gfx/geometry/mojom/geometry_mojom_traits.h"
 
 namespace mojo {
@@ -324,23 +325,26 @@
 };
 
 template <>
-struct EnumTraits<media::mojom::Bitrate_Mode, media::Bitrate::Mode> {
-  static media::mojom::Bitrate_Mode ToMojom(media::Bitrate::Mode input);
+struct StructTraits<media::mojom::ConstantBitrateDataView, media::Bitrate> {
+  static uint32_t target(const media::Bitrate& input) { return input.target(); }
 
-  static bool FromMojom(media::mojom::Bitrate_Mode,
-                        media::Bitrate::Mode* output);
+  static bool Read(media::mojom::ConstantBitrateDataView input,
+                   media::Bitrate* output);
 };
 
 template <>
-struct StructTraits<media::mojom::BitrateDataView, media::Bitrate> {
-  static media::Bitrate::Mode mode(const media::Bitrate& input) {
-    return input.mode();
-  }
-
+struct StructTraits<media::mojom::VariableBitrateDataView, media::Bitrate> {
   static uint32_t target(const media::Bitrate& input) { return input.target(); }
-
   static uint32_t peak(const media::Bitrate& input) { return input.peak(); }
+  static bool Read(media::mojom::VariableBitrateDataView input,
+                   media::Bitrate* output);
+};
 
+template <>
+struct UnionTraits<media::mojom::BitrateDataView, media::Bitrate> {
+  static media::mojom::BitrateDataView::Tag GetTag(const media::Bitrate& input);
+  static media::Bitrate constant(const media::Bitrate& input) { return input; }
+  static media::Bitrate variable(const media::Bitrate& input) { return input; }
   static bool Read(media::mojom::BitrateDataView input, media::Bitrate* output);
 };
 
diff --git a/media/mojo/mojom/video_encode_accelerator_mojom_traits_unittest.cc b/media/mojo/mojom/video_encode_accelerator_mojom_traits_unittest.cc
index 55d839d..37c9bb1 100644
--- a/media/mojo/mojom/video_encode_accelerator_mojom_traits_unittest.cc
+++ b/media/mojo/mojom/video_encode_accelerator_mojom_traits_unittest.cc
@@ -12,6 +12,7 @@
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace media {
+
 TEST(VideoEncodeAcceleratorSupportedProfile, RoundTrip) {
   ::media::VideoEncodeAccelerator::SupportedProfile input;
   input.profile = VP9PROFILE_PROFILE0;
@@ -116,6 +117,30 @@
   EXPECT_EQ(input_config, output_config);
 }
 
+TEST(VariableBitrateStructTraitTest, PeakZeroBps_Rejected) {
+  mojom::VariableBitratePtr mojom_variable_bitrate =
+      mojom::VariableBitrate::New();
+  mojom_variable_bitrate->target = 0u;
+  mojom_variable_bitrate->peak = 0u;
+  Bitrate output;
+
+  bool result = mojo::test::SerializeAndDeserialize<mojom::VariableBitrate>(
+      mojom_variable_bitrate, output);
+  EXPECT_FALSE(result);
+}
+
+TEST(VariableBitrateStructTraitTest, PeakLessThanTarget_Rejected) {
+  mojom::VariableBitratePtr mojom_variable_bitrate =
+      mojom::VariableBitrate::New();
+  mojom_variable_bitrate->target = 6000u;
+  mojom_variable_bitrate->peak = 5999u;
+  Bitrate output;
+
+  bool result = mojo::test::SerializeAndDeserialize<mojom::VariableBitrate>(
+      mojom_variable_bitrate, output);
+  EXPECT_FALSE(result);
+}
+
 TEST(BitstreamBufferMetadataTraitTest, RoundTrip) {
   ::media::BitstreamBufferMetadata input_metadata;
   input_metadata.payload_size_bytes = 1234;
diff --git a/storage/browser/blob/blob_registry_impl.cc b/storage/browser/blob/blob_registry_impl.cc
index 3d565c5..62b1b200 100644
--- a/storage/browser/blob/blob_registry_impl.cc
+++ b/storage/browser/blob/blob_registry_impl.cc
@@ -632,6 +632,7 @@
     return;
   }
   if (!context_->registry().HasEntry(uuid)) {
+    LOG(ERROR) << "Invalid UUID: " << uuid;
     // TODO(mek): Log histogram, old code logs Storage.Blob.InvalidReference
     std::move(callback).Run();
     return;
diff --git a/testing/buildbot/chromium.android.fyi.json b/testing/buildbot/chromium.android.fyi.json
index 30d9b47..b571f0b 100644
--- a/testing/buildbot/chromium.android.fyi.json
+++ b/testing/buildbot/chromium.android.fyi.json
@@ -5230,7 +5230,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M100",
-              "revision": "version:100.0.4896.12"
+              "revision": "version:100.0.4896.13"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
@@ -5398,7 +5398,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M99",
-              "revision": "version:99.0.4844.46"
+              "revision": "version:99.0.4844.47"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
@@ -5482,7 +5482,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M100",
-              "revision": "version:100.0.4896.12"
+              "revision": "version:100.0.4896.13"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
@@ -5650,7 +5650,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M99",
-              "revision": "version:99.0.4844.46"
+              "revision": "version:99.0.4844.47"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
diff --git a/testing/buildbot/chromium.android.json b/testing/buildbot/chromium.android.json
index bc5d87b..be95459a 100644
--- a/testing/buildbot/chromium.android.json
+++ b/testing/buildbot/chromium.android.json
@@ -41650,7 +41650,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M100",
-              "revision": "version:100.0.4896.12"
+              "revision": "version:100.0.4896.13"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
@@ -41818,7 +41818,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M99",
-              "revision": "version:99.0.4844.46"
+              "revision": "version:99.0.4844.47"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
@@ -41902,7 +41902,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M100",
-              "revision": "version:100.0.4896.12"
+              "revision": "version:100.0.4896.13"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
@@ -42070,7 +42070,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M99",
-              "revision": "version:99.0.4844.46"
+              "revision": "version:99.0.4844.47"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
@@ -42158,7 +42158,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M100",
-              "revision": "version:100.0.4896.12"
+              "revision": "version:100.0.4896.13"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
@@ -42326,7 +42326,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M99",
-              "revision": "version:99.0.4844.46"
+              "revision": "version:99.0.4844.47"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
@@ -42410,7 +42410,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M100",
-              "revision": "version:100.0.4896.12"
+              "revision": "version:100.0.4896.13"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
@@ -42578,7 +42578,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M99",
-              "revision": "version:99.0.4844.46"
+              "revision": "version:99.0.4844.47"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
@@ -42733,7 +42733,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M100",
-              "revision": "version:100.0.4896.12"
+              "revision": "version:100.0.4896.13"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
@@ -42901,7 +42901,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M99",
-              "revision": "version:99.0.4844.46"
+              "revision": "version:99.0.4844.47"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
@@ -42985,7 +42985,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M100",
-              "revision": "version:100.0.4896.12"
+              "revision": "version:100.0.4896.13"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
@@ -43153,7 +43153,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M99",
-              "revision": "version:99.0.4844.46"
+              "revision": "version:99.0.4844.47"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
@@ -43308,7 +43308,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M100",
-              "revision": "version:100.0.4896.12"
+              "revision": "version:100.0.4896.13"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
@@ -43476,7 +43476,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M99",
-              "revision": "version:99.0.4844.46"
+              "revision": "version:99.0.4844.47"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
@@ -43560,7 +43560,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M100",
-              "revision": "version:100.0.4896.12"
+              "revision": "version:100.0.4896.13"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
@@ -43728,7 +43728,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M99",
-              "revision": "version:99.0.4844.46"
+              "revision": "version:99.0.4844.47"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
diff --git a/testing/buildbot/chromium.perf.json b/testing/buildbot/chromium.perf.json
index f9a3662..e257795 100644
--- a/testing/buildbot/chromium.perf.json
+++ b/testing/buildbot/chromium.perf.json
@@ -1617,7 +1617,7 @@
           "ignore_task_failure": false,
           "io_timeout": 21600,
           "service_account": "chrome-tester@chops-service-accounts.iam.gserviceaccount.com",
-          "shards": 2
+          "shards": 5
         },
         "trigger_script": {
           "args": [
diff --git a/testing/buildbot/variants.pyl b/testing/buildbot/variants.pyl
index 99fe9bfa..b734e98 100644
--- a/testing/buildbot/variants.pyl
+++ b/testing/buildbot/variants.pyl
@@ -387,7 +387,7 @@
         {
           'cipd_package': 'chromium/testing/weblayer-x86',
           'location': 'weblayer_instrumentation_test_M100',
-          'revision': 'version:100.0.4896.12',
+          'revision': 'version:100.0.4896.13',
         }
       ],
     },
@@ -411,7 +411,7 @@
         {
           'cipd_package': 'chromium/testing/weblayer-x86',
           'location': 'weblayer_instrumentation_test_M99',
-          'revision': 'version:99.0.4844.46',
+          'revision': 'version:99.0.4844.47',
         }
       ],
     },
@@ -459,7 +459,7 @@
         {
           'cipd_package': 'chromium/testing/weblayer-x86',
           'location': 'weblayer_instrumentation_test_M100',
-          'revision': 'version:100.0.4896.12',
+          'revision': 'version:100.0.4896.13',
         }
       ],
     },
@@ -483,7 +483,7 @@
         {
           'cipd_package': 'chromium/testing/weblayer-x86',
           'location': 'weblayer_instrumentation_test_M99',
-          'revision': 'version:99.0.4844.46',
+          'revision': 'version:99.0.4844.47',
         }
       ],
     },
@@ -531,7 +531,7 @@
         {
           'cipd_package': 'chromium/testing/weblayer-x86',
           'location': 'weblayer_instrumentation_test_M100',
-          'revision': 'version:100.0.4896.12',
+          'revision': 'version:100.0.4896.13',
         }
       ],
     },
@@ -555,7 +555,7 @@
         {
           'cipd_package': 'chromium/testing/weblayer-x86',
           'location': 'weblayer_instrumentation_test_M99',
-          'revision': 'version:99.0.4844.46',
+          'revision': 'version:99.0.4844.47',
         }
       ],
     },
diff --git a/third_party/blink/public/devtools_protocol/browser_protocol.pdl b/third_party/blink/public/devtools_protocol/browser_protocol.pdl
index 1368ddd..06d5fcb 100644
--- a/third_party/blink/public/devtools_protocol/browser_protocol.pdl
+++ b/third_party/blink/public/devtools_protocol/browser_protocol.pdl
@@ -8207,6 +8207,11 @@
       BackForwardCacheNotRestoredReasonType type
       # Not restored reason
       BackForwardCacheNotRestoredReason reason
+      # Context associated with the reason. The meaning of this context is
+      # dependent on the reason:
+      # - EmbedderExtensionSentMessageToCachedFrame: the extension ID.
+      #
+      optional string context
 
   experimental type BackForwardCacheNotRestoredExplanationTree extends object
     properties
diff --git a/third_party/blink/public/mojom/interest_group/interest_group_types.mojom b/third_party/blink/public/mojom/interest_group/interest_group_types.mojom
index c00359b..b7c27f9 100644
--- a/third_party/blink/public/mojom/interest_group/interest_group_types.mojom
+++ b/third_party/blink/public/mojom/interest_group/interest_group_types.mojom
@@ -61,17 +61,19 @@
   // Opaque JSON data, passed as object to auction worklet.
   string? seller_signals;
 
+  // The value restricts the runtime of the seller's scoreAd() script.
+  mojo_base.mojom.TimeDelta? seller_timeout;
+
   // Keys of `per_buyer_signals` must be valid HTTPS origins. Value is opaque
   // JSON data, passed as object to auction worklet.
   map<url.mojom.Origin, string>? per_buyer_signals;
 
   // Keys of `per_buyer_timeouts` must be valid HTTPS origins, or '*'. Values
-  // restrict the runtime (in milliseconds) of particular buyer's generateBid()
-  // scripts.
+  // restrict the runtime of particular buyer's generateBid() scripts.
   map<url.mojom.Origin, mojo_base.mojom.TimeDelta>? per_buyer_timeouts;
 
-  // The value restricts generateBid() script's runtime (in milliseconds) of all
-  // buyers with unspecified timeouts, if not Null.
+  // The value restricts generateBid() script's runtime of all buyers with
+  // unspecified timeouts, if not Null.
   mojo_base.mojom.TimeDelta? all_buyers_timeout;
 };
 
diff --git a/third_party/blink/renderer/core/clipboard/data_transfer.cc b/third_party/blink/renderer/core/clipboard/data_transfer.cc
index 22b22b1..f34f4f1 100644
--- a/third_party/blink/renderer/core/clipboard/data_transfer.cc
+++ b/third_party/blink/renderer/core/clipboard/data_transfer.cc
@@ -77,6 +77,9 @@
                                 gfx::Vector2dF& paint_offset,
                                 gfx::RectF& bounding_box,
                                 PropertyTreeState& property_tree_state) {
+  if (!frame->View()->IsAttached() && !frame->IsLocalRoot())
+    return false;
+
   // Construct layout object for |node_| with pseudo class "-webkit-drag"
   if (update_lifecycle) {
     frame->View()->UpdateAllLifecyclePhasesExceptPaint(
diff --git a/third_party/blink/renderer/core/clipboard/data_transfer_test.cc b/third_party/blink/renderer/core/clipboard/data_transfer_test.cc
index 86d12bc..0bd84f5 100644
--- a/third_party/blink/renderer/core/clipboard/data_transfer_test.cc
+++ b/third_party/blink/renderer/core/clipboard/data_transfer_test.cc
@@ -18,6 +18,10 @@
 namespace blink {
 
 class DataTransferTest : public PaintTestConfigurations, public RenderingTest {
+ public:
+  DataTransferTest()
+      : RenderingTest(MakeGarbageCollected<SingleChildLocalFrameClient>()) {}
+
  protected:
   Page& GetPage() const { return *GetDocument().GetPage(); }
   LocalFrame& GetFrame() const { return *GetDocument().GetFrame(); }
@@ -429,4 +433,34 @@
   }
 }
 
+TEST_P(DataTransferTest, CreateBitmapAttachedFrame) {
+  SetBodyInnerHTML(R"HTML(
+      <iframe id="frame"</iframe>">
+  )HTML");
+  SetChildFrameHTML("");
+
+  LocalFrameView* frame_view = GetDocument().View();
+  frame_view->UpdateAllLifecyclePhasesForTest();
+
+  SkBitmap bitmap;
+  EXPECT_TRUE(DataTransfer::CreateBitmapFromNode(
+      &GetFrame(), GetDocument().getElementById("frame"), gfx::Size(), bitmap));
+  EXPECT_FALSE(bitmap.empty());
+}
+
+TEST_P(DataTransferTest, CreateBitmapDetachedFrame) {
+  SetBodyInnerHTML(R"HTML(
+      <iframe id="frame" style="display:none"</iframe>">
+  )HTML");
+  SetChildFrameHTML("");
+
+  LocalFrameView* frame_view = GetDocument().View();
+  frame_view->UpdateAllLifecyclePhasesForTest();
+
+  SkBitmap bitmap;
+  EXPECT_FALSE(DataTransfer::CreateBitmapFromNode(
+      &GetFrame(), GetDocument().getElementById("frame"), gfx::Size(), bitmap));
+  EXPECT_TRUE(bitmap.empty());
+}
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/editing/commands/editing_commands_utilities.cc b/third_party/blink/renderer/core/editing/commands/editing_commands_utilities.cc
index a7baceb..81f94f04 100644
--- a/third_party/blink/renderer/core/editing/commands/editing_commands_utilities.cc
+++ b/third_party/blink/renderer/core/editing/commands/editing_commands_utilities.cc
@@ -29,6 +29,7 @@
 
 #include "third_party/blink/renderer/core/editing/commands/editing_commands_utilities.h"
 
+#include "third_party/blink/public/web/web_local_frame_client.h"
 #include "third_party/blink/renderer/core/dom/node_computed_style.h"
 #include "third_party/blink/renderer/core/dom/text.h"
 #include "third_party/blink/renderer/core/editing/commands/selection_for_undo_step.h"
diff --git a/third_party/blink/renderer/core/editing/ime/edit_context.cc b/third_party/blink/renderer/core/editing/ime/edit_context.cc
index 8055d23..1cd62a36 100644
--- a/third_party/blink/renderer/core/editing/ime/edit_context.cc
+++ b/third_party/blink/renderer/core/editing/ime/edit_context.cc
@@ -4,6 +4,7 @@
 
 #include "third_party/blink/renderer/core/editing/ime/edit_context.h"
 
+#include "base/trace_event/trace_event.h"
 #include "third_party/blink/public/platform/web_string.h"
 #include "third_party/blink/public/platform/web_vector.h"
 #include "third_party/blink/public/web/web_range.h"
@@ -188,6 +189,8 @@
 }
 
 void EditContext::Focus() {
+  TRACE_EVENT0("ime", "EditContext::Focus");
+
   EditContext* current_active_edit_context =
       GetInputMethodController().GetActiveEditContext();
   if (current_active_edit_context && current_active_edit_context != this) {
@@ -200,6 +203,8 @@
 }
 
 void EditContext::Blur() {
+  TRACE_EVENT0("ime", "EditContext::Blur");
+
   if (GetInputMethodController().GetActiveEditContext() != this)
     return;
   // Clean up the state of the |this| EditContext.
@@ -210,6 +215,9 @@
 void EditContext::updateSelection(uint32_t start,
                                   uint32_t end,
                                   ExceptionState& exception_state) {
+  TRACE_EVENT2("ime", "EditContext::updateSelection", "start",
+               std::to_string(start), "end", std::to_string(end));
+
   // Following this spec:
   // https://html.spec.whatwg.org/C/#dom-textarea/input-setselectionrange
   if (start > end) {
@@ -240,18 +248,29 @@
     HeapVector<Member<DOMRect>>& character_bounds) {
   character_bounds_range_start_ = static_cast<uint32_t>(range_start);
 
+  TRACE_EVENT1("ime", "EditContext::updateCharacterBounds", "range_start, size",
+               std::to_string(range_start) + ", " +
+                   std::to_string(character_bounds.size()));
+
   character_bounds_.Clear();
   std::for_each(character_bounds.begin(), character_bounds.end(),
-                [this](const auto& bound) {
-                  character_bounds_.push_back(bound->ToEnclosingRect());
+                [this](const auto& bounds) {
+                  auto result_bounds = bounds->ToEnclosingRect();
+                  TRACE_EVENT1("ime", "EditContext::updateCharacterBounds",
+                               "charBounds", result_bounds.ToString());
+                  character_bounds_.push_back(result_bounds);
                 });
 }
 
 void EditContext::updateControlBounds(DOMRect* control_bounds) {
+  TRACE_EVENT1("ime", "EditContext::updateControlBounds", "control_bounds",
+               control_bounds->ToEnclosingRect().ToString());
   control_bounds_ = control_bounds->ToEnclosingRect();
 }
 
 void EditContext::updateSelectionBounds(DOMRect* selection_bounds) {
+  TRACE_EVENT1("ime", "EditContext::updateSelectionBounds", "selection_bounds",
+               selection_bounds->ToEnclosingRect().ToString());
   selection_bounds_ = selection_bounds->ToEnclosingRect();
 }
 
@@ -259,6 +278,10 @@
                              uint32_t end,
                              const String& new_text,
                              ExceptionState& exception_state) {
+  TRACE_EVENT2("ime", "EditContext::updateText", "start, end",
+               std::to_string(start) + ", " + std::to_string(end), "new_text",
+               new_text);
+
   // Following this spec:
   // https://html.spec.whatwg.org/C/#dom-textarea/input-setrangetext
   if (start > end) {
@@ -279,6 +302,7 @@
 }
 
 void EditContext::setText(const String& text) {
+  TRACE_EVENT1("ime", "EditContext::setText", "text", text);
   text_ = text;
 }
 
@@ -288,6 +312,8 @@
 
 void EditContext::setSelectionStart(uint32_t selection_start,
                                     ExceptionState& exception_state) {
+  TRACE_EVENT1("ime", "EditContext::setSelectionStart", "start",
+               std::to_string(selection_start));
   // Following this spec:
   // https://html.spec.whatwg.org/C/#dom-textarea/input-setselectionrange
   selection_start_ = std::min(selection_end_, selection_start);
@@ -303,6 +329,9 @@
 
 void EditContext::setSelectionEnd(uint32_t selection_end,
                                   ExceptionState& exception_state) {
+  TRACE_EVENT1("ime", "EditContext::setSelectionEnd", "end",
+               std::to_string(selection_end));
+
   // Following this spec:
   // https://html.spec.whatwg.org/C/#dom-textarea/input-setselectionrange
   selection_end_ = std::min(selection_end, text_.length());
@@ -429,6 +458,10 @@
       control_bounds_, DomWindow()->GetFrame()->DevicePixelRatio());
   *selection_bounds = gfx::ScaleToEnclosingRect(
       selection_bounds_, DomWindow()->GetFrame()->DevicePixelRatio());
+
+  TRACE_EVENT2("ime", "EditContext::GetLayoutBounds", "control",
+               control_bounds->ToString(), "selection",
+               selection_bounds->ToString());
 }
 
 bool EditContext::SetComposition(
@@ -437,6 +470,11 @@
     const WebRange& replacement_range,
     int selection_start,
     int selection_end) {
+  TRACE_EVENT2(
+      "ime", "EditContext::SetComposition", "start, end",
+      std::to_string(selection_start) + ", " + std::to_string(selection_end),
+      "text", text.Utf8());
+
   if (!text.IsEmpty() && !has_composition_) {
     if (!DispatchCompositionStartEvent(text))
       return false;
@@ -473,6 +511,11 @@
     int composition_start,
     int composition_end,
     const WebVector<ui::ImeTextSpan>& ime_text_spans) {
+  TRACE_EVENT1("ime", "EditContext::SetCompositionFromExistingText",
+               "start, end",
+               std::to_string(composition_start) + ", " +
+                   std::to_string(composition_end));
+
   if (composition_start < 0 || composition_end < 0)
     return false;
 
@@ -507,6 +550,8 @@
 }
 
 bool EditContext::InsertText(const WebString& text) {
+  TRACE_EVENT1("ime", "EditContext::InsertText", "text", text.Utf8());
+
   String update_text(text);
   text_ = text_.Substring(0, selection_start_) + update_text +
           text_.Substring(selection_end_);
@@ -588,6 +633,12 @@
                              const WebVector<ui::ImeTextSpan>& ime_text_spans,
                              const WebRange& replacement_range,
                              int relative_caret_position) {
+  TRACE_EVENT2("ime", "EditContext::CommitText", "range, ralative_caret",
+               "(" + std::to_string(replacement_range.StartOffset()) + "," +
+                   std::to_string(replacement_range.EndOffset()) + ")" + ", " +
+                   std::to_string(relative_caret_position),
+               "text", text.Utf8());
+
   // Fire textupdate and textformatupdate events to JS.
   // ime_text_spans can have multiple format updates so loop through and fire
   // events accordingly.
@@ -628,6 +679,8 @@
 
 bool EditContext::FinishComposingText(
     ConfirmCompositionBehavior selection_behavior) {
+  TRACE_EVENT0("ime", "EditContext::FinishComposingText");
+
   String text;
   if (has_composition_) {
     text = text_.Substring(composition_range_start_, composition_range_end_);
@@ -648,6 +701,8 @@
 }
 
 void EditContext::ExtendSelectionAndDelete(int before, int after) {
+  TRACE_EVENT1("ime", "EditContext::ExtendSelectionAndDelete", "before, afters",
+               std::to_string(before) + ", " + std::to_string(after));
   String text;
   before = std::min(before, static_cast<int>(selection_start_));
   after = std::min(after, static_cast<int>(text_.length()));
@@ -761,14 +816,20 @@
   if (static_cast<int>(character_bounds_.size()) != composition_range.length())
     return false;
 
+  TRACE_EVENT1("ime", "EditContext::GetCompositionCharacterBounds", "size",
+               std::to_string(character_bounds_.size()));
+
   bounds.Clear();
   std::for_each(
       character_bounds_.begin(), character_bounds_.end(),
       [&bounds, this](auto& bound_in_css_pixels) {
         // EditContext's coordinates are in CSS pixels, which need to be
         // converted to physical pixels before return.
-        bounds.push_back(gfx::ScaleToEnclosingRect(
-            bound_in_css_pixels, DomWindow()->GetFrame()->DevicePixelRatio()));
+        auto result_bounds = gfx::ScaleToEnclosingRect(
+            bound_in_css_pixels, DomWindow()->GetFrame()->DevicePixelRatio());
+        bounds.push_back(result_bounds);
+        TRACE_EVENT1("ime", "EditContext::GetCompositionCharacterBounds",
+                     "charBounds", result_bounds.ToString());
       });
 
   return true;
diff --git a/third_party/blink/renderer/core/editing/selection_modifier_test.cc b/third_party/blink/renderer/core/editing/selection_modifier_test.cc
index 6ac26dae..01e594c 100644
--- a/third_party/blink/renderer/core/editing/selection_modifier_test.cc
+++ b/third_party/blink/renderer/core/editing/selection_modifier_test.cc
@@ -49,6 +49,39 @@
 }
 
 TEST_F(SelectionModifierTest, MoveByLineBlockInInline) {
+  // TODO(crbug.com/1300781): This test does not work as expected when `<b>` is
+  // not culled.
+  if (RuntimeEnabledFeatures::LayoutNGBlockInInlineEnabled())
+    return;
+
+  LoadAhem();
+  InsertStyleElement(
+      "div {"
+      "font: 10px/20px Ahem;"
+      "padding: 10px;"
+      "writing-mode: horizontal-tb;"
+      "}"
+      "b { background: orange; }");
+  const SelectionInDOMTree selection =
+      SetSelectionTextToBody("<div>ab|c<b><p>ABC</p><p>DEF</p>def</b></div>");
+  SelectionModifier modifier(GetFrame(), selection);
+
+  EXPECT_EQ("<div>abc<b><p>AB|C</p><p>DEF</p>def</b></div>",
+            MoveForwardByLine(modifier));
+  EXPECT_EQ("<div>abc<b><p>ABC</p><p>DE|F</p>def</b></div>",
+            MoveForwardByLine(modifier));
+  EXPECT_EQ("<div>abc<b><p>ABC</p><p>DEF</p>de|f</b></div>",
+            MoveForwardByLine(modifier));
+
+  EXPECT_EQ("<div>abc<b><p>ABC</p><p>DE|F</p>def</b></div>",
+            MoveBackwardByLine(modifier));
+  EXPECT_EQ("<div>abc<b><p>AB|C</p><p>DEF</p>def</b></div>",
+            MoveBackwardByLine(modifier));
+  EXPECT_EQ("<div>ab|c<b><p>ABC</p><p>DEF</p>def</b></div>",
+            MoveBackwardByLine(modifier));
+}
+
+TEST_F(SelectionModifierTest, MoveByLineBlockInInlineCulled) {
   LoadAhem();
   InsertStyleElement(
       "div {"
diff --git a/third_party/blink/renderer/core/fetch/fetch_data_loader.cc b/third_party/blink/renderer/core/fetch/fetch_data_loader.cc
index b127c46..d03eacf 100644
--- a/third_party/blink/renderer/core/fetch/fetch_data_loader.cc
+++ b/third_party/blink/renderer/core/fetch/fetch_data_loader.cc
@@ -70,7 +70,11 @@
     data_pipe_loader_->Start(consumer_, this);
   }
 
-  void Cancel() override { consumer_->Cancel(); }
+  void Cancel() override {
+    load_canceled_ = true;
+    blob_handle_.reset();
+    consumer_->Cancel();
+  }
 
   void DidFetchDataStartedDataPipe(
       mojo::ScopedDataPipeConsumerHandle handle) override {
@@ -105,6 +109,8 @@
  private:
   void FinishedCreatingFromDataPipe(
       const scoped_refptr<BlobDataHandle>& blob_handle) {
+    if (load_canceled_)
+      return;
     if (!blob_handle) {
       DidFetchDataLoadFailed();
       return;
@@ -124,6 +130,7 @@
   const scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
   scoped_refptr<BlobDataHandle> blob_handle_;
   bool load_complete_ = false;
+  bool load_canceled_ = false;
 };
 
 class FetchDataLoaderAsArrayBuffer final : public FetchDataLoader,
diff --git a/third_party/blink/renderer/core/fetch/fetch_data_loader_test.cc b/third_party/blink/renderer/core/fetch/fetch_data_loader_test.cc
index 8aac2ca..026b1b2 100644
--- a/third_party/blink/renderer/core/fetch/fetch_data_loader_test.cc
+++ b/third_party/blink/renderer/core/fetch/fetch_data_loader_test.cc
@@ -279,6 +279,70 @@
   checkpoint.Call(4);
 }
 
+TEST_F(FetchDataLoaderBlobTest, LoadAsBlobNoClientCallbacksAfterCancel) {
+  Checkpoint checkpoint;
+  BytesConsumer::Client* client = nullptr;
+  auto* consumer = MakeGarbageCollected<MockBytesConsumer>();
+
+  FetchDataLoader* fetch_data_loader =
+      FetchDataLoader::CreateLoaderAsBlobHandle("text/test", fake_task_runner_);
+  auto* fetch_data_loader_client =
+      MakeGarbageCollected<MockFetchDataLoaderClient>();
+  scoped_refptr<BlobDataHandle> blob_data_handle;
+
+  base::RunLoop run_loop;
+
+  InSequence s;
+  EXPECT_CALL(checkpoint, Call(1));
+  EXPECT_CALL(*consumer,
+              DrainAsBlobDataHandle(
+                  BytesConsumer::BlobSizePolicy::kDisallowBlobWithInvalidSize))
+      .WillOnce(Return(ByMove(nullptr)));
+  EXPECT_CALL(*consumer, SetClient(_)).WillOnce(SaveArg<0>(&client));
+  EXPECT_CALL(*consumer, DrainAsDataPipe());
+  EXPECT_CALL(*consumer, GetPublicState())
+      .WillOnce(Return(BytesConsumer::PublicState::kReadableOrWaiting));
+  EXPECT_CALL(checkpoint, Call(2));
+  EXPECT_CALL(*consumer, BeginRead(_, _))
+      .WillOnce(DoAll(SetArgPointee<0>(nullptr), SetArgPointee<1>(0),
+                      Return(Result::kShouldWait)));
+  EXPECT_CALL(checkpoint, Call(3));
+  EXPECT_CALL(*consumer, BeginRead(_, _))
+      .WillOnce(DoAll(SetArgPointee<0>(kQuickBrownFox),
+                      SetArgPointee<1>(kQuickBrownFoxLengthWithTerminatingNull),
+                      Return(Result::kOk)));
+  EXPECT_CALL(*consumer, EndRead(kQuickBrownFoxLengthWithTerminatingNull))
+      .WillOnce(Return(Result::kOk));
+  EXPECT_CALL(*consumer, BeginRead(_, _))
+      .WillOnce(DoAll(SetArgPointee<0>(nullptr), SetArgPointee<1>(0),
+                      Return(Result::kShouldWait)));
+  EXPECT_CALL(checkpoint, Call(4));
+  EXPECT_CALL(*consumer, Cancel());
+  EXPECT_CALL(checkpoint, Call(5));
+  EXPECT_CALL(*consumer, BeginRead(_, _)).WillOnce(Return(Result::kDone));
+  EXPECT_CALL(*consumer, Cancel());
+  // This should never happen due to explicit FetchDataLoader::Cancel call.
+  EXPECT_CALL(*fetch_data_loader_client, DidFetchDataLoadedBlobHandleMock(_))
+      .Times(0);
+  EXPECT_CALL(checkpoint, Call(6));
+
+  checkpoint.Call(1);
+  fetch_data_loader->Start(consumer, fetch_data_loader_client);
+  checkpoint.Call(2);
+  fake_task_runner_->RunUntilIdle();
+  checkpoint.Call(3);
+  client->OnStateChange();
+  run_loop.RunUntilIdle();
+  checkpoint.Call(4);
+  // Cancel the load to verify no FetchDataLoader::Client calls happen
+  // afterwards.
+  fetch_data_loader->Cancel();
+  checkpoint.Call(5);
+  client->OnStateChange();
+  run_loop.RunUntilIdle();
+  checkpoint.Call(6);
+}
+
 TEST_F(FetchDataLoaderBlobTest,
        LoadAsBlobViaDrainAsBlobDataHandleWithSameContentType) {
   auto blob_data = std::make_unique<BlobData>();
diff --git a/third_party/blink/renderer/core/frame/local_frame_client.h b/third_party/blink/renderer/core/frame/local_frame_client.h
index 386fb615..3e36e5e7 100644
--- a/third_party/blink/renderer/core/frame/local_frame_client.h
+++ b/third_party/blink/renderer/core/frame/local_frame_client.h
@@ -40,6 +40,7 @@
 #include "services/network/public/mojom/web_sandbox_flags.mojom-blink-forward.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 #include "third_party/blink/public/common/loader/loading_behavior_flag.h"
+#include "third_party/blink/public/common/loader/url_loader_factory_bundle.h"
 #include "third_party/blink/public/common/permissions_policy/document_policy_features.h"
 #include "third_party/blink/public/common/permissions_policy/permissions_policy.h"
 #include "third_party/blink/public/common/responsiveness_metrics/user_interaction_latency.h"
@@ -57,7 +58,6 @@
 #include "third_party/blink/public/platform/web_worker_fetch_context.h"
 #include "third_party/blink/public/web/web_frame_load_type.h"
 #include "third_party/blink/public/web/web_history_commit_type.h"
-#include "third_party/blink/public/web/web_local_frame_client.h"
 #include "third_party/blink/public/web/web_manifest_manager.h"
 #include "third_party/blink/public/web/web_navigation_params.h"
 #include "third_party/blink/renderer/core/core_export.h"
@@ -80,6 +80,7 @@
 namespace blink {
 
 class AssociatedInterfaceProvider;
+class BrowserInterfaceBrokerProxy;
 class DocumentLoader;
 class HTMLFencedFrameElement;
 class HTMLFormElement;
@@ -91,7 +92,6 @@
 class KURL;
 class LocalDOMWindow;
 class LocalFrame;
-class WebPluginContainerImpl;
 class RemoteFrame;
 class ResourceError;
 class ResourceRequest;
@@ -103,11 +103,14 @@
 class WebMediaPlayer;
 class WebMediaPlayerClient;
 class WebMediaPlayerSource;
+class WebPluginContainerImpl;
 class WebRemotePlaybackClient;
 class WebServiceWorkerProvider;
 class WebSpellCheckPanelHostClient;
 class WebTextCheckClient;
 class ResourceLoadInfoNotifierWrapper;
+enum class SyncCondition;
+struct MobileFriendliness;
 
 class CORE_EXPORT LocalFrameClient : public FrameClient {
  public:
diff --git a/third_party/blink/renderer/core/layout/flexible_box_algorithm.cc b/third_party/blink/renderer/core/layout/flexible_box_algorithm.cc
index 2754ad6..5b0acd4 100644
--- a/third_party/blink/renderer/core/layout/flexible_box_algorithm.cc
+++ b/third_party/blink/renderer/core/layout/flexible_box_algorithm.cc
@@ -278,6 +278,7 @@
 
 void FlexItem::Trace(Visitor* visitor) const {
   visitor->Trace(box_);
+  visitor->Trace(ng_input_node_);
   visitor->Trace(layout_result_);
 }
 
@@ -801,7 +802,7 @@
 
 void FlexLayoutAlgorithm::AlignFlexLines(
     LayoutUnit cross_axis_content_extent,
-    Vector<NGFlexLine>* flex_line_outputs) {
+    HeapVector<NGFlexLine>* flex_line_outputs) {
   const StyleContentAlignmentData align_content = ResolvedAlignContent(*style_);
   if (align_content.GetPosition() == ContentPosition::kFlexStart &&
       gap_between_lines_ == 0) {
diff --git a/third_party/blink/renderer/core/layout/flexible_box_algorithm.h b/third_party/blink/renderer/core/layout/flexible_box_algorithm.h
index dddde5c..3b80d92 100644
--- a/third_party/blink/renderer/core/layout/flexible_box_algorithm.h
+++ b/third_party/blink/renderer/core/layout/flexible_box_algorithm.h
@@ -421,7 +421,7 @@
   // FlexItem::desired_position. When lines stretch, also modifies
   // FlexLine::cross_axis_extent.
   void AlignFlexLines(LayoutUnit cross_axis_content_extent,
-                      Vector<NGFlexLine>* flex_line_outputs = nullptr);
+                      HeapVector<NGFlexLine>* flex_line_outputs = nullptr);
 
   // Positions flex items by modifying FlexItem::offset.
   // When lines stretch, also modifies FlexItem::cross_axis_size.
diff --git a/third_party/blink/renderer/core/layout/ng/custom/custom_layout_child.cc b/third_party/blink/renderer/core/layout/ng/custom/custom_layout_child.cc
index dc95b3a..4ff787b 100644
--- a/third_party/blink/renderer/core/layout/ng/custom/custom_layout_child.cc
+++ b/third_party/blink/renderer/core/layout/ng/custom/custom_layout_child.cc
@@ -92,6 +92,7 @@
 }
 
 void CustomLayoutChild::Trace(Visitor* visitor) const {
+  visitor->Trace(node_);
   visitor->Trace(style_map_);
   visitor->Trace(token_);
   ScriptWrappable::Trace(visitor);
diff --git a/third_party/blink/renderer/core/layout/ng/flex/ng_flex_break_token_data.h b/third_party/blink/renderer/core/layout/ng/flex/ng_flex_break_token_data.h
index 878721b4..7ab25ee 100644
--- a/third_party/blink/renderer/core/layout/ng/flex/ng_flex_break_token_data.h
+++ b/third_party/blink/renderer/core/layout/ng/flex/ng_flex_break_token_data.h
@@ -12,7 +12,7 @@
 
 struct NGFlexBreakTokenData final : NGBlockBreakTokenData {
   NGFlexBreakTokenData(const NGBlockBreakTokenData* break_token_data,
-                       const Vector<NGFlexLine>& flex_lines,
+                       const HeapVector<NGFlexLine>& flex_lines,
                        const Vector<EBreakBetween>& row_break_between,
                        LayoutUnit intrinsic_block_size)
       : NGBlockBreakTokenData(kFlexBreakTokenData, break_token_data),
@@ -20,7 +20,12 @@
         row_break_between(row_break_between),
         intrinsic_block_size(intrinsic_block_size) {}
 
-  Vector<NGFlexLine> flex_lines;
+  void Trace(Visitor* visitor) const override {
+    visitor->Trace(flex_lines);
+    NGBlockBreakTokenData::Trace(visitor);
+  }
+
+  HeapVector<NGFlexLine> flex_lines;
   // |row_break_between| is only used in the case of row flex containers.
   Vector<EBreakBetween> row_break_between;
   LayoutUnit intrinsic_block_size;
diff --git a/third_party/blink/renderer/core/layout/ng/flex/ng_flex_child_iterator.cc b/third_party/blink/renderer/core/layout/ng/flex/ng_flex_child_iterator.cc
index 6272d011..d6ebe3c8 100644
--- a/third_party/blink/renderer/core/layout/ng/flex/ng_flex_child_iterator.cc
+++ b/third_party/blink/renderer/core/layout/ng/flex/ng_flex_child_iterator.cc
@@ -31,8 +31,6 @@
                        return c1.order < c2.order;
                      });
   }
-
-  iterator_ = children_.begin();
 }
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/layout/ng/flex/ng_flex_child_iterator.h b/third_party/blink/renderer/core/layout/ng/flex/ng_flex_child_iterator.h
index 0a06152..8d91f6c7 100644
--- a/third_party/blink/renderer/core/layout/ng/flex/ng_flex_child_iterator.h
+++ b/third_party/blink/renderer/core/layout/ng/flex/ng_flex_child_iterator.h
@@ -24,22 +24,27 @@
 
   // Returns the next block node which should be laid out.
   NGBlockNode NextChild() {
-    if (iterator_ == children_.end())
+    DCHECK(position_ <= children_.size());
+    if (position_ == children_.size())
       return nullptr;
-
-    return (*iterator_++).child;
+    return children_[position_++].child;
   }
 
   struct ChildWithOrder {
     DISALLOW_NEW();
+
+   public:
     ChildWithOrder(NGBlockNode child, int order) : child(child), order(order) {}
+    void Trace(Visitor* visitor) const { visitor->Trace(child); }
+
     NGBlockNode child;
     int order;
   };
 
  private:
-  Vector<ChildWithOrder, 4> children_;
-  Vector<ChildWithOrder, 4>::const_iterator iterator_;
+  // |children_| cannot be modified except in ctor;
+  HeapVector<ChildWithOrder, 4> children_;
+  wtf_size_t position_ = 0;
 };
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/layout/ng/flex/ng_flex_item_iterator.cc b/third_party/blink/renderer/core/layout/ng/flex/ng_flex_item_iterator.cc
index df66f10..874d9d1 100644
--- a/third_party/blink/renderer/core/layout/ng/flex/ng_flex_item_iterator.cc
+++ b/third_party/blink/renderer/core/layout/ng/flex/ng_flex_item_iterator.cc
@@ -9,7 +9,7 @@
 
 namespace blink {
 
-NGFlexItemIterator::NGFlexItemIterator(const Vector<NGFlexLine>& flex_lines,
+NGFlexItemIterator::NGFlexItemIterator(const HeapVector<NGFlexLine>& flex_lines,
                                        const NGBlockBreakToken* break_token,
                                        bool is_horizontal_flow)
     : flex_lines_(flex_lines),
diff --git a/third_party/blink/renderer/core/layout/ng/flex/ng_flex_item_iterator.h b/third_party/blink/renderer/core/layout/ng/flex/ng_flex_item_iterator.h
index 5d10f001..dab3d373 100644
--- a/third_party/blink/renderer/core/layout/ng/flex/ng_flex_item_iterator.h
+++ b/third_party/blink/renderer/core/layout/ng/flex/ng_flex_item_iterator.h
@@ -6,6 +6,7 @@
 #define THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_NG_FLEX_NG_FLEX_ITEM_ITERATOR_H_
 
 #include "third_party/blink/renderer/core/core_export.h"
+#include "third_party/blink/renderer/platform/heap/collection_support/heap_vector.h"
 #include "third_party/blink/renderer/platform/wtf/vector.h"
 
 namespace blink {
@@ -30,7 +31,7 @@
   STACK_ALLOCATED();
 
  public:
-  NGFlexItemIterator(const Vector<NGFlexLine>& flex_lines,
+  NGFlexItemIterator(const HeapVector<NGFlexLine>& flex_lines,
                      const NGBlockBreakToken* break_token,
                      bool is_horizontal_flow);
 
@@ -43,7 +44,7 @@
   NGFlexItem* FindNextItem(const NGBlockBreakToken* item_break_token = nullptr);
 
   NGFlexItem* next_unstarted_item_ = nullptr;
-  const Vector<NGFlexLine>& flex_lines_;
+  const HeapVector<NGFlexLine>& flex_lines_;
   const NGBlockBreakToken* break_token_;
   // TODO(almaher): This likely won't be the right check once writing mode roots
   // are no longer treated as monolithic.
diff --git a/third_party/blink/renderer/core/layout/ng/flex/ng_flex_layout_algorithm.cc b/third_party/blink/renderer/core/layout/ng/flex/ng_flex_layout_algorithm.cc
index d84cf8bf..2a1ca43 100644
--- a/third_party/blink/renderer/core/layout/ng/flex/ng_flex_layout_algorithm.cc
+++ b/third_party/blink/renderer/core/layout/ng/flex/ng_flex_layout_algorithm.cc
@@ -834,8 +834,10 @@
 
   PaintLayerScrollableArea::DelayScrollOffsetClampScope delay_clamp_scope;
 
-  Vector<NGFlexLine> flex_line_outputs;
   Vector<EBreakBetween> row_break_between_outputs;
+  HeapVector<NGFlexLine> flex_line_outputs;
+  ClearCollectionScope<HeapVector<NGFlexLine>> scope(&flex_line_outputs);
+
   bool use_empty_line_block_size;
   if (IsResumingLayout(BreakToken())) {
     const NGFlexBreakTokenData* flex_data =
@@ -943,9 +945,10 @@
 #endif
 
   if (ConstraintSpace().HasBlockFragmentation()) {
-    container_builder_.SetBreakTokenData(std::make_unique<NGFlexBreakTokenData>(
-        container_builder_.GetBreakTokenData(), flex_line_outputs,
-        row_break_between_outputs, total_intrinsic_block_size_));
+    container_builder_.SetBreakTokenData(
+        MakeGarbageCollected<NGFlexBreakTokenData>(
+            container_builder_.GetBreakTokenData(), flex_line_outputs,
+            row_break_between_outputs, total_intrinsic_block_size_));
   }
 
   // Un-freeze descendant scrollbars before we run the OOF layout part.
@@ -956,7 +959,7 @@
 }
 
 void NGFlexLayoutAlgorithm::PlaceFlexItems(
-    Vector<NGFlexLine>* flex_line_outputs) {
+    HeapVector<NGFlexLine>* flex_line_outputs) {
   ConstructAndAppendFlexItems();
 
   LayoutUnit main_axis_start_offset;
@@ -1072,8 +1075,8 @@
 }
 
 void NGFlexLayoutAlgorithm::ApplyFinalAlignmentAndReversals(
-    Vector<NGFlexLine>* flex_line_outputs) {
-  Vector<FlexLine>& line_contexts = algorithm_.FlexLines();
+    HeapVector<NGFlexLine>* flex_line_outputs) {
+  auto& line_contexts = algorithm_.FlexLines();
   const LayoutUnit cross_axis_start_edge =
       line_contexts.IsEmpty() ? LayoutUnit()
                               : line_contexts[0].cross_axis_offset_;
@@ -1111,7 +1114,7 @@
 }
 
 NGLayoutResult::EStatus NGFlexLayoutAlgorithm::GiveItemsFinalPositionAndSize(
-    Vector<NGFlexLine>* flex_line_outputs,
+    HeapVector<NGFlexLine>* flex_line_outputs,
     Vector<EBreakBetween>* row_break_between_outputs) {
   DCHECK(!IsResumingLayout(BreakToken()));
   LayoutUnit final_content_cross_size;
@@ -1232,7 +1235,7 @@
 
 NGLayoutResult::EStatus
 NGFlexLayoutAlgorithm::GiveItemsFinalPositionAndSizeForFragmentation(
-    Vector<NGFlexLine>* flex_line_outputs,
+    HeapVector<NGFlexLine>* flex_line_outputs,
     Vector<EBreakBetween>* row_break_between_outputs) {
   DCHECK(involved_in_block_fragmentation_);
 
@@ -1773,7 +1776,7 @@
 
 #if DCHECK_IS_ON()
 void NGFlexLayoutAlgorithm::CheckFlexLines(
-    const Vector<NGFlexLine>& flex_line_outputs) const {
+    const HeapVector<NGFlexLine>& flex_line_outputs) const {
   const Vector<FlexLine>& flex_lines = algorithm_.flex_lines_;
 
   DCHECK_EQ(flex_line_outputs.size(), flex_lines.size());
diff --git a/third_party/blink/renderer/core/layout/ng/flex/ng_flex_layout_algorithm.h b/third_party/blink/renderer/core/layout/ng/flex/ng_flex_layout_algorithm.h
index 4be6fff4..b3556d31 100644
--- a/third_party/blink/renderer/core/layout/ng/flex/ng_flex_layout_algorithm.h
+++ b/third_party/blink/renderer/core/layout/ng/flex/ng_flex_layout_algorithm.h
@@ -32,7 +32,7 @@
   const NGLayoutResult* RelayoutIgnoringChildScrollbarChanges();
   const NGLayoutResult* LayoutInternal();
 
-  void PlaceFlexItems(Vector<NGFlexLine>* flex_line_outputs);
+  void PlaceFlexItems(HeapVector<NGFlexLine>* flex_line_outputs);
   void CalculateTotalIntrinsicBlockSize(bool use_empty_line_block_size);
 
   Length GetUsedFlexBasis(const NGBlockNode& child) const;
@@ -72,12 +72,13 @@
       absl::optional<LayoutUnit> block_offset_for_fragmentation = absl::nullopt,
       bool min_block_size_should_encompass_intrinsic_size = false) const;
   void ConstructAndAppendFlexItems();
-  void ApplyFinalAlignmentAndReversals(Vector<NGFlexLine>* flex_line_outputs);
+  void ApplyFinalAlignmentAndReversals(
+      HeapVector<NGFlexLine>* flex_line_outputs);
   NGLayoutResult::EStatus GiveItemsFinalPositionAndSize(
-      Vector<NGFlexLine>* flex_line_outputs,
+      HeapVector<NGFlexLine>* flex_line_outputs,
       Vector<EBreakBetween>* row_break_between_outputs);
   NGLayoutResult::EStatus GiveItemsFinalPositionAndSizeForFragmentation(
-      Vector<NGFlexLine>* flex_line_outputs,
+      HeapVector<NGFlexLine>* flex_line_outputs,
       Vector<EBreakBetween>* row_break_between_outputs);
   NGLayoutResult::EStatus PropagateFlexItemInfo(FlexItem* flex_item,
                                                 wtf_size_t flex_line_idx,
@@ -135,7 +136,7 @@
                              wtf_size_t row_index);
 
 #if DCHECK_IS_ON()
-  void CheckFlexLines(const Vector<NGFlexLine>& flex_line_outputs) const;
+  void CheckFlexLines(const HeapVector<NGFlexLine>& flex_line_outputs) const;
 #endif
 
   const bool is_column_;
diff --git a/third_party/blink/renderer/core/layout/ng/flex/ng_flex_line.h b/third_party/blink/renderer/core/layout/ng/flex/ng_flex_line.h
index 17738b4..ae8942ef 100644
--- a/third_party/blink/renderer/core/layout/ng/flex/ng_flex_line.h
+++ b/third_party/blink/renderer/core/layout/ng/flex/ng_flex_line.h
@@ -12,10 +12,15 @@
 namespace blink {
 
 struct NGFlexItem {
+  DISALLOW_NEW();
+
+ public:
   NGFlexItem() : ng_input_node(nullptr) {}
 
   const ComputedStyle& Style() const { return ng_input_node.Style(); }
 
+  void Trace(Visitor* visitor) const { visitor->Trace(ng_input_node); }
+
   LayoutUnit main_axis_final_size;
   // This will originally be set to the total block size of the item before
   // fragmentation. It will then be reduced while performing fragmentation. If
@@ -27,14 +32,22 @@
 };
 
 struct NGFlexLine {
+  DISALLOW_NEW();
+
+ public:
   explicit NGFlexLine(wtf_size_t num_items) : line_items(num_items) {}
 
+  void Trace(Visitor* visitor) const { visitor->Trace(line_items); }
+
   LayoutUnit line_cross_size;
   LayoutUnit cross_axis_offset;
   LayoutUnit item_offset_adjustment;
-  Vector<NGFlexItem> line_items;
+  HeapVector<NGFlexItem> line_items;
 };
 
 }  // namespace blink
 
+WTF_ALLOW_CLEAR_UNUSED_SLOTS_WITH_MEM_FUNCTIONS(blink::NGFlexItem)
+WTF_ALLOW_CLEAR_UNUSED_SLOTS_WITH_MEM_FUNCTIONS(blink::NGFlexLine)
+
 #endif  // THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_NG_FLEX_NG_FLEX_LINE_H_
diff --git a/third_party/blink/renderer/core/layout/ng/grid/ng_grid_break_token_data.h b/third_party/blink/renderer/core/layout/ng/grid/ng_grid_break_token_data.h
index 926a5c9..6d49d75 100644
--- a/third_party/blink/renderer/core/layout/ng/grid/ng_grid_break_token_data.h
+++ b/third_party/blink/renderer/core/layout/ng/grid/ng_grid_break_token_data.h
@@ -22,8 +22,6 @@
 };
 
 struct NGGridBreakTokenData final : NGBlockBreakTokenData {
-  USING_FAST_MALLOC(NGGridBreakTokenData);
-
  public:
   NGGridBreakTokenData(const NGBlockBreakTokenData* break_token_data,
                        const NGGridGeometry& grid_geometry,
diff --git a/third_party/blink/renderer/core/layout/ng/grid/ng_grid_item.h b/third_party/blink/renderer/core/layout/ng/grid/ng_grid_item.h
index 5e35c991..7a0ddb1 100644
--- a/third_party/blink/renderer/core/layout/ng/grid/ng_grid_item.h
+++ b/third_party/blink/renderer/core/layout/ng/grid/ng_grid_item.h
@@ -137,6 +137,8 @@
         .HasProperty(TrackSpanProperties::kHasFixedMaximumTrack);
   }
 
+  void Trace(Visitor* visitor) const { visitor->Trace(node); }
+
   NGBlockNode node;
   GridArea resolved_position;
 
@@ -172,7 +174,7 @@
   OutOfFlowItemPlacement row_placement;
 };
 
-using GridItemStorageVector = Vector<GridItemData, 4>;
+using GridItemStorageVector = HeapVector<GridItemData, 4>;
 
 struct CORE_EXPORT GridItems {
   DISALLOW_NEW();
@@ -207,7 +209,7 @@
 
    private:
     GridItemStorageVector* item_data_;
-    Vector<wtf_size_t>::const_iterator current_index_;
+    HeapVector<wtf_size_t>::const_iterator current_index_;
   };
 
   Iterator begin() {
@@ -227,6 +229,8 @@
   wtf_size_t Size() const { return item_data.size(); }
   bool IsEmpty() const { return item_data.IsEmpty(); }
 
+  void Trace(Visitor* visitor) const { visitor->Trace(item_data); }
+
   // Grid items are appended in document order, but we want to rearrange them in
   // order-modified document order since auto-placement and painting rely on it
   // later in the algorithm.
@@ -236,4 +240,6 @@
 
 }  // namespace blink
 
+WTF_ALLOW_CLEAR_UNUSED_SLOTS_WITH_MEM_FUNCTIONS(blink::GridItemData)
+
 #endif  // THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_NG_GRID_NG_GRID_ITEM_H_
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 b94daa3..61634854 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
@@ -400,9 +400,10 @@
         grid_items, row_break_between, &grid_geometry, &offsets,
         &row_offset_adjustments, &intrinsic_block_size);
 
-    container_builder_.SetBreakTokenData(std::make_unique<NGGridBreakTokenData>(
-        container_builder_.GetBreakTokenData(), grid_geometry, offsets,
-        row_offset_adjustments, row_break_between, intrinsic_block_size));
+    container_builder_.SetBreakTokenData(
+        MakeGarbageCollected<NGGridBreakTokenData>(
+            container_builder_.GetBreakTokenData(), grid_geometry, offsets,
+            row_offset_adjustments, row_break_between, intrinsic_block_size));
   } else {
     PlaceGridItems(grid_items, grid_geometry, &row_break_between);
   }
diff --git a/third_party/blink/renderer/core/layout/ng/grid/ng_grid_layout_algorithm_test.cc b/third_party/blink/renderer/core/layout/ng/grid/ng_grid_layout_algorithm_test.cc
index 34fa25f..0232c85b 100644
--- a/third_party/blink/renderer/core/layout/ng/grid/ng_grid_layout_algorithm_test.cc
+++ b/third_party/blink/renderer/core/layout/ng/grid/ng_grid_layout_algorithm_test.cc
@@ -50,20 +50,20 @@
         algorithm.ComputeAutomaticRepetitions(kForColumns),
         algorithm.ComputeAutomaticRepetitions(kForRows));
 
-    grid_items_ = algorithm.Node().ConstructGridItems(&placement_data);
+    items_->grid_items_ = algorithm.Node().ConstructGridItems(&placement_data);
 
     // Build block track collections.
     NGGridBlockTrackCollection column_block_track_collection(kForColumns);
     NGGridBlockTrackCollection row_block_track_collection(kForRows);
-    algorithm.BuildBlockTrackCollections(placement_data, &grid_items_,
+    algorithm.BuildBlockTrackCollections(placement_data, &items_->grid_items_,
                                          &column_block_track_collection,
                                          &row_block_track_collection);
 
     LayoutUnit unused_intrinsic_block_size;
     grid_geometry_ = algorithm.ComputeGridGeometry(
-        column_block_track_collection, row_block_track_collection, &grid_items_,
-        &unused_intrinsic_block_size, &column_track_collection_,
-        &row_track_collection_);
+        column_block_track_collection, row_block_track_collection,
+        &items_->grid_items_, &unused_intrinsic_block_size,
+        &column_track_collection_, &row_track_collection_);
   }
 
   NGGridLayoutAlgorithmTrackCollection& TrackCollection(
@@ -75,7 +75,7 @@
   LayoutUnit BaseRowSizeForChild(const NGGridLayoutAlgorithm& algorithm,
                                  wtf_size_t index) {
     LayoutUnit offset, size;
-    algorithm.ComputeGridItemOffsetAndSize(grid_items_.item_data[index],
+    algorithm.ComputeGridItemOffsetAndSize(items_->grid_items_.item_data[index],
                                            grid_geometry_.row_geometry,
                                            kForRows, &offset, &size);
     return size;
@@ -83,11 +83,11 @@
 
   // Helper methods to access private data on NGGridLayoutAlgorithm. This class
   // is a friend of NGGridLayoutAlgorithm but the individual tests are not.
-  wtf_size_t GridItemCount() { return grid_items_.item_data.size(); }
+  wtf_size_t GridItemCount() { return items_->grid_items_.item_data.size(); }
 
   Vector<GridArea> GridItemGridAreas(const NGGridLayoutAlgorithm& algorithm) {
     Vector<GridArea> results;
-    for (const auto& item : grid_items_.item_data)
+    for (const auto& item : items_->grid_items_.item_data)
       results.push_back(item.resolved_position);
     return results;
   }
@@ -97,7 +97,8 @@
       TrackSpanProperties::PropertyId property) {
     Vector<wtf_size_t> results;
     for (wtf_size_t i = 0; i < GridItemCount(); ++i) {
-      if (grid_items_.item_data[i].column_span_properties.HasProperty(property))
+      if (items_->grid_items_.item_data[i].column_span_properties.HasProperty(
+              property))
         results.push_back(i);
     }
     return results;
@@ -108,7 +109,8 @@
       TrackSpanProperties::PropertyId property) {
     Vector<wtf_size_t> results;
     for (wtf_size_t i = 0; i < GridItemCount(); ++i) {
-      if (grid_items_.item_data[i].row_span_properties.HasProperty(property))
+      if (items_->grid_items_.item_data[i].row_span_properties.HasProperty(
+              property))
         results.push_back(i);
     }
     return results;
@@ -162,7 +164,14 @@
     return fragment->DumpFragmentTree(flags);
   }
 
-  GridItems grid_items_;
+  struct Items final : public GarbageCollected<Items> {
+   public:
+    GridItems grid_items_;
+
+    void Trace(Visitor* visitor) const { visitor->Trace(grid_items_); }
+  };
+  Persistent<Items> items_ = MakeGarbageCollected<Items>();
+
   NGGridGeometry grid_geometry_;
   NGGridLayoutAlgorithmTrackCollection column_track_collection_;
   NGGridLayoutAlgorithmTrackCollection row_track_collection_;
diff --git a/third_party/blink/renderer/core/layout/ng/mathml/ng_math_row_layout_algorithm.h b/third_party/blink/renderer/core/layout/ng/mathml/ng_math_row_layout_algorithm.h
index bfae238..4593125c 100644
--- a/third_party/blink/renderer/core/layout/ng/mathml/ng_math_row_layout_algorithm.h
+++ b/third_party/blink/renderer/core/layout/ng/mathml/ng_math_row_layout_algorithm.h
@@ -32,7 +32,10 @@
           offset(offset),
           result(std::move(result)) {}
 
-    void Trace(Visitor* visitor) const { visitor->Trace(result); }
+    void Trace(Visitor* visitor) const {
+      visitor->Trace(child);
+      visitor->Trace(result);
+    }
 
     NGBlockNode child;
     NGBoxStrut margins;
diff --git a/third_party/blink/renderer/core/layout/ng/mathml/ng_math_scripts_layout_algorithm.cc b/third_party/blink/renderer/core/layout/ng/mathml/ng_math_scripts_layout_algorithm.cc
index c4d199645..afae80d 100644
--- a/third_party/blink/renderer/core/layout/ng/mathml/ng_math_scripts_layout_algorithm.cc
+++ b/third_party/blink/renderer/core/layout/ng/mathml/ng_math_scripts_layout_algorithm.cc
@@ -94,7 +94,7 @@
 
 void NGMathScriptsLayoutAlgorithm::GatherChildren(
     NGBlockNode* base,
-    Vector<SubSupPair>* sub_sup_pairs,
+    HeapVector<SubSupPair>* sub_sup_pairs,
     NGBlockNode* prescripts,
     unsigned* first_prescript_index,
     NGBoxFragmentBuilder* container_builder) const {
@@ -292,8 +292,11 @@
 
   NGBlockNode base = nullptr;
   NGBlockNode prescripts = nullptr;
-  Vector<SubSupPair> sub_sup_pairs;
   wtf_size_t first_prescript_index = 0;
+
+  HeapVector<SubSupPair> sub_sup_pairs;
+  ClearCollectionScope<HeapVector<SubSupPair>> scope(&sub_sup_pairs);
+
   GatherChildren(&base, &sub_sup_pairs, &prescripts, &first_prescript_index,
                  &container_builder_);
   ChildrenAndMetrics sub_metrics, sup_metrics;
@@ -419,8 +422,11 @@
 
   NGBlockNode base = nullptr;
   NGBlockNode prescripts = nullptr;
-  Vector<SubSupPair> sub_sup_pairs;
   unsigned first_prescript_index = 0;
+
+  HeapVector<SubSupPair> sub_sup_pairs;
+  ClearCollectionScope<HeapVector<SubSupPair>> scope(&sub_sup_pairs);
+
   GatherChildren(&base, &sub_sup_pairs, &prescripts, &first_prescript_index);
   DCHECK_GE(sub_sup_pairs.size(), 1ul);
 
diff --git a/third_party/blink/renderer/core/layout/ng/mathml/ng_math_scripts_layout_algorithm.h b/third_party/blink/renderer/core/layout/ng/mathml/ng_math_scripts_layout_algorithm.h
index 26a648b..88ecedf4 100644
--- a/third_party/blink/renderer/core/layout/ng/mathml/ng_math_scripts_layout_algorithm.h
+++ b/third_party/blink/renderer/core/layout/ng/mathml/ng_math_scripts_layout_algorithm.h
@@ -33,18 +33,28 @@
     NGBoxStrut margins;
     NGBlockNode node = nullptr;
 
-    void Trace(Visitor* visitor) const { visitor->Trace(result); }
+    void Trace(Visitor* visitor) const {
+      visitor->Trace(result);
+      visitor->Trace(node);
+    }
   };
 
- private:
   struct SubSupPair {
     DISALLOW_NEW();
+
+   public:
+    void Trace(Visitor* visitor) const {
+      visitor->Trace(sub);
+      visitor->Trace(sup);
+    }
+
     NGBlockNode sub = nullptr;
     NGBlockNode sup = nullptr;
   };
 
+ private:
   void GatherChildren(NGBlockNode* base,
-                      Vector<SubSupPair>*,
+                      HeapVector<SubSupPair>*,
                       NGBlockNode* prescripts,
                       unsigned* first_prescript_index,
                       NGBoxFragmentBuilder* = nullptr) const;
@@ -77,5 +87,7 @@
 
 WTF_ALLOW_CLEAR_UNUSED_SLOTS_WITH_MEM_FUNCTIONS(
     blink::NGMathScriptsLayoutAlgorithm::ChildAndMetrics)
+WTF_ALLOW_CLEAR_UNUSED_SLOTS_WITH_MEM_FUNCTIONS(
+    blink::NGMathScriptsLayoutAlgorithm::SubSupPair)
 
 #endif  // THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_NG_MATHML_NG_MATH_SCRIPTS_LAYOUT_ALGORITHM_H_
diff --git a/third_party/blink/renderer/core/layout/ng/ng_block_break_token.cc b/third_party/blink/renderer/core/layout/ng/ng_block_break_token.cc
index 5d423450..eb40c67f 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_block_break_token.cc
+++ b/third_party/blink/renderer/core/layout/ng/ng_block_break_token.cc
@@ -43,14 +43,15 @@
   has_unpositioned_list_marker_ =
       static_cast<bool>(builder->UnpositionedListMarker());
   DCHECK(builder->HasBreakTokenData());
-  data_ = std::move(builder->break_token_data_);
+  data_ = builder->break_token_data_;
+  builder->break_token_data_ = nullptr;
   for (wtf_size_t i = 0; i < builder->child_break_tokens_.size(); ++i)
     child_break_tokens_[i] = builder->child_break_tokens_[i];
 }
 
 NGBlockBreakToken::NGBlockBreakToken(PassKey key, NGLayoutInputNode node)
     : NGBreakToken(kBlockBreakToken, node),
-      data_(std::make_unique<NGBlockBreakTokenData>()),
+      data_(MakeGarbageCollected<NGBlockBreakTokenData>()),
       const_num_children_(0) {}
 
 const NGInlineBreakToken* NGBlockBreakToken::InlineBreakTokenFor(
@@ -100,6 +101,7 @@
 #endif  // DCHECK_IS_ON()
 
 void NGBlockBreakToken::Trace(Visitor* visitor) const {
+  visitor->Trace(data_);
   // Looking up |ChildBreakTokens()| in Trace() here is safe because
   // |const_num_children_| is const.
   for (auto& child : ChildBreakTokens())
diff --git a/third_party/blink/renderer/core/layout/ng/ng_block_break_token.h b/third_party/blink/renderer/core/layout/ng/ng_block_break_token.h
index e23faaac..be81a07d 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_block_break_token.h
+++ b/third_party/blink/renderer/core/layout/ng/ng_block_break_token.h
@@ -80,7 +80,7 @@
 
   const NGBlockBreakTokenData* TokenData() const {
     DCHECK(data_);
-    return data_.get();
+    return data_;
   }
 
   // Return true if this is a break token that was produced without any
@@ -184,7 +184,7 @@
   void Trace(Visitor*) const override;
 
  private:
-  std::unique_ptr<NGBlockBreakTokenData> data_;
+  Member<NGBlockBreakTokenData> data_;
 
   const wtf_size_t const_num_children_;
   // This must be the last member, because it is a flexible array.
diff --git a/third_party/blink/renderer/core/layout/ng/ng_block_break_token_data.h b/third_party/blink/renderer/core/layout/ng/ng_block_break_token_data.h
index 6049ae88..59f15d8 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_block_break_token_data.h
+++ b/third_party/blink/renderer/core/layout/ng/ng_block_break_token_data.h
@@ -7,10 +7,12 @@
 
 #include "third_party/blink/renderer/core/core_export.h"
 #include "third_party/blink/renderer/platform/geometry/layout_unit.h"
+#include "third_party/blink/renderer/platform/heap/garbage_collected.h"
 
 namespace blink {
 
-struct NGBlockBreakTokenData {
+struct NGBlockBreakTokenData : public GarbageCollected<NGBlockBreakTokenData> {
+ public:
   enum NGBreakTokenDataType {
     kBlockBreakTokenData,
     kFlexBreakTokenData,
@@ -38,6 +40,8 @@
   bool IsFlexType() const { return Type() == kFlexBreakTokenData; }
   bool IsGridType() const { return Type() == kGridBreakTokenData; }
 
+  virtual void Trace(Visitor* visitor) const {}
+
   LayoutUnit consumed_block_size;
   LayoutUnit consumed_block_size_legacy_adjustment;
 
diff --git a/third_party/blink/renderer/core/layout/ng/ng_block_child_iterator_test.cc b/third_party/blink/renderer/core/layout/ng/ng_block_child_iterator_test.cc
index 3690ec88..69b36bd 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_block_child_iterator_test.cc
+++ b/third_party/blink/renderer/core/layout/ng/ng_block_child_iterator_test.cc
@@ -23,7 +23,7 @@
       node, &node.Style(), /* space */ nullptr,
       WritingDirectionMode(WritingMode::kHorizontalTb, TextDirection::kLtr));
   DCHECK(!builder.HasBreakTokenData());
-  builder.SetBreakTokenData(std::make_unique<NGBlockBreakTokenData>());
+  builder.SetBreakTokenData(MakeGarbageCollected<NGBlockBreakTokenData>());
   if (has_seen_all_children)
     builder.SetHasSeenAllChildren();
   if (child_break_tokens) {
diff --git a/third_party/blink/renderer/core/layout/ng/ng_block_node.h b/third_party/blink/renderer/core/layout/ng/ng_block_node.h
index e20a86f..b177d58 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_block_node.h
+++ b/third_party/blink/renderer/core/layout/ng/ng_block_node.h
@@ -289,4 +289,6 @@
 
 }  // namespace blink
 
+WTF_ALLOW_CLEAR_UNUSED_SLOTS_WITH_MEM_FUNCTIONS(blink::NGBlockNode)
+
 #endif  // THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_NG_NG_BLOCK_NODE_H_
diff --git a/third_party/blink/renderer/core/layout/ng/ng_box_fragment_builder.h b/third_party/blink/renderer/core/layout/ng/ng_box_fragment_builder.h
index 3d5f140..b0c3193 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_box_fragment_builder.h
+++ b/third_party/blink/renderer/core/layout/ng/ng_box_fragment_builder.h
@@ -594,19 +594,18 @@
     return *grid_layout_data_.get();
   }
 
-  bool HasBreakTokenData() const { return break_token_data_.get(); }
+  bool HasBreakTokenData() const { return break_token_data_; }
 
   NGBlockBreakTokenData* EnsureBreakTokenData() {
     if (!HasBreakTokenData())
-      break_token_data_ = std::make_unique<NGBlockBreakTokenData>();
-    return break_token_data_.get();
+      break_token_data_ = MakeGarbageCollected<NGBlockBreakTokenData>();
+    return break_token_data_;
   }
 
-  NGBlockBreakTokenData* GetBreakTokenData() { return break_token_data_.get(); }
+  NGBlockBreakTokenData* GetBreakTokenData() { return break_token_data_; }
 
-  void SetBreakTokenData(
-      std::unique_ptr<NGBlockBreakTokenData> break_token_data) {
-    break_token_data_ = std::move(break_token_data);
+  void SetBreakTokenData(NGBlockBreakTokenData* break_token_data) {
+    break_token_data_ = break_token_data;
   }
 
   // The |NGFragmentItemsBuilder| for the inline formatting context of this box.
@@ -723,8 +722,7 @@
 
   // Table specific types.
   absl::optional<PhysicalRect> table_grid_rect_;
-  absl::optional<NGTableFragmentData::ColumnGeometries>
-      table_column_geometries_;
+  NGTableFragmentData::ColumnGeometries table_column_geometries_;
   scoped_refptr<const NGTableBorders> table_collapsed_borders_;
   std::unique_ptr<NGTableFragmentData::CollapsedBordersGeometry>
       table_collapsed_borders_geometry_;
@@ -733,7 +731,7 @@
   // Table cell specific types.
   absl::optional<wtf_size_t> table_cell_column_index_;
 
-  std::unique_ptr<NGBlockBreakTokenData> break_token_data_;
+  NGBlockBreakTokenData* break_token_data_ = nullptr;
 
   // Grid specific types.
   std::unique_ptr<NGGridLayoutData> grid_layout_data_;
diff --git a/third_party/blink/renderer/core/layout/ng/ng_layout_input_node.h b/third_party/blink/renderer/core/layout/ng/ng_layout_input_node.h
index 0929e87..e184281 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_layout_input_node.h
+++ b/third_party/blink/renderer/core/layout/ng/ng_layout_input_node.h
@@ -321,6 +321,8 @@
   void ShowNodeTree() const;
 #endif
 
+  void Trace(Visitor* visitor) const { visitor->Trace(box_); }
+
  protected:
   NGLayoutInputNode(LayoutBox* box, NGLayoutInputNodeType type)
       : box_(box), type_(type) {}
@@ -329,7 +331,7 @@
       absl::optional<LayoutUnit>* computed_inline_size,
       absl::optional<LayoutUnit>* computed_block_size) const;
 
-  UntracedMember<LayoutBox> box_;
+  Member<LayoutBox> box_;
 
   unsigned type_ : 1;  // NGLayoutInputNodeType
 };
diff --git a/third_party/blink/renderer/core/layout/ng/ng_layout_result.cc b/third_party/blink/renderer/core/layout/ng/ng_layout_result.cc
index 9db586a..09d432d24 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_layout_result.cc
+++ b/third_party/blink/renderer/core/layout/ng/ng_layout_result.cc
@@ -357,6 +357,7 @@
 
 void NGLayoutResult::RareData::Trace(Visitor* visitor) const {
   visitor->Trace(early_break);
+  visitor->Trace(column_spanner);
 }
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/layout/ng/ng_length_utils.cc b/third_party/blink/renderer/core/layout/ng/ng_length_utils.cc
index bad3bbe7..c515039 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_length_utils.cc
+++ b/third_party/blink/renderer/core/layout/ng/ng_length_utils.cc
@@ -1620,8 +1620,9 @@
   if (default_intrinsic_size != kIndefiniteSize) {
     // <textarea>'s intrinsic size should ignore scrollbar existence.
     if (node.IsTextArea()) {
-      return default_intrinsic_size + border_scrollbar_padding.BlockSum() -
-             ComputeScrollbars(space, node).BlockSum();
+      return default_intrinsic_size -
+             ComputeScrollbars(space, node).BlockSum() +
+             border_scrollbar_padding.BlockSum();
     }
     return default_intrinsic_size + border_scrollbar_padding.BlockSum();
   }
diff --git a/third_party/blink/renderer/core/layout/ng/ng_out_of_flow_layout_part.cc b/third_party/blink/renderer/core/layout/ng/ng_out_of_flow_layout_part.cc
index de8b216..17e5b3e 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_out_of_flow_layout_part.cc
+++ b/third_party/blink/renderer/core/layout/ng/ng_out_of_flow_layout_part.cc
@@ -2037,11 +2037,16 @@
   visitor->Trace(parent_break_token);
 }
 
+void NGOutOfFlowLayoutPart::NodeInfo::Trace(Visitor* visitor) const {
+  visitor->Trace(node);
+}
+
 void NGOutOfFlowLayoutPart::OffsetInfo::Trace(Visitor* visitor) const {
   visitor->Trace(initial_layout_result);
 }
 
 void NGOutOfFlowLayoutPart::NodeToLayout::Trace(Visitor* visitor) const {
+  visitor->Trace(node_info);
   visitor->Trace(offset_info);
   visitor->Trace(break_token);
   visitor->Trace(containing_block_fragment);
diff --git a/third_party/blink/renderer/core/layout/ng/ng_out_of_flow_layout_part.h b/third_party/blink/renderer/core/layout/ng/ng_out_of_flow_layout_part.h
index 50fecc9..1729609 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_out_of_flow_layout_part.h
+++ b/third_party/blink/renderer/core/layout/ng/ng_out_of_flow_layout_part.h
@@ -154,6 +154,8 @@
           default_writing_direction(default_writing_direction),
           fixedpos_containing_block(fixedpos_containing_block),
           inline_container(inline_container) {}
+
+    void Trace(Visitor* visitor) const;
   };
 
   // Stores the calculated offset for an OOF positioned node, along with the
diff --git a/third_party/blink/renderer/core/layout/ng/ng_physical_box_fragment.cc b/third_party/blink/renderer/core/layout/ng/ng_physical_box_fragment.cc
index fab03ab8..9358837d 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_physical_box_fragment.cc
+++ b/third_party/blink/renderer/core/layout/ng/ng_physical_box_fragment.cc
@@ -188,12 +188,12 @@
       has_fragment_items = true;
   }
 
-  bool has_rare_data =
-      builder->mathml_paint_info_ ||
-      builder->table_grid_rect_ || builder->table_column_geometries_ ||
-      builder->table_collapsed_borders_ ||
-      builder->table_collapsed_borders_geometry_ ||
-      builder->table_cell_column_index_;
+  bool has_rare_data = builder->mathml_paint_info_ ||
+                       builder->table_grid_rect_ ||
+                       !builder->table_column_geometries_.IsEmpty() ||
+                       builder->table_collapsed_borders_ ||
+                       builder->table_collapsed_borders_geometry_ ||
+                       builder->table_cell_column_index_;
 
   wtf_size_t num_fragment_items =
       builder->ItemsBuilder() ? builder->ItemsBuilder()->Size() : 0;
@@ -511,8 +511,8 @@
     : mathml_paint_info(std::move(builder->mathml_paint_info_)) {
   if (builder->table_grid_rect_)
     table_grid_rect = *builder->table_grid_rect_;
-  if (builder->table_column_geometries_)
-    table_column_geometries = *builder->table_column_geometries_;
+  if (!builder->table_column_geometries_.IsEmpty())
+    table_column_geometries = builder->table_column_geometries_;
   if (builder->table_collapsed_borders_)
     table_collapsed_borders = std::move(builder->table_collapsed_borders_);
   if (builder->table_collapsed_borders_geometry_) {
@@ -1785,6 +1785,8 @@
   NGPhysicalFragment::TraceAfterDispatch(visitor);
 }
 
-void NGPhysicalBoxFragment::RareData::Trace(Visitor* visitor) const {}
+void NGPhysicalBoxFragment::RareData::Trace(Visitor* visitor) const {
+  visitor->Trace(table_column_geometries);
+}
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/layout/ng/table/layout_ng_table_column_visitor.h b/third_party/blink/renderer/core/layout/ng/table/layout_ng_table_column_visitor.h
index 538d97b..7cd1e191f 100644
--- a/third_party/blink/renderer/core/layout/ng/table/layout_ng_table_column_visitor.h
+++ b/third_party/blink/renderer/core/layout/ng/table/layout_ng_table_column_visitor.h
@@ -31,7 +31,7 @@
 //                      bool has_children);
 // }
 template <typename Visitor>
-void VisitLayoutNGTableColumn(const Vector<NGBlockNode>& columns,
+void VisitLayoutNGTableColumn(const HeapVector<NGBlockNode>& columns,
                               wtf_size_t table_column_count,
                               Visitor* visitor) {
   wtf_size_t current_column_index = 0;
diff --git a/third_party/blink/renderer/core/layout/ng/table/ng_table_borders.cc b/third_party/blink/renderer/core/layout/ng/table/ng_table_borders.cc
index 53fdd301..87f3934 100644
--- a/third_party/blink/renderer/core/layout/ng/table/ng_table_borders.cc
+++ b/third_party/blink/renderer/core/layout/ng/table/ng_table_borders.cc
@@ -233,13 +233,13 @@
                                       table_writing_direction,
                                       *table_borders.get());
   VisitLayoutNGTableColumn(
-      const_cast<Vector<NGBlockNode>&>(grouped_children.columns),
+      const_cast<HeapVector<NGBlockNode>&>(grouped_children.columns),
       table_column_count, &col_borders_marker);
   ColgroupBordersMarker colgroup_borders_marker(table_row_count, ++box_order,
                                                 table_writing_direction,
                                                 *table_borders.get());
   VisitLayoutNGTableColumn(
-      const_cast<Vector<NGBlockNode>&>(grouped_children.columns),
+      const_cast<HeapVector<NGBlockNode>&>(grouped_children.columns),
       table_column_count, &colgroup_borders_marker);
 
   // Mark table borders.
diff --git a/third_party/blink/renderer/core/layout/ng/table/ng_table_fragment_data.h b/third_party/blink/renderer/core/layout/ng/table/ng_table_fragment_data.h
index 5528864c..b5193e1676 100644
--- a/third_party/blink/renderer/core/layout/ng/table/ng_table_fragment_data.h
+++ b/third_party/blink/renderer/core/layout/ng/table/ng_table_fragment_data.h
@@ -31,6 +31,7 @@
           inline_offset(inline_offset),
           inline_size(inline_size),
           node(node) {}
+    void Trace(Visitor* visitor) const { visitor->Trace(node); }
     wtf_size_t start_column;
     wtf_size_t span;
     LayoutUnit inline_offset;
@@ -38,7 +39,7 @@
     NGLayoutInputNode node;
   };
 
-  using ColumnGeometries = Vector<ColumnGeometry>;
+  using ColumnGeometries = HeapVector<ColumnGeometry>;
 
   // Column/row location is used for collapsed border painting.
   // Only present if borders are collapsed.
diff --git a/third_party/blink/renderer/core/layout/ng/table/ng_table_layout_algorithm.cc b/third_party/blink/renderer/core/layout/ng/table/ng_table_layout_algorithm.cc
index ce6eb35..6585c73 100644
--- a/third_party/blink/renderer/core/layout/ng/table/ng_table_layout_algorithm.cc
+++ b/third_party/blink/renderer/core/layout/ng/table/ng_table_layout_algorithm.cc
@@ -334,6 +334,7 @@
 // back to LayoutObject.
 class ColumnGeometriesBuilder {
   STACK_ALLOCATED();
+
  public:
   void VisitCol(const NGLayoutInputNode& col,
                 wtf_size_t start_column_index,
diff --git a/third_party/blink/renderer/core/layout/ng/table/ng_table_layout_algorithm.h b/third_party/blink/renderer/core/layout/ng/table/ng_table_layout_algorithm.h
index 35f21189..a4b329f 100644
--- a/third_party/blink/renderer/core/layout/ng/table/ng_table_layout_algorithm.h
+++ b/third_party/blink/renderer/core/layout/ng/table/ng_table_layout_algorithm.h
@@ -45,7 +45,10 @@
     DISALLOW_NEW();
 
    public:
-    void Trace(Visitor* visitor) const { visitor->Trace(layout_result); }
+    void Trace(Visitor* visitor) const {
+      visitor->Trace(node);
+      visitor->Trace(layout_result);
+    }
 
     NGBlockNode node;
     Member<const NGLayoutResult> layout_result;
diff --git a/third_party/blink/renderer/core/layout/ng/table/ng_table_layout_algorithm_types.cc b/third_party/blink/renderer/core/layout/ng/table/ng_table_layout_algorithm_types.cc
index cfb460a..94d7e26 100644
--- a/third_party/blink/renderer/core/layout/ng/table/ng_table_layout_algorithm_types.cc
+++ b/third_party/blink/renderer/core/layout/ng/table/ng_table_layout_algorithm_types.cc
@@ -328,6 +328,14 @@
   }
 }
 
+void NGTableGroupedChildren::Trace(Visitor* visitor) const {
+  visitor->Trace(captions);
+  visitor->Trace(columns);
+  visitor->Trace(header);
+  visitor->Trace(bodies);
+  visitor->Trace(footer);
+}
+
 NGTableGroupedChildrenIterator NGTableGroupedChildren::begin() const {
   return NGTableGroupedChildrenIterator(*this);
 }
@@ -355,8 +363,8 @@
       AdvanceToNonEmptySection();
       break;
     case kBody:
-      ++body_iterator_;
-      if (body_iterator_ == grouped_children_.bodies.end())
+      ++position_;
+      if (body_vector_->begin() + position_ == grouped_children_.bodies.end())
         AdvanceToNonEmptySection();
       break;
     case kEnd:
@@ -375,7 +383,7 @@
     case kFoot:
       return grouped_children_.footer;
     case kBody:
-      return *body_iterator_;
+      return body_vector_->at(position_);
     case kEnd:
     case kNone:
       NOTREACHED();
@@ -388,7 +396,7 @@
   if (current_section_ != rhs.current_section_)
     return false;
   if (current_section_ == kBody)
-    return rhs.body_iterator_ == body_iterator_;
+    return rhs.body_vector_ == body_vector_ && rhs.position_ == position_;
   return true;
 }
 
@@ -406,8 +414,9 @@
       break;
     case kHead:
       current_section_ = kBody;
-      body_iterator_ = grouped_children_.bodies.begin();
-      if (body_iterator_ == grouped_children_.bodies.end())
+      body_vector_ = &grouped_children_.bodies;
+      position_ = 0;
+      if (body_vector_->size() == 0)
         AdvanceToNonEmptySection();
       break;
     case kBody:
diff --git a/third_party/blink/renderer/core/layout/ng/table/ng_table_layout_algorithm_types.h b/third_party/blink/renderer/core/layout/ng/table/ng_table_layout_algorithm_types.h
index 5ffd15c..287a5b1 100644
--- a/third_party/blink/renderer/core/layout/ng/table/ng_table_layout_algorithm_types.h
+++ b/third_party/blink/renderer/core/layout/ng/table/ng_table_layout_algorithm_types.h
@@ -241,12 +241,23 @@
 
  public:
   explicit NGTableGroupedChildren(const NGBlockNode& table);
+  ~NGTableGroupedChildren() {
+    captions.clear();
+    columns.clear();
+    bodies.clear();
+  }
 
-  Vector<NGBlockNode> captions;  // CAPTION
-  Vector<NGBlockNode> columns;   // COLGROUP, COL
+  void Trace(Visitor*) const;
+
+  HeapVector<NGBlockNode> captions;  // CAPTION
+  HeapVector<NGBlockNode> columns;   // COLGROUP, COL
 
   NGBlockNode header;          // first THEAD
-  Vector<NGBlockNode> bodies;  // TBODY/multiple THEAD/TFOOT
+
+  // These cannot be modified except in ctor to ensure
+  // NGTableGroupedChildrenIterator works correctly.
+  HeapVector<NGBlockNode> bodies;  // TBODY/multiple THEAD/TFOOT
+
   NGBlockNode footer;          // first TFOOT
 
   // Default iterators iterate over tbody-like (THEAD/TBODY/TFOOT) elements.
@@ -258,6 +269,7 @@
 // thead, tbody, tfoot
 class NGTableGroupedChildrenIterator {
   STACK_ALLOCATED();
+
   enum CurrentSection { kNone, kHead, kBody, kFoot, kEnd };
 
  public:
@@ -275,8 +287,12 @@
  private:
   void AdvanceToNonEmptySection();
   const NGTableGroupedChildren& grouped_children_;
-  Vector<NGBlockNode>::const_iterator body_iterator_;
   CurrentSection current_section_{kNone};
+
+  // |body_vector_| can be modified only in ctor and
+  // |AdvanceToNonEmptySection()|.
+  const HeapVector<NGBlockNode>* body_vector_ = nullptr;
+  wtf_size_t position_ = 0;
 };
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/layout/ng/table/ng_table_layout_algorithm_utils.cc b/third_party/blink/renderer/core/layout/ng/table/ng_table_layout_algorithm_utils.cc
index 6833a9a..3644d1e 100644
--- a/third_party/blink/renderer/core/layout/ng/table/ng_table_layout_algorithm_utils.cc
+++ b/third_party/blink/renderer/core/layout/ng/table/ng_table_layout_algorithm_utils.cc
@@ -373,7 +373,7 @@
 
 // Computes constraints specified on column elements.
 void ComputeColumnElementConstraints(
-    const Vector<NGBlockNode>& columns,
+    const HeapVector<NGBlockNode>& columns,
     bool is_fixed_layout,
     NGTableTypes::Columns* column_constraints) {
   ColumnConstraintsBuilder constraints_builder(column_constraints,
@@ -506,7 +506,7 @@
 
 // Computes maximum possible number of non-mergeable columns.
 wtf_size_t NGTableAlgorithmUtils::ComputeMaximumNonMergeableColumnCount(
-    const Vector<NGBlockNode>& columns,
+    const HeapVector<NGBlockNode>& columns,
     bool is_fixed_layout) {
   // Build column constraints.
   scoped_refptr<NGTableTypes::Columns> column_constraints =
diff --git a/third_party/blink/renderer/core/layout/ng/table/ng_table_layout_algorithm_utils.h b/third_party/blink/renderer/core/layout/ng/table/ng_table_layout_algorithm_utils.h
index 44c8ef0a..2c0e5f9f 100644
--- a/third_party/blink/renderer/core/layout/ng/table/ng_table_layout_algorithm_utils.h
+++ b/third_party/blink/renderer/core/layout/ng/table/ng_table_layout_algorithm_utils.h
@@ -49,7 +49,7 @@
       NGCacheSlot);
 
   static wtf_size_t ComputeMaximumNonMergeableColumnCount(
-      const Vector<NGBlockNode>& columns,
+      const HeapVector<NGBlockNode>& columns,
       bool is_fixed_layout);
 
   static scoped_refptr<NGTableTypes::Columns> ComputeColumnConstraints(
diff --git a/third_party/blink/renderer/core/paint/paint_invalidator.cc b/third_party/blink/renderer/core/paint/paint_invalidator.cc
index c0d3081..0115676 100644
--- a/third_party/blink/renderer/core/paint/paint_invalidator.cc
+++ b/third_party/blink/renderer/core/paint/paint_invalidator.cc
@@ -93,7 +93,8 @@
   if (!object.ShouldCheckGeometryForPaintInvalidation())
     return;
 
-  if (tree_builder_context.this_or_ancestor_opacity_is_zero) {
+  if (tree_builder_context.this_or_ancestor_opacity_is_zero ||
+      context.inside_opaque_layout_shift_root) {
     object.GetMutableForPainting().SetShouldSkipNextLayoutShiftTracking(true);
     return;
   }
@@ -156,11 +157,18 @@
   PhysicalRect old_rect = box.PreviousPhysicalVisualOverflowRect();
   old_rect.Move(adjusted_old_paint_offset);
 
+  // TODO(crbug.com/1178618): We may want to do better than this. For now, just
+  // don't report anything inside multicol containers.
+  const auto* block_flow = DynamicTo<LayoutBlockFlow>(&box);
+  if (block_flow && block_flow->IsFragmentationContextRoot() &&
+      block_flow->IsLayoutNGObject())
+    context.inside_opaque_layout_shift_root = true;
+
   bool should_create_containing_block_scope =
       // TODO(crbug.com/1178618): Support multiple-fragments when switching to
       // LayoutNGFragmentTraversal.
-      context.fragment_data == &box.FirstFragment() &&
-      box.IsLayoutBlockFlow() && box.ChildrenInline() && box.SlowFirstChild();
+      context.fragment_data == &box.FirstFragment() && block_flow &&
+      block_flow->ChildrenInline() && block_flow->FirstChild();
   if (should_create_containing_block_scope) {
     // For layout shift tracking of contained LayoutTexts.
     context.containing_block_scope_.emplace(
diff --git a/third_party/blink/renderer/core/paint/paint_invalidator.h b/third_party/blink/renderer/core/paint/paint_invalidator.h
index d79e12f..b4f1c0d 100644
--- a/third_party/blink/renderer/core/paint/paint_invalidator.h
+++ b/third_party/blink/renderer/core/paint/paint_invalidator.h
@@ -24,7 +24,9 @@
   explicit PaintInvalidatorContext(const PaintInvalidatorContext& parent)
       : parent_context(&parent),
         subtree_flags(parent.subtree_flags),
-        painting_layer(parent.painting_layer) {}
+        painting_layer(parent.painting_layer),
+        inside_opaque_layout_shift_root(
+            parent.inside_opaque_layout_shift_root) {}
 
   bool NeedsSubtreeWalk() const {
     return subtree_flags &
@@ -66,6 +68,10 @@
 
   const FragmentData* fragment_data = nullptr;
 
+  // Set when we have entered something that shouldn't track layout shift
+  // inside (multicol container).
+  bool inside_opaque_layout_shift_root = false;
+
  private:
   friend class PaintInvalidator;
 
diff --git a/third_party/blink/renderer/modules/ad_auction/auction_ad_config.idl b/third_party/blink/renderer/modules/ad_auction/auction_ad_config.idl
index b8ef418ef..aeb9b6d 100644
--- a/third_party/blink/renderer/modules/ad_auction/auction_ad_config.idl
+++ b/third_party/blink/renderer/modules/ad_auction/auction_ad_config.idl
@@ -12,6 +12,7 @@
   sequence<USVString> interestGroupBuyers;
   any auctionSignals;
   any sellerSignals;
+  unsigned long long sellerTimeout;
   record<USVString, any> perBuyerSignals;
   record<USVString, unsigned long long> perBuyerTimeouts;
   sequence<AuctionAdConfig> componentAuctions;
diff --git a/third_party/blink/renderer/modules/ad_auction/navigator_auction.cc b/third_party/blink/renderer/modules/ad_auction/navigator_auction.cc
index 3aa13fd..8ff921b 100644
--- a/third_party/blink/renderer/modules/ad_auction/navigator_auction.cc
+++ b/third_party/blink/renderer/modules/ad_auction/navigator_auction.cc
@@ -687,6 +687,11 @@
     return mojom::blink::AuctionAdConfigPtr();
   }
 
+  if (config.hasSellerTimeout()) {
+    mojo_config->auction_ad_config_non_shared_params->seller_timeout =
+        base::Milliseconds(config.sellerTimeout());
+  }
+
   // Recursively handle component auctions, if there are any.
   if (config.hasComponentAuctions()) {
     for (const auto& idl_component_auction : config.componentAuctions()) {
diff --git a/third_party/blink/renderer/modules/file_system_access/file_system_access_incognito_file_delegate.cc b/third_party/blink/renderer/modules/file_system_access/file_system_access_incognito_file_delegate.cc
index ed48512..cf5a203e 100644
--- a/third_party/blink/renderer/modules/file_system_access/file_system_access_incognito_file_delegate.cc
+++ b/third_party/blink/renderer/modules/file_system_access/file_system_access_incognito_file_delegate.cc
@@ -154,6 +154,7 @@
 
 void FileSystemAccessIncognitoFileDelegate::GetLength(
     base::OnceCallback<void(base::FileErrorOr<int64_t>)> callback) {
+  DCHECK(task_runner_->RunsTasksInCurrentSequence());
   mojo_ptr_->GetLength(WTF::Bind(
       [](base::OnceCallback<void(base::FileErrorOr<int64_t>)> callback,
          base::File::Error file_error, uint64_t length) {
@@ -192,9 +193,9 @@
 }
 
 void FileSystemAccessIncognitoFileDelegate::Close(base::OnceClosure callback) {
+  DCHECK(task_runner_->RunsTasksInCurrentSequence());
   mojo_ptr_.reset();
 
-  DCHECK(task_runner_->RunsTasksInCurrentSequence());
   task_runner_->PostTask(FROM_HERE, std::move(callback));
 }
 
diff --git a/third_party/blink/renderer/platform/runtime_enabled_features.json5 b/third_party/blink/renderer/platform/runtime_enabled_features.json5
index 4defe52..0ba27fc 100644
--- a/third_party/blink/renderer/platform/runtime_enabled_features.json5
+++ b/third_party/blink/renderer/platform/runtime_enabled_features.json5
@@ -1520,7 +1520,6 @@
     },
     {
       name: "MediaStreamTrackInWorker",
-      status: "test",
     },
     {
       name: "MediaStreamTrackTransfer",
diff --git a/third_party/blink/web_tests/FlagExpectations/disable-layout-ng b/third_party/blink/web_tests/FlagExpectations/disable-layout-ng
index 4dcc44a..ae1e27f 100644
--- a/third_party/blink/web_tests/FlagExpectations/disable-layout-ng
+++ b/third_party/blink/web_tests/FlagExpectations/disable-layout-ng
@@ -1411,6 +1411,9 @@
 ### external/wpt/html/semantics/interactive-elements/the-popup-element/
 crbug.com/1178754 external/wpt/html/semantics/interactive-elements/the-popup-element/popup-light-dismiss.tentative.html [ Crash ]
 
+### external/wpt/layout-instability
+crbug.com/829028 external/wpt/layout-instability/multicol-001.html [ Failure ]
+
 ### virtual/stable/compositing/filters/
 crbug.com/591099 virtual/stable/compositing/filters/sw-nested-shadow-overlaps-hw-nested-shadow.html [ Failure ]
 
diff --git a/third_party/blink/web_tests/TestExpectations b/third_party/blink/web_tests/TestExpectations
index f8e74a8..637d5ae 100644
--- a/third_party/blink/web_tests/TestExpectations
+++ b/third_party/blink/web_tests/TestExpectations
@@ -5946,7 +5946,6 @@
 
 crbug.com/1105271 [ Mac ] scrollbars/custom-scrollbar-adjust-on-inactive-pseudo.html [ Failure Pass ]
 crbug.com/1105274 http/tests/devtools/tracing/timeline-js/timeline-js-line-level-profile-no-url-end-to-end.js [ Failure Pass Skip Timeout ]
-crbug.com/1105275 [ Mac ] fast/dom/Window/window-onFocus.html [ Failure Pass ]
 
 # This test fails due to the linked bug since input is now routed through the compositor.
 crbug.com/1112508 fast/forms/number/number-wheel-event.html [ Failure ]
@@ -7298,6 +7297,8 @@
 
 # Flaky on mac
 crbug.com/1263709 [ Mac ] http/tests/devtools/elements/node-xpath.js [ Failure ]
+crbug.com/1300772 [ Mac ] http/tests/devtools/oopif/oopif-elements-nesting-error-page.js [ Failure Pass ]
+crbug.com/1300772 [ Mac ] http/tests/devtools/coverage/coverage-view-unused.js [ Failure Pass ]
 
 # Flaky on Mac and Windows
 crbug.com/1272199 external/wpt/websockets/stream/tentative/backpressure-receive.any.serviceworker.html?wpt_flags=h2 [ Failure Pass ]
diff --git a/third_party/blink/web_tests/external/wpt/css/css-sizing/textarea-large-padding-crash.html b/third_party/blink/web_tests/external/wpt/css/css-sizing/textarea-large-padding-crash.html
new file mode 100644
index 0000000..b156c55
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-sizing/textarea-large-padding-crash.html
@@ -0,0 +1,4 @@
+<!DOCTYPE html>
+<link rel="author" title="Morten Stenshorne" href="mailto:mstensho@chromium.org">
+<link rel="help" href="https://bugs.chromium.org/p/chromium/issues/detail?id=1289408">
+<textarea style="padding:12345967890px;"></textarea>
diff --git a/third_party/blink/web_tests/external/wpt/layout-instability/multicol-000.html b/third_party/blink/web_tests/external/wpt/layout-instability/multicol-000.html
new file mode 100644
index 0000000..c06cddd6
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/layout-instability/multicol-000.html
@@ -0,0 +1,22 @@
+<!DOCTYPE html>
+<link rel="help" href="https://wicg.github.io/layout-instability/" />
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/util.js"></script>
+<div id="multicol" style="position:relative; columns:40; width:500px; column-gap:0;">
+  <div style="height:4000px; background:black;"></div>
+</div>
+<script>
+
+promise_test(async () => {
+  const watcher = new ScoreWatcher;
+  await waitForAnimationFrames(2);
+
+  multicol.style.top = '100px';
+  const expectedScore = computeExpectedScore(500 * (100 + 100), 100);
+
+  await watcher.promise;
+  assert_equals(watcher.score, expectedScore);
+}, 'Move balanced multicol container');
+
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/layout-instability/multicol-001.html b/third_party/blink/web_tests/external/wpt/layout-instability/multicol-001.html
new file mode 100644
index 0000000..a47d5f0
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/layout-instability/multicol-001.html
@@ -0,0 +1,26 @@
+<!DOCTYPE html>
+<link rel="help" href="https://wicg.github.io/layout-instability/" />
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/util.js"></script>
+<div id="multicol" style="position:relative; columns:5; column-gap:0; column-gap:0; width:500px; height:1px;">
+  <div style="contain:size; height:100px; background:hotpink;"></div>
+  <div style="contain:size; height:100px; background:hotpink;"></div>
+  <div style="contain:size; height:100px; background:hotpink;"></div>
+  <div style="contain:size; height:100px; background:hotpink;"></div>
+  <div style="contain:size; height:100px; background:hotpink;"></div>
+</div>
+<script>
+
+promise_test(async () => {
+  const watcher = new ScoreWatcher;
+  await waitForAnimationFrames(2);
+
+  multicol.style.top = '100px';
+  const expectedScore = computeExpectedScore(500 * (100 + 100), 100);
+
+  await watcher.promise;
+  assert_equals(watcher.score, expectedScore);
+}, 'Move multicol container with overflow');
+
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/mediacapture-insertable-streams/MediaStreamTrackGenerator-in-service-worker.https-expected.txt b/third_party/blink/web_tests/external/wpt/mediacapture-insertable-streams/MediaStreamTrackGenerator-in-service-worker.https-expected.txt
new file mode 100644
index 0000000..cfab5d1
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/mediacapture-insertable-streams/MediaStreamTrackGenerator-in-service-worker.https-expected.txt
@@ -0,0 +1,4 @@
+This is a testharness.js-based test.
+FAIL A service worker is able to initialize a MediaStreamTrackGenerator without crashing promise_test: Unhandled rejection with value: "Failed with error ReferenceError: MediaStreamTrackGenerator is not defined"
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/external/wpt/mediacapture-insertable-streams/MediaStreamTrackGenerator-in-shared-worker.https-expected.txt b/third_party/blink/web_tests/external/wpt/mediacapture-insertable-streams/MediaStreamTrackGenerator-in-shared-worker.https-expected.txt
new file mode 100644
index 0000000..1bc0072
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/mediacapture-insertable-streams/MediaStreamTrackGenerator-in-shared-worker.https-expected.txt
@@ -0,0 +1,4 @@
+This is a testharness.js-based test.
+FAIL A shared worker is able to initialize a MediaStreamTrackGenerator without crashing promise_test: Unhandled rejection with value: "Failed with error ReferenceError: MediaStreamTrackGenerator is not defined"
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/external/wpt/mediacapture-insertable-streams/MediaStreamTrackGenerator-in-worker.https-expected.txt b/third_party/blink/web_tests/external/wpt/mediacapture-insertable-streams/MediaStreamTrackGenerator-in-worker.https-expected.txt
new file mode 100644
index 0000000..43c364f
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/mediacapture-insertable-streams/MediaStreamTrackGenerator-in-worker.https-expected.txt
@@ -0,0 +1,6 @@
+This is a testharness.js-based test.
+FAIL A worker is able to initialize a MediaStreamTrackGenerator without crashing promise_test: Unhandled rejection with value: "Failed with error ReferenceError: MediaStreamTrackGenerator is not defined"
+FAIL A worker is able to enable a MediaStreamTrackGenerator without crashing promise_test: Unhandled rejection with value: "Failed with error ReferenceError: MediaStreamTrackGenerator is not defined"
+FAIL A worker is able to disable a MediaStreamTrackGenerator without crashing promise_test: Unhandled rejection with value: "Failed with error ReferenceError: MediaStreamTrackGenerator is not defined"
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/external/wpt/mediacapture-insertable-streams/MediaStreamTrackGenerator-pipes-data-in-worker.https-expected.txt b/third_party/blink/web_tests/external/wpt/mediacapture-insertable-streams/MediaStreamTrackGenerator-pipes-data-in-worker.https-expected.txt
new file mode 100644
index 0000000..df92162
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/mediacapture-insertable-streams/MediaStreamTrackGenerator-pipes-data-in-worker.https-expected.txt
@@ -0,0 +1,4 @@
+This is a testharness.js-based test.
+FAIL A worker is able to pipe data through a MediaStreamTrackGenerator without crashing promise_test: Unhandled rejection with value: "Failed with error ReferenceError: MediaStreamTrackGenerator is not defined"
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/external/wpt/mediacapture-streams/MediaStreamTrack-transfer.https-expected.txt b/third_party/blink/web_tests/external/wpt/mediacapture-streams/MediaStreamTrack-transfer.https-expected.txt
new file mode 100644
index 0000000..356c602
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/mediacapture-streams/MediaStreamTrack-transfer.https-expected.txt
@@ -0,0 +1,4 @@
+This is a testharness.js-based test.
+FAIL MediaStreamTrack transfer to Worker promise_test: Unhandled rejection with value: "Failed: ReferenceError: MediaStreamTrack is not defined"
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/external/wpt/mediacapture-streams/MediaStreamTrack-transfer.https.html b/third_party/blink/web_tests/external/wpt/mediacapture-streams/MediaStreamTrack-transfer.https.html
index 9d582f3..0024aea 100644
--- a/third_party/blink/web_tests/external/wpt/mediacapture-streams/MediaStreamTrack-transfer.https.html
+++ b/third_party/blink/web_tests/external/wpt/mediacapture-streams/MediaStreamTrack-transfer.https.html
@@ -7,12 +7,20 @@
 <script src=permission-helper.js></script>
 <script id="workerCode" type="javascript/worker">
 self.onmessage = (e) => {
-  if(e.data instanceof MediaStreamTrack) {
-    self.postMessage({result: 'Success'});
-  } else {
+  try {
+    if(e.data instanceof MediaStreamTrack) {
+      self.postMessage({result: 'Success'});
+      return;
+    } else {
+      self.postMessage({
+        result: 'Failure',
+        error: `${e.data} is not a MediaStreamTrack`
+      });
+    }
+  } catch (error) {
     self.postMessage({
       result: 'Failure',
-      error: `${e.data} is not a MediaStreamTrack`
+      error
     });
   }
 }
diff --git a/third_party/blink/web_tests/fast/dom/Window/window-onFocus.html b/third_party/blink/web_tests/fast/dom/Window/window-onFocus.html
index 533c6e0..347b0d1 100644
--- a/third_party/blink/web_tests/fast/dom/Window/window-onFocus.html
+++ b/third_party/blink/web_tests/fast/dom/Window/window-onFocus.html
@@ -1,40 +1,36 @@
-<script>
-var failed = false;
+<!DOCTYPE html>
 
-function runTest() {
-    window.onfocus = windowFocused;
-    window.onblur = windowBlurred;
-    
-    if (window.testRunner) {
-        testRunner.dumpAsText();
-        testRunner.setMainFrameIsFirstResponder(true);
-        
-        testRunner.setWindowFocus(false);
-        testRunner.setWindowFocus(true);
-        testRunner.setWindowFocus(false);
-    }
-}
-
-function log(message) {
-    var console = document.getElementById("console");
-    var li = document.createElement("li");
-    var text = document.createTextNode(message);
-    
-    console.appendChild(li);
-    li.appendChild(text);
-}
-
-function windowFocused() {
-    log("Window was focused");
-}
-
-function windowBlurred() {
-    log("Window was blurred");
-}
-</script>
 <body>
 This tests window.onFocus and window.onBlur handlers.  It uses the testRunners ability to mimic bringing the window to the front and pushing it to the back.
 <p><b>It is not intended to be run interactively:</b> when you run it interactively, you should see 'Window was blurred' and 'Window was focused' messages as you push the window to the back and bring it to the front, respectively.</p>
 <ul id="console"></ul>
-<script>runTest();</script>
-</body>
+
+<script>
+function log(message) {
+  const console = document.getElementById("console");
+  const li = document.createElement("li");
+  const text = document.createTextNode(message);
+  
+  console.appendChild(li);
+  li.appendChild(text);
+}
+
+// Make sure the window is focused before starting the test.
+if (window.testRunner)
+  testRunner.setWindowFocus(true);
+
+window.onload = () => {
+
+  window.onfocus = () => log('Window was focused');
+  window.onblur = () => log('Window was blurred');
+  
+  if (window.testRunner) {
+    testRunner.dumpAsText();
+    testRunner.setMainFrameIsFirstResponder(true);
+    
+    testRunner.setWindowFocus(false);
+    testRunner.setWindowFocus(true);
+    testRunner.setWindowFocus(false);
+  }
+}
+</script>
diff --git a/third_party/blink/web_tests/http/tests/serviceworker/webexposed/global-interface-listing-service-worker-expected.txt b/third_party/blink/web_tests/http/tests/serviceworker/webexposed/global-interface-listing-service-worker-expected.txt
index 585c4de..f303be3 100644
--- a/third_party/blink/web_tests/http/tests/serviceworker/webexposed/global-interface-listing-service-worker-expected.txt
+++ b/third_party/blink/web_tests/http/tests/serviceworker/webexposed/global-interface-listing-service-worker-expected.txt
@@ -793,37 +793,6 @@
     method constructor
     method decodingInfo
     method encodingInfo
-interface MediaStreamTrack : EventTarget
-    attribute @@toStringTag
-    getter contentHint
-    getter enabled
-    getter id
-    getter kind
-    getter label
-    getter muted
-    getter oncapturehandlechange
-    getter onended
-    getter onmute
-    getter onunmute
-    getter readyState
-    method applyConstraints
-    method clone
-    method constructor
-    method getCapabilities
-    method getCaptureHandle
-    method getConstraints
-    method getSettings
-    method stop
-    setter contentHint
-    setter enabled
-    setter oncapturehandlechange
-    setter onended
-    setter onmute
-    setter onunmute
-interface MediaStreamTrackGenerator : MediaStreamTrack
-    attribute @@toStringTag
-    getter writable
-    method constructor
 interface MessageChannel
     attribute @@toStringTag
     getter port1
diff --git a/third_party/blink/web_tests/media/video-poster-after-playing.html b/third_party/blink/web_tests/media/video-poster-after-playing.html
index 5eaf9c467..305bb9e 100644
--- a/third_party/blink/web_tests/media/video-poster-after-playing.html
+++ b/third_party/blink/web_tests/media/video-poster-after-playing.html
@@ -5,14 +5,22 @@
 if (window.testRunner)
   testRunner.waitUntilDone();
 
+function notifyDone() {
+  if (testRunner)
+    testRunner.notifyDone();
+}
+
 function startTest() {
   var video = document.querySelector('video');
+  video.addEventListener("ended", notifyDone);
   video.addEventListener("playing", function () {
+    // Set the poster image. This should not render on top of the video if there
+    // is a video frame to display.
     video.poster = "content/abe.png";
-  });
-  video.requestVideoFrameCallback(function () {
-    if (window.testRunner)
-      testRunner.notifyDone();
+
+    // Check against expectations for the next video frame (or the end of the
+    // video, if there is none).
+    video.requestVideoFrameCallback(notifyDone);
   });
 
   video.src = "resources/test-positive-start-time.webm";
diff --git a/third_party/blink/web_tests/webexposed/global-interface-listing-dedicated-worker-expected.txt b/third_party/blink/web_tests/webexposed/global-interface-listing-dedicated-worker-expected.txt
index 2777df54..9ff0ebef 100644
--- a/third_party/blink/web_tests/webexposed/global-interface-listing-dedicated-worker-expected.txt
+++ b/third_party/blink/web_tests/webexposed/global-interface-listing-dedicated-worker-expected.txt
@@ -843,37 +843,6 @@
 [Worker]     setter onsourceclose
 [Worker]     setter onsourceended
 [Worker]     setter onsourceopen
-[Worker] interface MediaStreamTrack : EventTarget
-[Worker]     attribute @@toStringTag
-[Worker]     getter contentHint
-[Worker]     getter enabled
-[Worker]     getter id
-[Worker]     getter kind
-[Worker]     getter label
-[Worker]     getter muted
-[Worker]     getter oncapturehandlechange
-[Worker]     getter onended
-[Worker]     getter onmute
-[Worker]     getter onunmute
-[Worker]     getter readyState
-[Worker]     method applyConstraints
-[Worker]     method clone
-[Worker]     method constructor
-[Worker]     method getCapabilities
-[Worker]     method getCaptureHandle
-[Worker]     method getConstraints
-[Worker]     method getSettings
-[Worker]     method stop
-[Worker]     setter contentHint
-[Worker]     setter enabled
-[Worker]     setter oncapturehandlechange
-[Worker]     setter onended
-[Worker]     setter onmute
-[Worker]     setter onunmute
-[Worker] interface MediaStreamTrackGenerator : MediaStreamTrack
-[Worker]     attribute @@toStringTag
-[Worker]     getter writable
-[Worker]     method constructor
 [Worker] interface MessageChannel
 [Worker]     attribute @@toStringTag
 [Worker]     getter port1
diff --git a/third_party/blink/web_tests/webexposed/global-interface-listing-shared-worker-expected.txt b/third_party/blink/web_tests/webexposed/global-interface-listing-shared-worker-expected.txt
index 47898d6a..69baadc 100644
--- a/third_party/blink/web_tests/webexposed/global-interface-listing-shared-worker-expected.txt
+++ b/third_party/blink/web_tests/webexposed/global-interface-listing-shared-worker-expected.txt
@@ -721,37 +721,6 @@
 [Worker]     method constructor
 [Worker]     method decodingInfo
 [Worker]     method encodingInfo
-[Worker] interface MediaStreamTrack : EventTarget
-[Worker]     attribute @@toStringTag
-[Worker]     getter contentHint
-[Worker]     getter enabled
-[Worker]     getter id
-[Worker]     getter kind
-[Worker]     getter label
-[Worker]     getter muted
-[Worker]     getter oncapturehandlechange
-[Worker]     getter onended
-[Worker]     getter onmute
-[Worker]     getter onunmute
-[Worker]     getter readyState
-[Worker]     method applyConstraints
-[Worker]     method clone
-[Worker]     method constructor
-[Worker]     method getCapabilities
-[Worker]     method getCaptureHandle
-[Worker]     method getConstraints
-[Worker]     method getSettings
-[Worker]     method stop
-[Worker]     setter contentHint
-[Worker]     setter enabled
-[Worker]     setter oncapturehandlechange
-[Worker]     setter onended
-[Worker]     setter onmute
-[Worker]     setter onunmute
-[Worker] interface MediaStreamTrackGenerator : MediaStreamTrack
-[Worker]     attribute @@toStringTag
-[Worker]     getter writable
-[Worker]     method constructor
 [Worker] interface MessageChannel
 [Worker]     attribute @@toStringTag
 [Worker]     getter port1
diff --git a/third_party/zlib/google/zip_unittest.cc b/third_party/zlib/google/zip_unittest.cc
index b46299e..98a5a3d 100644
--- a/third_party/zlib/google/zip_unittest.cc
+++ b/third_party/zlib/google/zip_unittest.cc
@@ -926,10 +926,10 @@
 // performing this test (android-asan, android-11-x86-rel,
 // android-marshmallow-x86-rel-non-cq).
 // Some Mac, Linux and Debug (dbg) bots tend to time out when performing this
-// test (crbug.com/1299736).
+// test (crbug.com/1299736, crbug.com/1300448).
 #if defined(THREAD_SANITIZER) || BUILDFLAG(IS_FUCHSIA) ||                \
     BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_LINUX) || \
-    !defined(NDEBUG)
+    BUILDFLAG(IS_CHROMEOS) || !defined(NDEBUG)
 TEST_F(ZipTest, DISABLED_BigFile) {
 #else
 TEST_F(ZipTest, BigFile) {
diff --git a/tools/metrics/histograms/README.md b/tools/metrics/histograms/README.md
index 64ab28143..785a6c17 100644
--- a/tools/metrics/histograms/README.md
+++ b/tools/metrics/histograms/README.md
@@ -186,6 +186,8 @@
 UmaHistogramEnumeration("NewTabPageAction", action);
 ```
 
+where `action` is an enumerator of the enumeration type `NewTabPageAction`.
+
 Logging histograms from Java should look similar:
 
 ```java
diff --git a/tools/perf/core/bot_platforms.py b/tools/perf/core/bot_platforms.py
index 011ac7a3..57bcd20f9 100644
--- a/tools/perf/core/bot_platforms.py
+++ b/tools/perf/core/bot_platforms.py
@@ -402,6 +402,7 @@
     _GetBenchmarkConfig('jetstream2'),
     _GetBenchmarkConfig('kraken'),
     _GetBenchmarkConfig('octane'),
+    _GetBenchmarkConfig('speedometer2'),
 ])
 _WIN_7_BENCHMARK_CONFIGS = PerfSuite([
     'loading.desktop',
@@ -569,7 +570,7 @@
                           _WIN_10_AMD_BENCHMARK_CONFIGS, 1, 'win')
 WIN_10_AMD_LAPTOP = PerfPlatform('win-10_amd_laptop-perf',
                                  'Windows 10 Laptop with AMD chipset.',
-                                 _WIN_10_AMD_LAPTOP_BENCHMARK_CONFIGS, 2, 'win')
+                                 _WIN_10_AMD_LAPTOP_BENCHMARK_CONFIGS, 5, 'win')
 
 # Android
 ANDROID_GO = PerfPlatform(
diff --git a/tools/perf/core/shard_maps/win-10_amd_laptop-perf_map.json b/tools/perf/core/shard_maps/win-10_amd_laptop-perf_map.json
index d00d0f6..29638626 100644
--- a/tools/perf/core/shard_maps/win-10_amd_laptop-perf_map.json
+++ b/tools/perf/core/shard_maps/win-10_amd_laptop-perf_map.json
@@ -3,29 +3,47 @@
         "benchmarks": {
             "jetstream": {
                 "abridged": false
-            },
-            "jetstream2": {
-                "abridged": false
             }
         }
     },
     "1": {
         "benchmarks": {
+            "jetstream2": {
+                "abridged": false
+            }
+        }
+    },
+    "2": {
+        "benchmarks": {
             "kraken": {
                 "abridged": false
-            },
+            }
+        }
+    },
+    "3": {
+        "benchmarks": {
             "octane": {
                 "abridged": false
             }
         }
     },
+    "4": {
+        "benchmarks": {
+            "speedometer2": {
+                "abridged": false
+            }
+        }
+    },
     "extra_infos": {
-        "num_stories": 4,
-        "predicted_min_shard_time": 20,
+        "num_stories": 5,
+        "predicted_min_shard_time": 10,
         "predicted_min_shard_index": 0,
-        "predicted_max_shard_time": 20,
+        "predicted_max_shard_time": 10,
         "predicted_max_shard_index": 0,
-        "shard #0": 20,
-        "shard #1": 20
+        "shard #0": 10,
+        "shard #1": 10,
+        "shard #2": 10,
+        "shard #3": 10,
+        "shard #4": 10
     }
 }
\ No newline at end of file
diff --git a/tools/traffic_annotation/summary/annotations.xml b/tools/traffic_annotation/summary/annotations.xml
index 7332243..134d134 100644
--- a/tools/traffic_annotation/summary/annotations.xml
+++ b/tools/traffic_annotation/summary/annotations.xml
@@ -163,7 +163,7 @@
  <item id="openscreen_message" added_in_milestone="83" content_hash_code="076a1faf" os_list="linux,windows,chromeos,android" file_path="components/openscreen_platform/udp_socket.cc" />
  <item id="openscreen_tls_message" added_in_milestone="83" content_hash_code="00f4022a" os_list="linux,windows,chromeos,android" file_path="components/openscreen_platform/tls_connection_factory.cc" />
  <item id="optimization_guide_model" added_in_milestone="79" content_hash_code="01ee6e67" os_list="linux,windows,chromeos,android" file_path="components/optimization_guide/core/prediction_model_fetcher_impl.cc" />
- <item id="optimization_guide_model_download" added_in_milestone="88" content_hash_code="05d71d9b" os_list="linux,windows,chromeos,android" file_path="chrome/browser/optimization_guide/prediction/prediction_model_download_manager.cc" />
+ <item id="optimization_guide_model_download" added_in_milestone="88" content_hash_code="05d71d9b" os_list="linux,windows,chromeos,android" file_path="components/optimization_guide/core/prediction_model_download_manager.cc" />
  <item id="origin_policy_loader" added_in_milestone="69" content_hash_code="07fd1eaf" os_list="linux,windows,chromeos,android" file_path="services/network/origin_policy/origin_policy_fetcher.cc" />
  <item id="parallel_download_job" added_in_milestone="62" content_hash_code="064736f3" os_list="linux,windows,chromeos,android" file_path="components/download/internal/common/parallel_download_job.cc" />
  <item id="password_protection_request" added_in_milestone="62" content_hash_code="01869413" os_list="linux,windows,chromeos,android" file_path="components/safe_browsing/core/browser/password_protection/password_protection_request.cc" />
diff --git a/ui/base/ime/win/tsf_text_store.cc b/ui/base/ime/win/tsf_text_store.cc
index 6173fba9..bf1e522d 100644
--- a/ui/base/ime/win/tsf_text_store.cc
+++ b/ui/base/ime/win/tsf_text_store.cc
@@ -324,6 +324,9 @@
     return TS_E_INVALIDPOS;
   }
 
+  TRACE_EVENT1("ime", "TSFTextStore::GetTextExt", "start, end",
+               std::to_string(acp_start) + ", " + std::to_string(acp_end));
+
   // According to a behavior of notepad.exe and wordpad.exe, top left corner of
   // rect indicates a first character's one, and bottom right corner of rect
   // indicates a last character's one.
@@ -395,11 +398,14 @@
       result_rect = gfx::Rect(text_input_client_->GetCaretBounds());
     }
   }
+  TRACE_EVENT1("ime", "TSFTextStore::GetTextExt", "DIP rect",
+               result_rect->ToString());
+
   *rect = display::win::ScreenWin::DIPToScreenRect(window_handle_,
                                                    result_rect.value())
               .ToRECT();
   *clipped = FALSE;
-  TRACE_EVENT1("ime", "TSFTextStore::GetTextExt", "selection_bounding_rect",
+  TRACE_EVENT1("ime", "TSFTextStore::GetTextExt", "screen rect",
                gfx::Rect(*rect).ToString());
   return S_OK;
 }
diff --git a/ui/chromeos/file_manager_strings.grdp b/ui/chromeos/file_manager_strings.grdp
index 01b9538..7216185e 100644
--- a/ui/chromeos/file_manager_strings.grdp
+++ b/ui/chromeos/file_manager_strings.grdp
@@ -449,6 +449,9 @@
   <message name="IDS_FILE_BROWSER_CUT_BUTTON_LABEL" desc="Context menu item that cuts the currently-selected file(s) to the clipboard.">
     Cut
   </message>
+  <message name="IDS_FILE_BROWSER_EXTRACT_ALL_BUTTON_LABEL" desc="Menu item label, showing dialog to extract content from a zip file.">
+    Extract all
+  </message>
   <message name="IDS_FILE_BROWSER_OPEN_WITH_BUTTON_LABEL" desc="Menu item label, showing dialog to choose extension to open selected files or directories.">
     Open with...
   </message>
diff --git a/ui/chromeos/file_manager_strings_grdp/IDS_FILE_BROWSER_EXTRACT_ALL_BUTTON_LABEL.png.sha1 b/ui/chromeos/file_manager_strings_grdp/IDS_FILE_BROWSER_EXTRACT_ALL_BUTTON_LABEL.png.sha1
new file mode 100644
index 0000000..85e5bbb
--- /dev/null
+++ b/ui/chromeos/file_manager_strings_grdp/IDS_FILE_BROWSER_EXTRACT_ALL_BUTTON_LABEL.png.sha1
@@ -0,0 +1 @@
+26134a5f6f7294c80bd96539733402dda886c859
\ No newline at end of file
diff --git a/ui/events/ozone/evdev/touch_event_converter_evdev.cc b/ui/events/ozone/evdev/touch_event_converter_evdev.cc
index 6db763c..a38ee70 100644
--- a/ui/events/ozone/evdev/touch_event_converter_evdev.cc
+++ b/ui/events/ozone/evdev/touch_event_converter_evdev.cc
@@ -45,7 +45,7 @@
 
 const int kMaxTrackingId = 0xffff;  // TRKID_MAX in kernel.
 
-// Convert tilt from [min, min + num_values) to [-90deg, +90deg)
+// Convert tilt from [min, min + num_values] to [-90deg, +90deg]
 float ScaleTilt(int value, int min_value, int num_values) {
   return 180.f * (value - min_value) / num_values - 90.f;
 }
@@ -195,8 +195,8 @@
     y_res = info.GetAbsInfoByCode(ABS_Y).resolution;
     tilt_x_min_ = info.GetAbsMinimum(ABS_TILT_X);
     tilt_y_min_ = info.GetAbsMinimum(ABS_TILT_Y);
-    tilt_x_range_ = info.GetAbsMaximum(ABS_TILT_X) - tilt_x_min_ + 1;
-    tilt_y_range_ = info.GetAbsMaximum(ABS_TILT_Y) - tilt_y_min_ + 1;
+    tilt_x_range_ = info.GetAbsMaximum(ABS_TILT_X) - tilt_x_min_;
+    tilt_y_range_ = info.GetAbsMaximum(ABS_TILT_Y) - tilt_y_min_;
 
     // No orientation without mt.
     orientation_min_ = orientation_max_ = 0;
diff --git a/ui/events/ozone/evdev/touch_event_converter_evdev_unittest.cc b/ui/events/ozone/evdev/touch_event_converter_evdev_unittest.cc
index de24e31..6303723 100644
--- a/ui/events/ozone/evdev/touch_event_converter_evdev_unittest.cc
+++ b/ui/events/ozone/evdev/touch_event_converter_evdev_unittest.cc
@@ -1708,13 +1708,15 @@
 TEST_F(TouchEventConverterEvdevTest, ActiveStylusTouchAndRelease) {
   ui::MockTouchEventConverterEvdev* dev = device();
   EventDeviceInfo devinfo;
-  EXPECT_TRUE(CapabilitiesToDeviceInfo(kWilsonBeachActiveStylus, &devinfo));
+  EXPECT_TRUE(CapabilitiesToDeviceInfo(kEveStylus, &devinfo));
   dev->Initialize(devinfo);
 
   struct input_event mock_kernel_queue[]{
       {{0, 0}, EV_KEY, BTN_TOOL_PEN, 1},
       {{0, 0}, EV_ABS, ABS_X, 9170},
       {{0, 0}, EV_ABS, ABS_Y, 3658},
+      {{0, 0}, EV_ABS, ABS_TILT_X, 0},
+      {{0, 0}, EV_ABS, ABS_TILT_Y, 0},
       {{0, 0}, EV_SYN, SYN_REPORT, 0},
       {{0, 0}, EV_KEY, BTN_TOUCH, 1},
       {{0, 0}, EV_ABS, ABS_PRESSURE, 60},
@@ -1722,6 +1724,8 @@
       {{0, 0}, EV_KEY, BTN_TOUCH, 0},
       {{0, 0}, EV_ABS, ABS_X, 9173},
       {{0, 0}, EV_ABS, ABS_Y, 3906},
+      {{0, 0}, EV_ABS, ABS_TILT_X, 30},
+      {{0, 0}, EV_ABS, ABS_TILT_Y, 50},
       {{0, 0}, EV_ABS, ABS_PRESSURE, 0},
       {{0, 0}, EV_SYN, SYN_REPORT, 0},
       {{0, 0}, EV_KEY, BTN_TOOL_PEN, 0},
@@ -1737,7 +1741,9 @@
   EXPECT_EQ(9170, down_event.location.x());
   EXPECT_EQ(3658, down_event.location.y());
   EXPECT_EQ(EventPointerType::kPen, down_event.pointer_details.pointer_type);
-  EXPECT_EQ(60.f / 1024, down_event.pointer_details.force);
+  EXPECT_EQ(60.f / 2047, down_event.pointer_details.force);
+  EXPECT_EQ(0, down_event.pointer_details.tilt_x);
+  EXPECT_EQ(0, down_event.pointer_details.tilt_y);
 
   auto up_event = dispatched_touch_event(1);
   EXPECT_EQ(ET_TOUCH_RELEASED, up_event.type);
@@ -1745,6 +1751,8 @@
   EXPECT_EQ(3906, up_event.location.y());
   EXPECT_EQ(EventPointerType::kPen, up_event.pointer_details.pointer_type);
   EXPECT_EQ(0.f, up_event.pointer_details.force);
+  EXPECT_EQ(30, up_event.pointer_details.tilt_x);
+  EXPECT_EQ(50, up_event.pointer_details.tilt_y);
 }
 
 TEST_F(TouchEventConverterEvdevTest, ActiveStylusMotion) {
diff --git a/ui/file_manager/file_manager/foreground/js/file_manager_commands.js b/ui/file_manager/file_manager/foreground/js/file_manager_commands.js
index 950f057..79c495c 100644
--- a/ui/file_manager/file_manager/foreground/js/file_manager_commands.js
+++ b/ui/file_manager/file_manager/foreground/js/file_manager_commands.js
@@ -2078,6 +2078,36 @@
 };
 
 /**
+ * Extracts content of ZIP files in the current selection.
+ */
+CommandHandler.COMMANDS_['extract-all'] = new class extends FilesCommand {
+  execute(event, fileManager) {
+    // TODO(crbug.com/953256) wire up IOTask for extraction.
+  }
+  /** @override */
+  canExecute(event, fileManager) {
+    if (!util.isExtractArchiveEnabled()) {
+      event.command.setHidden(true);
+      event.canExecute = false;
+      return;
+    }
+    const dirEntry = fileManager.getCurrentDirectoryEntry();
+    const selection = fileManager.getSelection();
+
+    // Enable this only for a single selected file which is an archive.
+    // TODO(crbug.com/953256) allow more selections and check for ZIP only.
+    if (selection.entries.length === 1 && selection.iconType === 'archive') {
+      event.command.setHidden(false);
+      event.canExecute = dirEntry && !fileManager.directoryModel.isReadOnly() &&
+          selection && selection.totalCount > 0;
+    } else {
+      event.command.setHidden(true);
+      event.canExecute = false;
+    }
+  }
+};
+
+/**
  * Creates ZIP file for current selection.
  */
 CommandHandler.COMMANDS_['zip-selection'] = new class extends FilesCommand {
diff --git a/ui/file_manager/file_manager/main.html b/ui/file_manager/file_manager/main.html
index ec9f7b8d..6bc16c51 100644
--- a/ui/file_manager/file_manager/main.html
+++ b/ui/file_manager/file_manager/main.html
@@ -127,6 +127,8 @@
                label="$i18n{GO_TO_FILE_LOCATION_BUTTON_LABEL}">
       <command id="zip-selection"
                label="$i18n{ZIP_SELECTION_BUTTON_LABEL}">
+      <command id="extract-all"
+               label="$i18n{EXTRACT_ALL_BUTTON_LABEL}">
       <command id="set-wallpaper"
                label="$i18n{SET_WALLPAPER_BUTTON_LABEL}">
 
@@ -190,6 +192,7 @@
         <cr-menu-item command="#delete" class="hide-on-toolbar">$i18n{DELETE_BUTTON_LABEL}</cr-menu-item>
         <cr-menu-item command="#restore-from-trash"></cr-menu-item>
         <cr-menu-item command="#zip-selection"></cr-menu-item>
+        <cr-menu-item command="#extract-all"></cr-menu-item>
         <cr-menu-item command="#toggle-holding-space" visibleif="full-page">
             </cr-menu-item>
         <cr-menu-item command="#share-with-linux"></cr-menu-item>
diff --git a/ui/file_manager/integration_tests/file_manager/office.js b/ui/file_manager/integration_tests/file_manager/office.js
index 4ef89ca..11de483 100644
--- a/ui/file_manager/integration_tests/file_manager/office.js
+++ b/ui/file_manager/integration_tests/file_manager/office.js
@@ -116,7 +116,36 @@
     actionId: actionId
   };
   const taskDescriptor = await getExecutedTask(appId);
-  chrome.test.assertEq(taskDescriptor, expectedDescriptor);
+  chrome.test.assertEq(expectedDescriptor, taskDescriptor);
+
+  // Remove fakes.
+  const removedCount = await remoteCall.callRemoteTestUtil(
+      'removeAllForegroundFakes', appId, []);
+  chrome.test.assertEq(1, removedCount);
+};
+
+testcase.openOfficeFromDriveOffline = async () => {
+  const appId = await setupAndWaitUntilReady(
+      RootPath.DRIVE, [], [ENTRIES.smallDocxPinned]);
+
+  // Fake chrome.fileManagerPrivate.executeTask to return
+  // chrome.fileManagerPrivate.TaskResult.OPENED.
+  const fakeData = {
+    'chrome.fileManagerPrivate.executeTask': ['static_fake', ['opened']],
+  };
+  await remoteCall.callRemoteTestUtil('foregroundFake', appId, [fakeData]);
+
+  // Open file.
+  chrome.test.assertTrue(
+      await remoteCall.callRemoteTestUtil('openFile', appId, ['pinned.docx']));
+
+  // When offline, the Web Drive Office task should not be available: another
+  // task should have been executed instead (QuickOffice or generic task).
+  const taskDescriptor = await getExecutedTask(appId);
+  const webDriveOfficeActionId =
+      (remoteCall.isSwaMode() ? FILE_SWA_BASE_URL + '?' : '') +
+      'open-web-drive-office';
+  chrome.test.assertFalse(taskDescriptor.actionId == webDriveOfficeActionId);
 
   // Remove fakes.
   const removedCount = await remoteCall.callRemoteTestUtil(
diff --git a/ui/file_manager/integration_tests/test_util.js b/ui/file_manager/integration_tests/test_util.js
index 8aa481d3..bbab5e0 100644
--- a/ui/file_manager/integration_tests/test_util.js
+++ b/ui/file_manager/integration_tests/test_util.js
@@ -818,7 +818,20 @@
     lastModifiedTime: 'Jan 4, 2019, 10:57 AM',
     nameText: 'text.docx',
     sizeText: '8.7 KB',
-    typeText: 'Office document'
+    typeText: 'Office document',
+  }),
+
+  smallDocxPinned: new TestEntryInfo({
+    type: EntryType.FILE,
+    sourceFileName: 'text.docx',
+    targetPath: 'pinned.docx',
+    mimeType: `application/vnd.openxmlformats-officedocument.wordprocessingml\
+.document`,
+    lastModifiedTime: 'Jan 4, 2019, 10:57 AM',
+    nameText: 'pinned.docx',
+    sizeText: '8.7 KB',
+    typeText: 'Office document',
+    pinned: true,
   }),
 
   pinned: new TestEntryInfo({
diff --git a/ui/lottie/animation.cc b/ui/lottie/animation.cc
index 2fff105b..254e084 100644
--- a/ui/lottie/animation.cc
+++ b/ui/lottie/animation.cc
@@ -73,7 +73,9 @@
 Animation::Animation(scoped_refptr<cc::SkottieWrapper> skottie,
                      cc::SkottieColorMap color_map,
                      cc::SkottieFrameDataProvider* frame_data_provider)
-    : skottie_(skottie), color_map_(std::move(color_map)) {
+    : skottie_(skottie),
+      color_map_(std::move(color_map)),
+      text_map_(skottie_->GetCurrentTextPropertyValues()) {
   DCHECK(skottie_);
   bool animation_has_image_assets =
       !skottie_->GetImageAssetMetadata().asset_storage().empty();
@@ -234,7 +236,7 @@
                                         base::Unretained(this), canvas,
                                         std::ref(all_frame_data)));
   canvas->DrawSkottie(skottie(), gfx::Rect(size), t, std::move(all_frame_data),
-                      color_map_, cc::SkottieTextPropertyValueMap());
+                      color_map_, text_map_);
 }
 
 cc::SkottieWrapper::FrameDataFetchResult Animation::LoadImageForAsset(
diff --git a/ui/lottie/animation.h b/ui/lottie/animation.h
index a6d6e05..d1b4f19 100644
--- a/ui/lottie/animation.h
+++ b/ui/lottie/animation.h
@@ -18,6 +18,7 @@
 #include "cc/paint/skottie_frame_data.h"
 #include "cc/paint/skottie_frame_data_provider.h"
 #include "cc/paint/skottie_resource_metadata.h"
+#include "cc/paint/skottie_text_property_value.h"
 #include "cc/paint/skottie_wrapper.h"
 #include "third_party/skia/include/core/SkRefCnt.h"
 #include "third_party/skia/include/core/SkStream.h"
@@ -158,6 +159,13 @@
   // Returns the skottie object that contins the animation data.
   scoped_refptr<cc::SkottieWrapper> skottie() const { return skottie_; }
 
+  // Returns the text nodes in the animation and their corresponding current
+  // property values. The text nodes' initial property values reflect those
+  // embedded in the Lottie animation file. A mutable reference is returned
+  // so that the caller may modify the text map with its own custom values
+  // before calling Paint(). The caller may do so as many times as desired.
+  cc::SkottieTextPropertyValueMap& text_map() { return text_map_; }
+
  private:
   friend class AnimationTest;
 
@@ -257,6 +265,7 @@
 
   scoped_refptr<cc::SkottieWrapper> skottie_;
   cc::SkottieColorMap color_map_;
+  cc::SkottieTextPropertyValueMap text_map_;
   base::flat_map<cc::SkottieResourceIdHash,
                  scoped_refptr<cc::SkottieFrameDataProvider::ImageAsset>>
       image_assets_;
diff --git a/ui/ozone/platform/scenic/overlay_manager_scenic.cc b/ui/ozone/platform/scenic/overlay_manager_scenic.cc
index 7ec96d5..17d3ca6d 100644
--- a/ui/ozone/platform/scenic/overlay_manager_scenic.cc
+++ b/ui/ozone/platform/scenic/overlay_manager_scenic.cc
@@ -13,7 +13,7 @@
 
 class OverlayCandidatesScenic : public OverlayCandidatesOzone {
  public:
-  OverlayCandidatesScenic(gfx::AcceleratedWidget widget) : widget_(widget) {}
+  OverlayCandidatesScenic() = default;
 
   void CheckOverlaySupport(OverlaySurfaceCandidateList* candidates) override {
     for (auto& candidate : *candidates) {
@@ -21,13 +21,9 @@
         continue;
       SysmemNativePixmap* sysmem_native_pixmap =
           reinterpret_cast<SysmemNativePixmap*>(candidate.native_pixmap.get());
-      candidate.overlay_handled =
-          sysmem_native_pixmap->SupportsOverlayPlane(widget_);
+      candidate.overlay_handled = sysmem_native_pixmap->SupportsOverlayPlane();
     }
   }
-
- private:
-  const gfx::AcceleratedWidget widget_;
 };
 
 }  // namespace
@@ -41,7 +37,7 @@
 
 std::unique_ptr<OverlayCandidatesOzone>
 OverlayManagerScenic::CreateOverlayCandidates(gfx::AcceleratedWidget widget) {
-  return std::make_unique<OverlayCandidatesScenic>(widget);
+  return std::make_unique<OverlayCandidatesScenic>();
 }
 
 }  // namespace ui
diff --git a/ui/ozone/platform/scenic/scenic_overlay_view.cc b/ui/ozone/platform/scenic/scenic_overlay_view.cc
index 7e45240..bcbfa09 100644
--- a/ui/ozone/platform/scenic/scenic_overlay_view.cc
+++ b/ui/ozone/platform/scenic/scenic_overlay_view.cc
@@ -5,7 +5,6 @@
 #include "ui/ozone/platform/scenic/scenic_overlay_view.h"
 
 #include <lib/ui/scenic/cpp/commands.h>
-#include <lib/ui/scenic/cpp/view_token_pair.h>
 
 #include "base/fuchsia/fuchsia_logging.h"
 #include "ui/ozone/platform/scenic/scenic_surface_factory.h"
@@ -19,24 +18,13 @@
 static const uint32_t kImagePipeBufferCollectionId = 1;
 static const std::string kSessionDebugName = "chromium scenic overlay";
 
-fuchsia::ui::views::ViewToken CreateViewToken(
-    fuchsia::ui::views::ViewHolderToken* holder_token) {
-  auto token_pair = scenic::ViewTokenPair::New();
-  *holder_token = std::move(token_pair.view_holder_token);
-  return std::move(token_pair.view_token);
-}
-
 }  // namespace
 
 ScenicOverlayView::ScenicOverlayView(
-    scenic::SessionPtrAndListenerRequest session_and_listener_request,
-    ScenicSurfaceFactory* scenic_surface_factory)
+    scenic::SessionPtrAndListenerRequest session_and_listener_request)
     : scenic_session_(std::move(session_and_listener_request)),
       safe_presenter_(&scenic_session_),
-      scenic_surface_factory_(scenic_surface_factory),
-      view_(&scenic_session_,
-            CreateViewToken(&view_holder_token_),
-            kSessionDebugName) {
+      image_material_(&scenic_session_) {
   scenic_session_.SetDebugName(kSessionDebugName);
   scenic_session_.set_error_handler(
       base::LogFidlErrorAndExitProcess(FROM_HERE, "ScenicSession"));
@@ -45,12 +33,6 @@
 ScenicOverlayView::~ScenicOverlayView() {
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
 
-  ScenicSurface* surface = scenic_surface_factory_->GetSurface(widget_);
-  if (surface) {
-    surface->AssertBelongsToCurrentThread();
-    surface->RemoveOverlayView(buffer_collection_id_);
-  }
-
   // Releasing |image_pipe_| implicitly also enforces cleanup.
   image_pipe_->RemoveBufferCollection(kImagePipeBufferCollectionId);
 }
@@ -66,15 +48,7 @@
   image_pipe_.set_error_handler(
       base::LogFidlErrorAndExitProcess(FROM_HERE, "ImagePipe"));
 
-  image_material_ = std::make_unique<scenic::Material>(&scenic_session_);
-  image_material_->SetTexture(image_pipe_id);
-
-  scenic::ShapeNode shape(&scenic_session_);
-  shape.SetShape(scenic::Rectangle(&scenic_session_, 1.f, 1.f));
-  shape.SetMaterial(*image_material_);
-
-  view_.AddChild(shape);
-  scenic_session_.ReleaseResource(image_pipe_id);
+  image_material_.SetTexture(image_pipe_id);
   safe_presenter_.QueuePresent();
 
   // Since there is one ImagePipe for each BufferCollection, it is ok to use a
@@ -118,35 +92,22 @@
   enable_blend_ = enable_blend;
   // Setting alpha as |255| marks the image as opaque and no content below would
   // be seen. Anything lower than 255 allows blending.
-  image_material_->SetColor(255, 255, 255, enable_blend ? 254 : 255);
+  image_material_.SetColor(255, 255, 255, enable_blend ? 254 : 255);
   safe_presenter_.QueuePresent();
 }
 
-bool ScenicOverlayView::CanAttachToAcceleratedWidget(
-    gfx::AcceleratedWidget widget) {
-  return view_holder_token_.value.is_valid() || (widget_ == widget);
-}
-
-bool ScenicOverlayView::AttachToScenicSurface(
-    gfx::AcceleratedWidget widget,
-    gfx::SysmemBufferCollectionId id) {
+void ScenicOverlayView::AttachToScenicSurface(
+    fuchsia::ui::views::ViewToken view_token) {
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
 
-  if (widget_ != gfx::kNullAcceleratedWidget && widget_ == widget)
-    return true;
+  view_.emplace(&scenic_session_, std::move(view_token), kSessionDebugName);
 
-  if (!view_holder_token_.value.is_valid()) {
-    DLOG(ERROR) << "ViewHolder is already attached.";
-    return false;
-  }
+  scenic::ShapeNode shape(&scenic_session_);
+  shape.SetShape(scenic::Rectangle(&scenic_session_, 1.f, 1.f));
+  shape.SetMaterial(image_material_);
 
-  buffer_collection_id_ = id;
-  widget_ = widget;
-
-  ScenicSurface* surface = scenic_surface_factory_->GetSurface(widget_);
-  DCHECK(surface);
-  return surface->PresentOverlayView(buffer_collection_id_,
-                                     std::move(view_holder_token_));
+  view_->AddChild(shape);
+  safe_presenter_.QueuePresent();
 }
 
 }  // namespace ui
diff --git a/ui/ozone/platform/scenic/scenic_overlay_view.h b/ui/ozone/platform/scenic/scenic_overlay_view.h
index 0eb667e..7512a9b 100644
--- a/ui/ozone/platform/scenic/scenic_overlay_view.h
+++ b/ui/ozone/platform/scenic/scenic_overlay_view.h
@@ -8,7 +8,6 @@
 #include <fuchsia/ui/scenic/cpp/fidl.h>
 #include <lib/ui/scenic/cpp/resources.h>
 #include <lib/ui/scenic/cpp/session.h>
-#include <memory>
 
 #include "base/threading/thread_checker.h"
 #include "ui/gfx/geometry/size.h"
@@ -24,9 +23,8 @@
 // instances to display overlays.
 class ScenicOverlayView {
  public:
-  ScenicOverlayView(
-      scenic::SessionPtrAndListenerRequest session_and_listener_request,
-      ScenicSurfaceFactory* scenic_surface_factory);
+  explicit ScenicOverlayView(
+      scenic::SessionPtrAndListenerRequest session_and_listener_request);
   ~ScenicOverlayView();
   ScenicOverlayView(const ScenicOverlayView&) = delete;
   ScenicOverlayView& operator=(const ScenicOverlayView&) = delete;
@@ -50,28 +48,18 @@
   // If |enable_blend| is true, sets |image_pipe_| as non-opaque.
   void SetBlendMode(bool enable_blend);
 
-  // Return true if |view_holder_token_| can be attached to a surface from
-  // |widget|.
-  bool CanAttachToAcceleratedWidget(gfx::AcceleratedWidget widget);
-
-  // Return true if |view_holder_token_| is attached to the scene graph of
-  // surface corresponding to |widget|.
-  bool AttachToScenicSurface(gfx::AcceleratedWidget widget,
-                             gfx::SysmemBufferCollectionId id);
+  // Attaches the view using the specified token.
+  void AttachToScenicSurface(fuchsia::ui::views::ViewToken view_token);
 
  private:
   scenic::Session scenic_session_;
   // Used for safely queueing Present() operations on |scenic_session_|.
   SafePresenter safe_presenter_;
-  ScenicSurfaceFactory* const scenic_surface_factory_;
-  fuchsia::ui::views::ViewHolderToken view_holder_token_;
-  scenic::View view_;
+  absl::optional<scenic::View> view_;
   fuchsia::images::ImagePipe2Ptr image_pipe_;
-  std::unique_ptr<scenic::Material> image_material_;
+  scenic::Material image_material_;
 
   bool enable_blend_ = false;
-  gfx::AcceleratedWidget widget_ = gfx::kNullAcceleratedWidget;
-  gfx::SysmemBufferCollectionId buffer_collection_id_;
 
   THREAD_CHECKER(thread_checker_);
 };
diff --git a/ui/ozone/platform/scenic/scenic_surface.cc b/ui/ozone/platform/scenic/scenic_surface.cc
index 023d064..3af68c2 100644
--- a/ui/ozone/platform/scenic/scenic_surface.cc
+++ b/ui/ozone/platform/scenic/scenic_surface.cc
@@ -118,9 +118,16 @@
   scenic_surface_factory_->RemoveSurface(window_);
 }
 
-ScenicSurface::OverlayViewInfo::OverlayViewInfo(scenic::ViewHolder holder,
-                                                scenic::EntityNode node)
-    : view_holder(std::move(holder)), entity_node(std::move(node)) {}
+ScenicSurface::OverlayViewInfo::OverlayViewInfo(
+    scenic::Session* scenic_session,
+    fuchsia::ui::views::ViewHolderToken view_holder_token)
+    : view_holder(scenic_session,
+                  std::move(view_holder_token),
+                  "OverlayViewHolder"),
+      entity_node(scenic_session) {
+  view_holder.SetHitTestBehavior(fuchsia::ui::gfx::HitTestBehavior::kSuppress);
+  entity_node.AddChild(view_holder);
+}
 
 void ScenicSurface::OnScenicEvents(
     std::vector<fuchsia::ui::scenic::Event> events) {
@@ -159,7 +166,7 @@
     BufferPresentedCallback presentation_callback) {
   if (!image_pipe_) {
     std::move(completion_callback)
-        .Run(gfx::SwapCompletionResult(gfx::SwapResult::SWAP_FAILED));
+        .Run(gfx::SwapCompletionResult(gfx::SwapResult::SWAP_SKIPPED));
     return;
   }
 
@@ -184,26 +191,36 @@
                                          DuplicateGpuFences(acquire_fences),
                                          /*release_fences=*/{});
 
-    auto& overlay_handle =
-        static_cast<SysmemNativePixmap*>(overlay.pixmap.get())->PeekHandle();
+    auto* pixmap = static_cast<SysmemNativePixmap*>(overlay.pixmap.get());
+    auto& overlay_handle = pixmap->PeekHandle();
     gfx::SysmemBufferCollectionId overlay_id =
         overlay_handle.buffer_collection_id.value();
     auto it = overlay_views_.find(overlay_id);
-    CHECK(it != overlay_views_.end());
+
+    // If this is a new overlay then attach it.
+    if (it == overlay_views_.end()) {
+      auto token_pair = scenic::ViewTokenPair::New();
+      pixmap->GetScenicOverlayView()->AttachToScenicSurface(
+          std::move(token_pair.view_token));
+      auto emplace_result = overlay_views_.emplace(
+          std::piecewise_construct, std::forward_as_tuple(overlay_id),
+          std::forward_as_tuple(&scenic_session_,
+                                std::move(token_pair.view_holder_token)));
+      it = emplace_result.first;
+      layout_update_required = true;
+    }
+
     auto& overlay_view_info = it->second;
     overlay_view_info.should_be_visible = true;
 
     auto& overlay_data = overlay.overlay_plane_data;
-    if (!overlay_view_info.visible ||
-        overlay_view_info.plane_z_order != overlay_data.z_order ||
-        overlay_view_info.display_bounds !=
-            gfx::ToNearestRect(overlay_data.display_bounds) ||
+    auto rounded_bounds = gfx::ToRoundedRect(overlay_data.display_bounds);
+    if (overlay_view_info.plane_z_order != overlay_data.z_order ||
+        overlay_view_info.display_bounds != rounded_bounds ||
         overlay_view_info.crop_rect != overlay_data.crop_rect ||
         overlay_view_info.plane_transform != overlay_data.plane_transform) {
-      overlay_view_info.visible = true;
       overlay_view_info.plane_z_order = overlay_data.z_order;
-      overlay_view_info.display_bounds =
-          gfx::ToNearestRect(overlay_data.display_bounds);
+      overlay_view_info.display_bounds = rounded_bounds;
       overlay_view_info.crop_rect = overlay_data.crop_rect;
       overlay_view_info.plane_transform = overlay_data.plane_transform;
       layout_update_required = true;
@@ -211,12 +228,17 @@
   }
 
   // Hide all overlays views that are not in `overlays_to_present`.
-  for (auto it = overlay_views_.begin(); it != overlay_views_.end(); ++it) {
+  auto it = overlay_views_.begin();
+  while (it != overlay_views_.end()) {
     auto& overlay_view = it->second;
-    if (overlay_view.visible && !overlay_view.should_be_visible) {
-      overlay_view.visible = false;
+    if (!overlay_view.should_be_visible) {
       layout_update_required = true;
+      parent_->DetachChild(overlay_view.entity_node);
+      it = overlay_views_.erase(it);
+      continue;
     }
+
+    it++;
   }
 
   if (layout_update_required) {
@@ -348,36 +370,6 @@
   main_shape_.SetMaterial(main_material_);
 }
 
-bool ScenicSurface::PresentOverlayView(
-    gfx::SysmemBufferCollectionId id,
-    fuchsia::ui::views::ViewHolderToken view_holder_token) {
-  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
-  scenic::ViewHolder view_holder(&scenic_session_, std::move(view_holder_token),
-                                 "OverlayViewHolder");
-  scenic::EntityNode entity_node(&scenic_session_);
-  view_holder.SetHitTestBehavior(fuchsia::ui::gfx::HitTestBehavior::kSuppress);
-
-  entity_node.AddChild(view_holder);
-
-  DCHECK(!overlay_views_.count(id));
-  overlay_views_.emplace(
-      std::piecewise_construct, std::forward_as_tuple(id),
-      std::forward_as_tuple(std::move(view_holder), std::move(entity_node)));
-
-  return true;
-}
-
-bool ScenicSurface::RemoveOverlayView(gfx::SysmemBufferCollectionId id) {
-  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
-
-  auto it = overlay_views_.find(id);
-  DCHECK(it != overlay_views_.end());
-  parent_->DetachChild(it->second.entity_node);
-  safe_presenter_.QueuePresent();
-  overlay_views_.erase(it);
-  return true;
-}
-
 mojo::PlatformHandle ScenicSurface::CreateView() {
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
 
@@ -449,6 +441,10 @@
 void ScenicSurface::UpdateViewHolderScene() {
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
 
+  // Layout will be updated once we receive view size.
+  if (main_shape_size_.IsEmpty())
+    return;
+
   // |plane_z_order| for main surface is 0.
   int min_z_order = 0;
   for (auto& item : overlay_views_) {
@@ -459,12 +455,6 @@
   for (auto& item : overlay_views_) {
     auto& overlay_view = item.second;
 
-    if (!overlay_view.visible) {
-      // `Detach()` is a no-op if the node is not attached.
-      overlay_view.entity_node.Detach();
-      continue;
-    }
-
     // No-op if the node is already attached.
     parent_->AddChild(overlay_view.entity_node);
 
diff --git a/ui/ozone/platform/scenic/scenic_surface.h b/ui/ozone/platform/scenic/scenic_surface.h
index 5587aec6..485bae7 100644
--- a/ui/ozone/platform/scenic/scenic_surface.h
+++ b/ui/ozone/platform/scenic/scenic_surface.h
@@ -76,15 +76,6 @@
   // Sets the texture of the surface to an image resource.
   void SetTextureToImage(const scenic::Image& image);
 
-  // Presents a ViewHolder that is corresponding to the overlay content coming
-  // from BufferCollection specified by |id|.
-  bool PresentOverlayView(
-      gfx::SysmemBufferCollectionId id,
-      fuchsia::ui::views::ViewHolderToken view_holder_token);
-
-  // Remove ViewHolder specified by |id|.
-  bool RemoveOverlayView(gfx::SysmemBufferCollectionId id);
-
   // Creates a View for this surface, and returns a ViewHolderToken handle
   // that can be used to attach it into a scene graph.
   mojo::PlatformHandle CreateView();
@@ -185,12 +176,12 @@
   const gfx::AcceleratedWidget window_;
 
   struct OverlayViewInfo {
-    OverlayViewInfo(scenic::ViewHolder holder, scenic::EntityNode node);
+    OverlayViewInfo(scenic::Session* scenic_session,
+                    fuchsia::ui::views::ViewHolderToken view_holder_token);
 
     scenic::ViewHolder view_holder;
     scenic::EntityNode entity_node;
 
-    bool visible = false;
     int plane_z_order = 0;
     gfx::Rect display_bounds;
     gfx::RectF crop_rect;
diff --git a/ui/ozone/platform/scenic/sysmem_buffer_collection.cc b/ui/ozone/platform/scenic/sysmem_buffer_collection.cc
index de216b4..fb585e20 100644
--- a/ui/ozone/platform/scenic/sysmem_buffer_collection.cc
+++ b/ui/ozone/platform/scenic/sysmem_buffer_collection.cc
@@ -282,7 +282,7 @@
   if (register_with_image_pipe) {
     overlay_view_task_runner_ = base::ThreadTaskRunnerHandle::Get();
     scenic_overlay_view_ = std::make_unique<ScenicOverlayView>(
-        scenic_surface_factory->CreateScenicSession(), scenic_surface_factory);
+        scenic_surface_factory->CreateScenicSession());
   }
 
   fuchsia::sysmem::BufferCollectionTokenSyncPtr collection_token;
diff --git a/ui/ozone/platform/scenic/sysmem_native_pixmap.cc b/ui/ozone/platform/scenic/sysmem_native_pixmap.cc
index 82575f5..0322f07 100644
--- a/ui/ozone/platform/scenic/sysmem_native_pixmap.cc
+++ b/ui/ozone/platform/scenic/sysmem_native_pixmap.cc
@@ -83,11 +83,6 @@
     std::vector<gfx::GpuFence> release_fences) {
   DCHECK(collection_->scenic_overlay_view());
   ScenicOverlayView* overlay_view = collection_->scenic_overlay_view();
-  const auto& buffer_collection_id = handle_.buffer_collection_id.value();
-  if (!overlay_view->AttachToScenicSurface(widget, buffer_collection_id)) {
-    DLOG(ERROR) << "Failed to attach to surface.";
-    return false;
-  }
 
   // Convert gfx::GpuFence to zx::event for PresentImage call.
   std::vector<zx::event> acquire_events;
@@ -112,13 +107,17 @@
   return handle_;
 }
 
-bool SysmemNativePixmap::SupportsOverlayPlane(
-    gfx::AcceleratedWidget widget) const {
-  if (!collection_->scenic_overlay_view())
-    return false;
+bool SysmemNativePixmap::SupportsOverlayPlane() const {
+  // We can display an overlay as long as we have a ScenicOverlayView. Note that
+  // ScenicOverlayView can migrate from one surface to another, but it can't
+  // be used across multiple surfaces similtaneously. But on Fuchsia each buffer
+  // collection is allocated (in FuchsiaVideoDecoder) for a specific web frame,
+  // and each frame can be displayed only on one specific surface.
+  return !!collection_->scenic_overlay_view();
+}
 
-  return collection_->scenic_overlay_view()->CanAttachToAcceleratedWidget(
-      widget);
+ScenicOverlayView* SysmemNativePixmap::GetScenicOverlayView() {
+  return collection_->scenic_overlay_view();
 }
 
 }  // namespace ui
diff --git a/ui/ozone/platform/scenic/sysmem_native_pixmap.h b/ui/ozone/platform/scenic/sysmem_native_pixmap.h
index a8e6d147..b54eca9 100644
--- a/ui/ozone/platform/scenic/sysmem_native_pixmap.h
+++ b/ui/ozone/platform/scenic/sysmem_native_pixmap.h
@@ -10,6 +10,8 @@
 
 namespace ui {
 
+class ScenicOverlayView;
+
 class SysmemNativePixmap : public gfx::NativePixmap {
  public:
   SysmemNativePixmap(scoped_refptr<SysmemBufferCollection> collection,
@@ -39,7 +41,10 @@
 
   // Returns true if overlay planes are supported and ScheduleOverlayPlane() can
   // be called.
-  bool SupportsOverlayPlane(gfx::AcceleratedWidget widget) const;
+  bool SupportsOverlayPlane() const;
+
+  // Returns true ScenicOverlayView for the pixmap if any.
+  ScenicOverlayView* GetScenicOverlayView();
 
  private:
   ~SysmemNativePixmap() override;
diff --git a/ui/webui/resources/cr_components/chromeos/network/BUILD.gn b/ui/webui/resources/cr_components/chromeos/network/BUILD.gn
index c731d058..61ff21c 100644
--- a/ui/webui/resources/cr_components/chromeos/network/BUILD.gn
+++ b/ui/webui/resources/cr_components/chromeos/network/BUILD.gn
@@ -384,7 +384,7 @@
     ":network_listener_behavior.m",
     ":network_password_input.m",
     ":network_shared_css.m",
-    "//chrome/browser/ui/webui/settings/ash/search:mojo_bindings_js_library_for_compile",
+    "//chrome/browser/ui/webui/settings/chromeos/search:mojo_bindings_js_library_for_compile",
     "//third_party/polymer/v3_0/components-chromium/iron-flex-layout:iron-flex-layout",
     "//third_party/polymer/v3_0/components-chromium/iron-icon:iron-icon",
     "//third_party/polymer/v3_0/components-chromium/paper-spinner:paper-spinner-lite",
diff --git a/ui/webui/resources/cr_components/chromeos/network/network_config.js b/ui/webui/resources/cr_components/chromeos/network/network_config.js
index 2acc313..c008d06 100644
--- a/ui/webui/resources/cr_components/chromeos/network/network_config.js
+++ b/ui/webui/resources/cr_components/chromeos/network/network_config.js
@@ -1213,7 +1213,7 @@
 
     const isOpenVpn = this.vpnType_ === VPNConfigType.OPEN_VPN;
     const isIpsec = this.vpnType_ === VPNConfigType.L2TP_IPSEC;
-    const caCerts = this.cachedServerCaCerts_.slice();
+    let caCerts = this.cachedServerCaCerts_.slice();
     if (!isOpenVpn && !isIpsec) {
       // 'Default' is the same as 'Do not check' except that 'Default' sets
       // eap.useSystemCas (which does not apply to OpenVPN and IPsec-based
@@ -1222,9 +1222,17 @@
           chromeos.networkConfig.mojom.CertificateType.kServerCA,
           this.i18n('networkCAUseDefault'), DEFAULT_HASH));
     }
-    caCerts.push(this.getDefaultCert_(
-        chromeos.networkConfig.mojom.CertificateType.kServerCA,
-        this.i18n('networkCADoNotCheck'), DO_NOT_CHECK_HASH));
+    if (!isIpsec) {
+      // For IPsec-based VPNs, it is mandatory to verify the server.
+      caCerts.push(this.getDefaultCert_(
+          chromeos.networkConfig.mojom.CertificateType.kServerCA,
+          this.i18n('networkCADoNotCheck'), DO_NOT_CHECK_HASH));
+    }
+    if (!caCerts.length) {
+      caCerts = [this.getDefaultCert_(
+          chromeos.networkConfig.mojom.CertificateType.kServerCA,
+          this.i18n('networkCertificateNoneInstalled'), NO_CERTS_HASH)];
+    }
     this.set('serverCaCerts_', caCerts);
 
     let userCerts = this.cachedUserCerts_.slice();
@@ -1662,6 +1670,15 @@
    * @return {boolean}
    * @private
    */
+  selectedServerCaHashIsValid_() {
+    return !!this.selectedServerCaHash_ &&
+        this.selectedServerCaHash_ !== NO_CERTS_HASH;
+  },
+
+  /**
+   * @return {boolean}
+   * @private
+   */
   selectedUserCertHashIsValid_() {
     return !!this.selectedUserCertHash_ &&
         this.selectedUserCertHash_ !== NO_CERTS_HASH;
@@ -1707,7 +1724,10 @@
       case IpsecAuthType.PSK:
         return !!vpn.l2tp.username && !!vpn.ipSec.psk;
       case IpsecAuthType.CERT:
-        return !!vpn.l2tp.username && this.selectedUserCertHashIsValid_();
+        // TODO(b/206722135): Show proper error message in the UI if server CA
+        // is invalid.
+        return !!vpn.l2tp.username && this.selectedServerCaHashIsValid_() &&
+            this.selectedUserCertHashIsValid_();
       default:
         assertNotReached();
     }